2018-04-01上架版本:
核心板原理图: Lichee_nano.pdf
底板原理图: lpi-nano-dock.pdf
网盘资料(有可能会失效): https://pan.baidu.com/s/1smzuGS9
购买地址: 9.9元起 荔枝派Nano 跨界核心板 开发板 多系统 linux
--------------------------------------------------
F1C100s资料集合:
参考链接0: 买个全志arm9 F1C100s 方案的唱戏机回来拆一拆
参考链接1: F1C100s 手册
参考链接2: 看戏机(唱戏机)F1C100原理图
参考链接3: 侨德实业有限公司VS (全志C100电脑升级)流程示意图
参考链接4: 先科唱戏机(看戏机)F1C100s方案运行XBOOT大神的XBOOT系统步骤
参考链接5: F1C100s入坑记录
参考链接6: 编译、安装Windows版本sunxi-fel步骤
参考链接7: 感谢荔枝派群主泽畔友情赠送的nano f1c100s开发板、液晶屏、电容触摸屏.
参考链接8: 荔枝派nano f1c100s开发板连接JLink调试器分享
代码下载: n329xx_project_run_in_sram.7z
非常简单, 就是把协处理器的寄存器读到 R0, 没别的功能.
MDK工程,读芯片Id, 代码不到10行,没有中断向量.
注: 这个代码只能在 N3290x 上面跑, 因为N3290x 的SRAM地址在 0xFF00 0000,
全志f1c100s的sram地址在 0x0 位置。
2019-04-13 更新
---------------------------------------------
支持ARM7/9旧器件的 MDK 版本:
http://www2.keil.com/mdk5/legacy/
https://armkeil.blob.core.windows.net/legacy/MDK79525.EXE
https://armkeil.blob.core.windows.net/legacy/MDK79524.EXE
离线
#include <stdio.h>
#include "types.h"
#include "io.h"
#include "reg-ccu.h"
typedef unsigned int u32_t;
#define __O volatile
#define __IO volatile
#define CPU_PORT_BASE 0x01C20800
#define GPIOG_BASE (CPU_PORT_BASE + 0xD8)
#define GPIOG_INTERRUPT_BASE (GPIO_CPU_INTERRUPT_BASE + 0x20)
typedef struct{
__IO u32_t CFG[4];
__IO u32_t DAT;
__IO u32_t DRV[2];
__IO u32_t PUL[2];
}GPIO_Typedef;
#define GPIOG ((GPIO_Typedef *)GPIOG_BASE)
static void wait_pll_stable(u32_t base)
{
u32_t rval = 0;
u32_t time = 0xfff;
do {
rval = read32(base);
time--;
} while(time && !(rval & (1 << 28)));
}
static void clock_set_pll_cpu(u32_t clk)
{
u32_t n, k, m, p;
u32_t rval = 0;
u32_t div = 0;
if(clk > 720000000)
clk = 720000000;
if((clk % 24000000) == 0)
{
div = clk / 24000000;
n = div - 1;
k = 0;
m = 0;
p = 0;
}
else if((clk % 12000000) == 0)
{
m = 1;
div = clk / 12000000;
if((div % 3) == 0)
k = 2;
else if((div % 4) == 0)
k = 3;
else
k = 1;
n = (div / (k + 1)) - 1;
p = 0;
}
else
{
div = clk / 24000000;
n = div - 1;
k = 0;
m = 0;
p = 0;
}
rval = read32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL);
rval &= ~((0x3 << 16) | (0x1f << 8) | (0x3 << 4) | (0x3 << 0));
rval |= (1U << 31) | (p << 16) | (n << 8) | (k << 4) | m;
write32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL, rval);
wait_pll_stable(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL);
}
void sys_clock_init(void)
{
write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME0, 0x1ff);
write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME1, 0x1ff);
write32(F1C100S_CCU_BASE + CCU_CPU_CFG, 0x00020000);
write32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG, 0x00012110);
clock_set_pll_cpu(408000000);
}
void sys_uart_init(void)
{
u32_t addr;
u32_t val;
/* Config GPIOF4 and GPIOF2 to txd0 and rxd0 */
addr = 0x01c208b4 + 0x00;
val = read32(addr);
val &= ~(0xf << ((4 & 0x7) << 2));
val |= ((0x3 & 0x7) << ((4 & 0x7) << 2));
write32(addr, val);
val = read32(addr);
val &= ~(0xf << ((2 & 0x7) << 2));
val |= ((0x3 & 0x7) << ((2 & 0x7) << 2));
write32(addr, val);
/* Open the clock gate for uart0 */
addr = 0x01c20068;
val = read32(addr);
val |= 1 << 20;
write32(addr, val);
/* Deassert uart0 reset */
addr = 0x01c202d0;
val = read32(addr);
val |= 1 << 20;
write32(addr, val);
/* Config uart0 to 115200-8-1-0 */
addr = 0x01c25000;
write32(addr + 0x04, 0x0);
write32(addr + 0x08, 0xf7);
write32(addr + 0x10, 0x0);
val = read32(addr + 0x0c);
val |= (1 << 7);
write32(addr + 0x0c, val);
write32(addr + 0x00, 0x37 & 0xff);
write32(addr + 0x04, (0x37 >> 8) & 0xff);
val = read32(addr + 0x0c);
val &= ~(1 << 7);
write32(addr + 0x0c, val);
val = read32(addr + 0x0c);
val &= ~0x1f;
val |= (0x3 << 0) | (0 << 2) | (0x0 << 3);
write32(addr + 0x0c, val);
}
void sys_uart_putc(char c)
{
u32_t addr = 0x01c25000;
while((read32(addr + 0x7c) & (0x1 << 1)) == 0);
write32(addr + 0x00, c);
}
int main(void)
{
sys_clock_init();
sys_uart_init();
while(1)
{
sys_uart_putc('A');
}
u32_t cnt = 0;
GPIOG->CFG[0] = (1 << 0) << 8;
for(;;)
{
if(cnt==0)
GPIOG->DAT &= ~(1U << 2);
if(cnt==100000)
GPIOG->DAT = 1U << 2;
if(++cnt>200000)cnt = 0;
}
int i = 0;
while(1)
{
i++;
}
return 0;
}
licheepi nano 核心板原理图: Lichee_nano.pdf
仍然是在 SRAM 里面跑,驱动串口 UART0(PF2, PF4)
源码下载: f1c100s_project_run_in_sram_3.7z
从原来的IAR项目移过来的: https://whycan.cn/t_1003.html
参考: https://github.com/xboot/xboot/tree/master/src/arch/arm32/mach-f1c100s
离线
#include <stdio.h>
#include <string.h>
#include "types.h"
#include "io.h"
#include "reg-ccu.h"
typedef unsigned int u32_t;
#define __O volatile
#define __IO volatile
#define CPU_PORT_BASE 0x01C20800
#define GPIOG_BASE (CPU_PORT_BASE + 0xD8)
#define GPIOG_INTERRUPT_BASE (GPIO_CPU_INTERRUPT_BASE + 0x20)
#define vaStart(list, param) list = (char*)((s32_t)¶m + sizeof(param))
#define vaArg(list, type) ((type *)(list += sizeof(type)))[-1]
typedef struct{
__IO u32_t CFG[4];
__IO u32_t DAT;
__IO u32_t DRV[2];
__IO u32_t PUL[2];
}GPIO_Typedef;
#define GPIOG ((GPIO_Typedef *)GPIOG_BASE)
static void wait_pll_stable(u32_t base)
{
u32_t rval = 0;
u32_t time = 0xfff;
do {
rval = read32(base);
time--;
} while(time && !(rval & (1 << 28)));
}
static void clock_set_pll_cpu(u32_t clk)
{
u32_t n, k, m, p;
u32_t rval = 0;
u32_t div = 0;
if(clk > 720000000)
clk = 720000000;
if((clk % 24000000) == 0)
{
div = clk / 24000000;
n = div - 1;
k = 0;
m = 0;
p = 0;
}
else if((clk % 12000000) == 0)
{
m = 1;
div = clk / 12000000;
if((div % 3) == 0)
k = 2;
else if((div % 4) == 0)
k = 3;
else
k = 1;
n = (div / (k + 1)) - 1;
p = 0;
}
else
{
div = clk / 24000000;
n = div - 1;
k = 0;
m = 0;
p = 0;
}
rval = read32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL);
rval &= ~((0x3 << 16) | (0x1f << 8) | (0x3 << 4) | (0x3 << 0));
rval |= (1U << 31) | (p << 16) | (n << 8) | (k << 4) | m;
write32(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL, rval);
wait_pll_stable(F1C100S_CCU_BASE + CCU_PLL_CPU_CTRL);
}
void sys_clock_init(void)
{
write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME0, 0x1ff);
write32(F1C100S_CCU_BASE + CCU_PLL_STABLE_TIME1, 0x1ff);
write32(F1C100S_CCU_BASE + CCU_CPU_CFG, 0x00020000);
write32(F1C100S_CCU_BASE + CCU_AHB_APB_CFG, 0x00012110);
clock_set_pll_cpu(408000000);
}
void sys_uart_init(void)
{
u32_t addr;
u32_t val;
/* Config GPIOF4 and GPIOF2 to txd0 and rxd0 */
addr = 0x01c208b4 + 0x00;
val = read32(addr);
val &= ~(0xf << ((4 & 0x7) << 2));
val |= ((0x3 & 0x7) << ((4 & 0x7) << 2));
write32(addr, val);
val = read32(addr);
val &= ~(0xf << ((2 & 0x7) << 2));
val |= ((0x3 & 0x7) << ((2 & 0x7) << 2));
write32(addr, val);
/* Open the clock gate for uart0 */
addr = 0x01c20068;
val = read32(addr);
val |= 1 << 20;
write32(addr, val);
/* Deassert uart0 reset */
addr = 0x01c202d0;
val = read32(addr);
val |= 1 << 20;
write32(addr, val);
/* Config uart0 to 115200-8-1-0 */
addr = 0x01c25000;
write32(addr + 0x04, 0x0);
write32(addr + 0x08, 0xf7);
write32(addr + 0x10, 0x0);
val = read32(addr + 0x0c);
val |= (1 << 7);
write32(addr + 0x0c, val);
write32(addr + 0x00, 0x37 & 0xff);
write32(addr + 0x04, (0x37 >> 8) & 0xff);
val = read32(addr + 0x0c);
val &= ~(1 << 7);
write32(addr + 0x0c, val);
val = read32(addr + 0x0c);
val &= ~0x1f;
val |= (0x3 << 0) | (0 << 2) | (0x0 << 3);
write32(addr + 0x0c, val);
}
void sys_uart_putc(char c)
{
u32_t addr = 0x01c25000;
while((read32(addr + 0x7c) & (0x1 << 1)) == 0);
write32(addr + 0x00, c);
}
void sysPutString(char *string)
{
while (*string != '\0')
{
sys_uart_putc(*string);
string++;
}
}
void sysPutStringReverse(char *s, u32_t index)
{
while ((index--) > 0)
sys_uart_putc(s[index]);
}
static void sysPutRepChar(char c, u32_t count)
{
while (count--)
sys_uart_putc(c);
}
static void sysPutNumber(s32_t value, s32_t radix, s32_t width, s8_t fill)
{
s8_t buffer[40];
s32_t bi = 0;
u32_t uvalue;
u16_t digit;
u16_t left = 0;
u16_t negative = 0;
if (fill == 0)
fill = ' ';
if (width < 0)
{
width = -width;
left = 0;
}
if (width < 0 || width > 80)
width = 0;
if (radix < 0)
{
radix = -radix;
if (value < 0)
{
negative = 0;
value = -value;
}
}
uvalue = value;
do
{
if (radix != 16)
{
digit = uvalue % radix;
uvalue = uvalue / radix;
}
else
{
digit = uvalue & 0xf;
uvalue = uvalue >> 4;
}
buffer[bi] = digit + ((digit <= 9) ? '0' : ('A' - 10));
bi++;
if (uvalue != 0)
{
if ((radix == 10)
&& ((bi == 3) || (bi == 7) || (bi == 11) | (bi == 15)))
{
buffer[bi++] = ',';
}
}
}
while (uvalue != 0);
if (negative)
{
buffer[bi] = '-';
bi += 1;
}
if (width <= bi)
sysPutStringReverse(buffer, bi);
else
{
width -= bi;
if (!left)
sysPutRepChar(fill, width);
sysPutStringReverse(buffer, bi);
if (left)
sysPutRepChar(fill, width);
}
}
static u8_t *FormatItem(u8_t *f, s32_t a)
{
u8_t c;
s32_t fieldwidth = 0;
s32_t leftjust = 0;
s32_t radix = 0;
u8_t fill = ' ';
if (*f == '0')
fill = '0';
while ((c = *f++) != 0)
{
if (c >= '0' && c <= '9')
{
fieldwidth = (fieldwidth * 10) + (c - '0');
}
else
switch (c)
{
case '\000':
return (--f);
case '%':
sys_uart_putc('%');
return (f);
case '-':
leftjust = 1;
break;
case 'c':
{
if (leftjust)
sys_uart_putc(a & 0x7f);
if (fieldwidth > 0)
sysPutRepChar(fill, fieldwidth - 1);
if (!leftjust)
sys_uart_putc(a & 0x7f);
return (f);
}
case 's':
{
if (leftjust)
sysPutString((char*)a);
if (fieldwidth > strlen((s8_t*)a))
sysPutRepChar(fill, fieldwidth - strlen((s8_t*)a));
if (!leftjust)
sysPutString((s8_t*)a);
return (f);
}
case 'd':
case 'i':
radix = -10;
break;
case 'u':
radix = 10;
break;
case 'x':
radix = 16;
break;
case 'X':
radix = 16;
break;
case 'o':
radix = 8;
break;
default:
radix = 3;
break; /* unknown switch! */
}
if (radix)
break;
}
if (leftjust)
fieldwidth = -fieldwidth;
sysPutNumber(a, radix, fieldwidth, fill);
return (f);
}
void sysprintf(s8_t* pcStr,...)
{
u8_t *argP;
vaStart(argP, pcStr); /* point at the end of the format string */
while (*pcStr)
{ /* this works because args are all ints */
if (*pcStr == '%')
pcStr = FormatItem(pcStr + 1, vaArg(argP, u32_t));
else
sys_uart_putc(*pcStr++);
}
}
int main(void)
{
sys_clock_init();
sys_uart_init();
while(1)
{
sysprintf("hello %s: %d 0x%x \n", "world", 10, 20);
//sys_uart_putc('A');
}
u32_t cnt = 0;
GPIOG->CFG[0] = (1 << 0) << 8;
for(;;)
{
if(cnt==0)
GPIOG->DAT &= ~(1U << 2);
if(cnt==100000)
GPIOG->DAT = 1U << 2;
if(++cnt>200000)cnt = 0;
}
int i = 0;
while(1)
{
i++;
}
return 0;
}
实现了格式化输出的demo:
f1c100s_project_run_in_sram_4.7z
离线
sys_clock_init();
sys_uart_init();
write32(TMR0_INTV_VALUE_REG, 0x2EE0); //Set interval value
write32(TMR0_CTRL_REG, 0x14); //Select Single mode,24MHz clock source,2 pre-scale
write32(TMR0_CTRL_REG, read32(TMR0_CTRL_REG)|(1<<1)); //Set Reload bit
while((read32(TMR0_CTRL_REG)>>1)&1); //Waiting Reload bit turns to 0
write32(TMR0_CTRL_REG, read32(TMR0_CTRL_REG)|(1<<0)); //Enable Timer0
while(1)
{
//sysprintf("hello %s: %d 0x%x \n", "world", 10, 20);
//sys_uart_putc('A');
u32_t a = read32(TMR0_CUR_VALUE_REG);
sysprintf("%d\n", a);
}
实现timer0 定时器累加简单demo: f1c100s_project_run_in_sram_5.7z
离线
__irq void sysIrqHandler()
{
static int i = 0;
i++;
}
#define INTC_EN_REG0 (0x01C20400 + 0x20)
#define INTC_MASK_REG0 (0x01C20400 + 0x30)
int main(void)
{
//void* _mOldIrqVect = *(void volatile *)0x38;
*(void* volatile *)0x38 = (void* volatile)sysIrqHandler;
sys_clock_init();
sys_uart_init();
write32(TMR_IRQ_EN_REG, 1);
write32(INTC_EN_REG0, (read32(INTC_EN_REG0)) | 1 << 13);
write32(TMR_IRQ_EN_REG, (read32(TMR_IRQ_EN_REG)) | 1 << 13);
write32(TMR0_INTV_VALUE_REG, 0x2EE0); //Set interval value
write32(TMR0_CTRL_REG, 0x10); //Select Single mode,24MHz clock source,2 pre-scale
write32(TMR0_CTRL_REG, read32(TMR0_CTRL_REG)|(1<<1)); //Set Reload bit
while((read32(TMR0_CTRL_REG)>>1)&1); //Waiting Reload bit turns to 0
write32(TMR0_CTRL_REG, read32(TMR0_CTRL_REG)|(1<<0)); //Enable Timer0
u32_t temp;
__asm
{
MRS temp, CPSR
AND temp, temp, 0x7F
MSR CPSR_c, temp
}
u32_t old = 0;
while(1)
{
//sysprintf("hello %s: %d 0x%x \n", "world", 10, 20);
//sys_uart_putc('A');
volatile u32_t a = read32(TMR0_CUR_VALUE_REG);
if(old == a)
{
}
else
{
old = a;
if(a % 256 == 0)
{
sysprintf("%d\n", a);
}
}
}
u32_t cnt = 0;
GPIOG->CFG[0] = (1 << 0) << 8;
for(;;)
{
if(cnt==0)
GPIOG->DAT &= ~(1U << 2);
if(cnt==100000)
GPIOG->DAT = 1U << 2;
if(++cnt>200000)cnt = 0;
}
int i = 0;
while(1)
{
i++;
}
return 0;
}
实现了定时器中断的简单demo: f1c100s_project_run_in_sram_6.7z
仍然感谢新唐提供的裸奔程序。
离线
__asm __irq void sysSwiHandler()
{
STMFD r13!,{r0 - r5,r14} //Push r0 - r5 and LR保存寄存器
MOV R0, #0xAA;
MOV R1, #0xBB;
MOV R2, #0xCC;
MOV R3, #0xDD;
LDR R4, [LR, #-4]
BIC R4, R4, #0xFF000000 //将SWI指令高8位清除掉,只保留低24位立即数,取得SWI指令编码
LDMFD r13!,{r0 - r5,r14} //Pop r0 - r5 and LR恢复寄存器
MOV PC, LR
}
int main(void)
{
*(void* volatile *)0x28 = (void* volatile)sysSwiHandler;
}
实现一个简单的swi中断处理,并取出用户自定义中断号到R4寄存器: f1c100s_project_run_in_sram_7.7z
离线
#define TMR_IRQ_EN_REG (0x01C20C00 + 0x00)
#define TMR_IRQ_STA_REG (0x01C20C00 + 0x04)
#define TMR0_CTRL_REG (0x01C20C00 + 0x10)
#define TMR0_INTV_VALUE_REG (0x01C20C00 + 0x14)
#define TMR0_CUR_VALUE_REG (0x01C20C00 + 0x18)
#define INTC_PEND_REG0 (0x01C20400 + 0x10)
#define INTC_PEND_REG1 (0x01C20400 + 0x14)
#define INTC_EN_REG0 (0x01C20400 + 0x20)
#define INTC_EN_REG1 (0x01C20400 + 0x24)
#define INTC_MASK_REG0 (0x01C20400 + 0x30)
#define INTC_MASK_REG1 (0x01C20400 + 0x34)
#define INTC_RESP_REG0 (0x01C20400 + 0x40)
#define INTC_RESP_REG1 (0x01C20400 + 0x44)
#define INTC_FF_REG0 (0x01C20400 + 0x50)
#define INTC_FF_REG1 (0x01C20400 + 0x54)
#define INTC_PRIO_REG0 (0x01C20400 + 0x60)
#define INTC_PRIO_REG1 (0x01C20400 + 0x60)
static volatile int test_count = 0;
__irq void sysIrqHandler()
{
test_count++;
write32(TMR_IRQ_STA_REG, (1)); //清除TIMER0中断标识
}
int main(void)
{
*(void* volatile *)0x38 = (void* volatile)sysIrqHandler;//设置IRQ中断处理程序
sys_clock_init();
sys_uart_init();
write32(TMR_IRQ_EN_REG, 1);
write32(INTC_EN_REG0, (read32(INTC_EN_REG0)) | 1 << 13);
write32(TMR_IRQ_EN_REG, (read32(TMR_IRQ_EN_REG)) | 1 << 13);
write32(TMR0_INTV_VALUE_REG, 0x2EE0); //Set interval value
write32(TMR0_CTRL_REG, 0x10); //此行注释有误,具体看手册 Select Single mode,24MHz clock source,2 pre-scale
write32(TMR0_CTRL_REG, read32(TMR0_CTRL_REG)|(1<<1)); //Set Reload bit
while((read32(TMR0_CTRL_REG)>>1)&1); //Waiting Reload bit turns to 0
#if 1
write32(TMR0_CTRL_REG, read32(TMR0_CTRL_REG)|(1<<0)); //Enable Timer0
//开启中断使能
u32_t temp;
__asm
{
MRS temp, CPSR
AND temp, temp, 0x7F
MSR CPSR_c, temp
}
#endif
u32_t old = 0;
while(1)
{
if(old == test_count)
{
}
else
{
old = test_count;
if(test_count % 256 == 0)
{
sysprintf("%d\n", test_count);
}
}
}
}
跳出七七四十九道坑写的TIMER0 中断处理程序:
离线
//xarray数组定义到ddr
unsigned char xarray[128] __attribute__((at(0x80000000)));
extern void sys_dram_init(void);
int main(void)
{
sys_dram_init();
memset(xarray, 0xAA, sizeof(xarray));
xarray[1] = xarray[0] + 1;
xarray[2] = xarray[1] + 2;
}
代码参考: https://github.com/xboot/xboot/blob/master/src/arch/arm32/mach-f1c100s/sys-dram.c
/*
* sys-dram.c
*
* Copyright(c) 2007-2018 Jianjun Jiang <8192542@qq.com>
* Official site: http://xboot.org
* Mobile phone: +86-18665388956
* QQ: 8192542
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include <xboot.h>
#include <f1c100s/reg-ccu.h>
#include <f1c100s/reg-dram.h>
#define PLL_DDR_CLK (156000000)
#define SDR_T_CAS (0x2)
#define SDR_T_RAS (0x8)
#define SDR_T_RCD (0x3)
#define SDR_T_RP (0x3)
#define SDR_T_WR (0x3)
#define SDR_T_RFC (0xd)
#define SDR_T_XSR (0xf9)
#define SDR_T_RC (0xb)
#define SDR_T_INIT (0x8)
#define SDR_T_INIT_REF (0x7)
#define SDR_T_WTR (0x2)
#define SDR_T_RRD (0x2)
#define SDR_T_XP (0x0)
enum dram_type_t
{
DRAM_TYPE_SDR = 0,
DRAM_TYPE_DDR = 1,
DRAM_TYPE_MDDR = 2,
};
struct dram_para_t
{
u32_t base; /* dram base address */
u32_t size; /* dram size (unit: MByte) */
u32_t clk; /* dram work clock (unit: MHz) */
u32_t access_mode; /* 0: interleave mode 1: sequence mode */
u32_t cs_num; /* dram chip count 1: one chip 2: two chip */
u32_t ddr8_remap; /* for 8bits data width DDR 0: normal 1: 8bits */
enum dram_type_t sdr_ddr;
u32_t bwidth; /* dram bus width */
u32_t col_width; /* column address width */
u32_t row_width; /* row address width */
u32_t bank_size; /* dram bank count */
u32_t cas; /* dram cas */
};
static inline void sdelay(int loops)
{
__asm__ __volatile__ ("1:\n" "subs %0, %1, #1\n"
"bne 1b":"=r" (loops):"0"(loops));
}
static void dram_delay(int ms)
{
sdelay(ms * 2 * 1000);
}
static int dram_initial(void)
{
unsigned int time = 0xffffff;
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, read32(F1C100S_DRAM_BASE + DRAM_SCTLR) | 0x1);
while((read32(F1C100S_DRAM_BASE + DRAM_SCTLR) & 0x1) && time--)
{
if(time == 0)
return 0;
}
return 1;
}
static int dram_delay_scan(void)
{
unsigned int time = 0xffffff;
write32(F1C100S_DRAM_BASE + DRAM_DDLYR, read32(F1C100S_DRAM_BASE + DRAM_DDLYR) | 0x1);
while((read32(F1C100S_DRAM_BASE + DRAM_DDLYR) & 0x1) && time--)
{
if(time == 0)
return 0;
}
return 1;
}
static void dram_set_autofresh_cycle(u32_t clk)
{
u32_t val = 0;
u32_t row = 0;
u32_t temp = 0;
row = read32(F1C100S_DRAM_BASE + DRAM_SCONR);
row &= 0x1e0;
row >>= 0x5;
if(row == 0xc)
{
if(clk >= 1000000)
{
temp = clk + (clk >> 3) + (clk >> 4) + (clk >> 5);
while(temp >= (10000000 >> 6))
{
temp -= (10000000 >> 6);
val++;
}
}
else
{
val = (clk * 499) >> 6;
}
}
else if(row == 0xb)
{
if(clk >= 1000000)
{
temp = clk + (clk >> 3) + (clk >> 4) + (clk >> 5);
while(temp >= (10000000 >> 7))
{
temp -= (10000000 >> 7);
val++;
}
}
else
{
val = (clk * 499) >> 5;
}
}
write32(F1C100S_DRAM_BASE + DRAM_SREFR, val);
}
static int dram_para_setup(struct dram_para_t * para)
{
u32_t val = 0;
val = (para->ddr8_remap) |
(0x1 << 1) |
((para->bank_size >> 2) << 3) |
((para->cs_num >> 1) << 4) |
((para->row_width - 1) << 5) |
((para->col_width - 1) << 9) |
((para->sdr_ddr ? (para->bwidth >> 4) : (para->bwidth >> 5)) << 13) |
(para->access_mode << 15) |
(para->sdr_ddr << 16);
write32(F1C100S_DRAM_BASE + DRAM_SCONR, val);
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, read32(F1C100S_DRAM_BASE + DRAM_SCTLR) | (0x1 << 19));
return dram_initial();
}
static u32_t dram_check_delay(u32_t bwidth)
{
u32_t dsize;
u32_t i,j;
u32_t num = 0;
u32_t dflag = 0;
dsize = ((bwidth == 16) ? 4 : 2);
for(i = 0; i < dsize; i++)
{
if(i == 0)
dflag = read32(F1C100S_DRAM_BASE + DRAM_DRPTR0);
else if(i == 1)
dflag = read32(F1C100S_DRAM_BASE + DRAM_DRPTR1);
else if(i == 2)
dflag = read32(F1C100S_DRAM_BASE + DRAM_DRPTR2);
else if(i == 3)
dflag = read32(F1C100S_DRAM_BASE + DRAM_DRPTR3);
for(j = 0; j < 32; j++)
{
if(dflag & 0x1)
num++;
dflag >>= 1;
}
}
return num;
}
static int sdr_readpipe_scan(void)
{
u32_t k = 0;
for(k = 0; k < 32; k++)
{
write32(0x80000000 + 4 * k, k);
}
for(k = 0; k < 32; k++)
{
if(read32(0x80000000 + 4 * k) != k)
return 0;
}
return 1;
}
static u32_t sdr_readpipe_select(void)
{
u32_t value = 0;
u32_t i = 0;
for(i = 0; i < 8; i++)
{
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, (read32(F1C100S_DRAM_BASE + DRAM_SCTLR) & (~(0x7 << 6))) | (i << 6));
if(sdr_readpipe_scan())
{
value = i;
return value;
}
}
return value;
}
static u32_t dram_check_type(struct dram_para_t * para)
{
u32_t val = 0;
u32_t times = 0;
u32_t i;
for(i = 0; i < 8; i++)
{
val = read32(F1C100S_DRAM_BASE + DRAM_SCTLR);
val &= ~(0x7 << 6);
val |= (i << 6);
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, val);
dram_delay_scan();
if(read32(F1C100S_DRAM_BASE + DRAM_DDLYR) & 0x30)
times++;
}
if(times == 8)
{
para->sdr_ddr = DRAM_TYPE_SDR;
return 0;
}
else
{
para->sdr_ddr = DRAM_TYPE_DDR;
return 1;
}
}
static u32_t dram_scan_readpipe(struct dram_para_t * para)
{
u32_t i, rp_best = 0, rp_val = 0;
u32_t val = 0;
u32_t readpipe[8];
if(para->sdr_ddr == DRAM_TYPE_DDR)
{
for(i = 0; i < 8; i++)
{
val = read32(F1C100S_DRAM_BASE + DRAM_SCTLR);
val &= ~(0x7 << 6);
val |= (i << 6);
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, val);
dram_delay_scan();
readpipe[i] = 0;
if((((read32(F1C100S_DRAM_BASE + DRAM_DDLYR) >> 4) & 0x3) == 0x0) &&
(((read32(F1C100S_DRAM_BASE + DRAM_DDLYR) >> 4) & 0x1) == 0x0))
{
readpipe[i] = dram_check_delay(para->bwidth);
}
if(rp_val < readpipe[i])
{
rp_val = readpipe[i];
rp_best = i;
}
}
val = read32(F1C100S_DRAM_BASE + DRAM_SCTLR);
val &= ~(0x7 << 6);
val |= (rp_best << 6);
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, val);
dram_delay_scan();
}
else
{
val = read32(F1C100S_DRAM_BASE + DRAM_SCONR);
val &= (~(0x1 << 16));
val &= (~(0x3 << 13));
write32(F1C100S_DRAM_BASE + DRAM_SCONR, val);
rp_best = sdr_readpipe_select();
val = read32(F1C100S_DRAM_BASE + DRAM_SCTLR);
val &= ~(0x7 << 6);
val |= (rp_best << 6);
write32(F1C100S_DRAM_BASE + DRAM_SCTLR, val);
}
return 0;
}
static u32_t dram_get_dram_size(struct dram_para_t * para)
{
u32_t colflag = 10, rowflag = 13;
u32_t i = 0;
u32_t val1 = 0;
u32_t count = 0;
u32_t addr1, addr2;
para->col_width = colflag;
para->row_width = rowflag;
dram_para_setup(para);
dram_scan_readpipe(para);
for(i = 0; i < 32; i++)
{
*((u32_t *)(0x80000200 + i)) = 0x11111111;
*((u32_t *)(0x80000600 + i)) = 0x22222222;
}
for(i = 0; i < 32; i++)
{
val1 = *((u32_t *)(0x80000200 + i));
if(val1 == 0x22222222)
count++;
}
if(count == 32)
{
colflag = 9;
}
else
{
colflag = 10;
}
count = 0;
para->col_width = colflag;
para->row_width = rowflag;
dram_para_setup(para);
if(colflag == 10)
{
addr1 = 0x80400000;
addr2 = 0x80c00000;
}
else
{
addr1 = 0x80200000;
addr2 = 0x80600000;
}
for(i = 0; i < 32; i++)
{
*((u32_t *)(addr1 + i)) = 0x33333333;
*((u32_t *)(addr2 + i)) = 0x44444444;
}
for(i = 0; i < 32; i++)
{
val1 = *((u32_t *)(addr1 + i));
if(val1 == 0x44444444)
{
count++;
}
}
if(count == 32)
{
rowflag = 12;
}
else
{
rowflag = 13;
}
para->col_width = colflag;
para->row_width = rowflag;
if(para->row_width != 13)
{
para->size = 16;
}
else if(para->col_width == 10)
{
para->size = 64;
}
else
{
para->size = 32;
}
dram_set_autofresh_cycle(para->clk);
para->access_mode = 0;
dram_para_setup(para);
return 0;
}
static int dram_init(struct dram_para_t * para)
{
u32_t val = 0;
u32_t i;
write32(0x01c20800 + 0x24, read32(0x01c20800 + 0x24) | (0x7 << 12));
dram_delay(5);
if(((para->cas) >> 3) & 0x1)
{
write32(0x01c20800 + 0x2c4, read32(0x01c20800 + 0x2c4) | (0x1 << 23) | (0x20 << 17));
}
if((para->clk >= 144) && (para->clk <= 180))
{
write32(0x01c20800 + 0x2c0, 0xaaa);
}
if(para->clk >= 180)
{
write32(0x01c20800 + 0x2c0, 0xfff);
}
if((para->clk) <= 96)
{
val = (0x1 << 0) | (0x0 << 4) | (((para->clk * 2) / 12 - 1) << 8) | (0x1u << 31);
}
else
{
val = (0x0 << 0) | (0x0 << 4) | (((para->clk * 2) / 24 - 1) << 8) | (0x1u << 31);
}
if(para->cas & (0x1 << 4))
{
write32(F1C100S_CCU_BASE + CCU_PLL_DDR0_PAT, 0xd1303333);
}
else if(para->cas & (0x1 << 5))
{
write32(F1C100S_CCU_BASE + CCU_PLL_DDR0_PAT, 0xcce06666);
}
else if(para->cas & (0x1 << 6))
{
write32(F1C100S_CCU_BASE + CCU_PLL_DDR0_PAT, 0xc8909999);
}
else if(para->cas & (0x1 << 7))
{
write32(F1C100S_CCU_BASE + CCU_PLL_DDR0_PAT, 0xc440cccc);
}
if(para->cas & (0xf << 4))
{
val |= 0x1 << 24;
}
write32(F1C100S_CCU_BASE + CCU_PLL_DDR_CTRL, val);
write32(F1C100S_CCU_BASE + CCU_PLL_DDR_CTRL, read32(F1C100S_CCU_BASE + CCU_PLL_DDR_CTRL) | (0x1 << 20));
while((read32(F1C100S_CCU_BASE + CCU_PLL_DDR_CTRL) & (1 << 28)) == 0);
dram_delay(5);
write32(F1C100S_CCU_BASE + CCU_BUS_CLK_GATE0, read32(F1C100S_CCU_BASE + CCU_BUS_CLK_GATE0) | (0x1 << 14));
write32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0, read32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0) & ~(0x1 << 14));
for(i = 0; i < 10; i++)
continue;
write32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0, read32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0) | (0x1 << 14));
val = read32(0x01c20800 + 0x2c4);
(para->sdr_ddr == DRAM_TYPE_DDR) ? (val |= (0x1 << 16)) : (val &= ~(0x1 << 16));
write32(0x01c20800 + 0x2c4, val);
val = (SDR_T_CAS << 0) | (SDR_T_RAS << 3) | (SDR_T_RCD << 7) | (SDR_T_RP << 10) | (SDR_T_WR << 13) | (SDR_T_RFC << 15) | (SDR_T_XSR << 19) | (SDR_T_RC << 28);
write32(F1C100S_DRAM_BASE + DRAM_STMG0R, val);
val = (SDR_T_INIT << 0) | (SDR_T_INIT_REF << 16) | (SDR_T_WTR << 20) | (SDR_T_RRD << 22) | (SDR_T_XP << 25);
write32(F1C100S_DRAM_BASE + DRAM_STMG1R, val);
dram_para_setup(para);
dram_check_type(para);
val = read32(0x01c20800 + 0x2c4);
(para->sdr_ddr == DRAM_TYPE_DDR) ? (val |= (0x1 << 16)) : (val &= ~(0x1 << 16));
write32(0x01c20800 + 0x2c4, val);
dram_set_autofresh_cycle(para->clk);
dram_scan_readpipe(para);
dram_get_dram_size(para);
for(i = 0; i < 128; i++)
{
*((volatile u32_t *)(para->base + 4 * i)) = para->base + 4 * i;
}
for(i = 0; i < 128; i++)
{
if(*((volatile u32_t *)(para->base + 4 * i)) != (para->base + 4 * i))
return 0;
}
return 1;
}
void sys_dram_init(void)
{
struct dram_para_t para;
u32_t * dsz = (void *)0x0000005c;
para.base = 0x80000000;
para.size = 32;
para.clk = PLL_DDR_CLK / 1000000;
para.access_mode = 1;
para.cs_num = 1;
para.ddr8_remap = 0;
para.sdr_ddr = DRAM_TYPE_DDR;
para.bwidth = 16;
para.col_width = 10;
para.row_width = 13;
para.bank_size = 4;
para.cas = 0x3;
if((dsz[0] >> 24) == 'X')
return;
if(dram_init(¶))
dsz[0] = (((u32_t)'X') << 24) | (para.size << 0);
}
f1c100s 初始化ddr 代码工程:f1c100s_project_run_in_sram_9.7z
离线
上面的视频演示 timer0 控制寄存器 TMR0_CTRL_REG 设置为单次触发,
中断程序只进入一次.
TMR0_MODE.
Timer 0 mode.0: Continuous mode. When interval value reached, the timer will not disable
automatically.1: Single mode. When interval value reached, the timer will disable
automatically.
离线
离线
我这个工程和 xboot 的初始化代码不一样,
还有这个代码直接通过 MDK 的调试按钮,下载到 SRAM 里面调试.
1. 如果 main 函数这样 (清除了flash):
int main(void)
{
sys_clock_init();
sys_dram_init();
//....
}
sys_dram_init() 每次都可以初始化通过,
但是 sys_dram_init() 执行过后把 SRAM 的很多处数据都改了,
也没有跟踪到到底哪行代码作怪,
因为数据被改,这样就出现了非法指令,进入了错误中断.
2. 代码改成这样(清除了flash):
int main(void)
{
#if 1
arm32_interrupt_disable();
arm32_mmu_disable();
arm32_icache_disable();
arm32_dcache_disable();
#endif
sys_clock_init();
sys_dram_init();
//....
情况和1 一模一样.
3. 如果第一次用1的方法代码初始化DDR, 程序飞了,停止调试。屏蔽DDR初始化那一行代码
//sys_dram_init();
程序可以正常运行了,循环写帧缓冲区也没有问题, 但是屏幕只显示淡蓝色, 一直都是淡蓝色.
实际上中断程序,看颜色变量是正常的红绿蓝颜色.
4. 如果按上面方法, 把xboot 的 bin 烧录到 flash(只有ddr 初始化,然后进入死循环), 然后再用MDK SRAM调试,
int main(void)
{
sys_clock_init();
//sys_dram_init();
//....
}
代码中不再有 DDR 初始化相关部分, 那么运行正常, 可以蓝绿红切换屏幕.
离线
有朋友反应MDK没有 ARM9 器件可以选择, 解决方案如下:
2019-04-13 更新
---------------------------------------------
支持ARM7/9旧器件的 MDK 版本:
http://www2.keil.com/mdk5/legacy/
https://armkeil.blob.core.windows.net/legacy/MDK79525.EXE
https://armkeil.blob.core.windows.net/legacy/MDK79524.EXE
离线