历史上的今天
今天是:2024年10月13日(星期日)
2018年10月13日 | Tiny210裸机之UART串口操作
2018-10-13 来源:eefocus
start.S源码:
.global _start
_start:
ldr sp, =0xD0030000 @初始化堆栈
b main
====================================================================
main.c:
#include "clock.h"
#include "led.h"
#include "uart.h"
#include "lib.h"
int main(void)
{
led_init(); // 设置对应管脚为输出
clock_init(); // 初始化时钟
uart_init(); // 初始化UART0
uart_putchar('0');
uart_putchar('x');
putchar_hex(0x5a);
while (1)
{
char c = 'x';
putchar('\n');
c = uart_getchar();
putchar(c);
for (c = 'a'; c <= 'z'; c++)
putchar(c);
break;
}
while(1)
{
led_water();
}
return 0;
}
====================================================================
led.c源码:
#include "lib.h"
#define GPJ2CON (*(volatile unsigned int *)0xe0200280)
#define GPJ2DAT (*(volatile unsigned int *)0xe0200284)
#define GPH2CON (*(volatile unsigned int *)0xE0200C40)
#define GPH2DAT (*(volatile unsigned int *)0xE0200C44)
void led_init(void)
{
// 配置GPJ2_0为输出引脚
GPJ2CON = 0x1111;
}
void led_water(void)
{
int i = 0;
while (1)
{
GPJ2DAT = i;
i++;
if (i == 16)
i = 0;
delay();
}
return 0;
}
====================================================================
clock.c源码:
#define APLL_CON (*(volatile unsigned int *)0xe0100100)
#define CLK_SRC0 (*(volatile unsigned int *)0xe0100200)
#define CLK_DIV0 (*(volatile unsigned int *)0xe0100300)
#define MPLL_CON (*(volatile unsigned int *)0xe0100108)
void clock_init(void)
{
// 设置时钟为:
// ARMCLK=1000MHz, HCLKM=200MHz, HCLKD=166.75MHz
// HCLKP =133.44MHz, PCLKM=100MHz, PCLKD=83.375MHz,
// PCLKP =66.7MHz
// SDIV[2:0] : S = 1
// PDIV[13:8] : P = 0x3
// MDIV[25:16]: M = 0x7d
// LOCKED [29]: 1 = 使能锁
// ENABLE [31]: 1 = 使能APLL控制器
// 得出FoutAPLL = 500MHz
APLL_CON = (1<<31)|(1<<29)|(0x7d<<16)|(0x3<<8)|(1<<0);
// 时钟源的设置
// APLL_SEL[0] :1 = FOUTAPLL
// MPLL_SEL[4] :1 = FOUTMPLL
// EPLL_SEL[8] :1 = FOUTEPLL
// VPLL_SEL[12]:1 = FOUTVPLL
// MUX_MSYS_SEL[16]:0 = SCLKAPLL
// MUX_DSYS_SEL[20]:0 = SCLKMPLL
// MUX_PSYS_SEL[24]:0 = SCLKMPLL
// ONENAND_SEL [28]:1 = HCLK_DSYS
CLK_SRC0 = (1<<28)|(1<<12)|(1<<8)|(1<<4)|(1<<0);
// 设置分频系数
// APLL_RATIO[2:0]: APLL_RATIO = 0x0
// A2M_RATIO [6:4]: A2M_RATIO = 0x4
// HCLK_MSYS_RATIO[10:8]: HCLK_MSYS_RATIO = 0x4
// PCLK_MSYS_RATIO[14:12]:PCLK_MSYS_RATIO = 0x1
// HCLK_DSYS_RATIO[19:16]:HCLK_DSYS_RATIO = 0x3
// PCLK_DSYS_RATIO[22:20]:PCLK_DSYS_RATIO = 0x1
// HCLK_PSYS_RATIO[27:24]:HCLK_PSYS_RATIO = 0x4
// PCLK_PSYS_RATIO[30:28]:PCLK_PSYS_RATIO = 0x1
CLK_DIV0 = (0x1<<28)|(0x4<<24)|(0x1<<20)|(0x3<<16)|(0x1<<12)|(0x4<<8)|(0x4<<4);
// SDIV[2:0] : S = 1
// PDIV[13:8] : P = 0xc
// MDIV[25:16]: M = 0x29b
// VSEL [27]: 0
// LOCKED [29]: 1 = 使能锁
// ENABLE [31]: 1 = 使能MPLL控制器
// 得出FoutAPLL = 667MHz
APLL_CON = (1<<31)|(1<<29)|(0x29d<<16)|(0xc<<8)|(1<<0);
}
====================================================================
uart.c源码:
#define GPA0CON (*(volatile unsigned int *)0xE0200000)
#define ULCON0 (*(volatile unsigned int *)0xE2900000)
#define UCON0 (*(volatile unsigned int *)0xE2900004)
#define UTRSTAT0 (*(volatile unsigned int *)0xE2900010)
#define UTXH0 (*(volatile unsigned char *)0xE2900020)
#define URXH0 (*(volatile unsigned char *)0xE2900024)
#define UBRDIV0 (*(volatile unsigned int *)0xE2900028)
#define UDIVSLOT0 (*(volatile unsigned int *)0xE290002C)
void uart_init(void)
{
// 设置对应GPIO用于UART0
GPA0CON |= 0x22;
// 设置UART0寄存器
// bit[1:0]:0x3 = 8位数据位
// 其他位默认,即1位停止位,无校验,正常模式
ULCON0 |= (0x3<<0);
// Receive Mode [1:0]:1 = 接收采用查询或者中断模式
// Transmit Mode[3:2]:1 = 发送采用查询或者中断模式
// bit[6]:1 = 产生错误中断
// bit[10]:0 = 时钟源为PCLK
UCON0 = (1<<6)|(1<<2)|(1<<0);
// 设置波特率(详细信息请参考手册或者学习日记)
// DIV_VAL = UBRDIVn + (num of 1's in UDIVSLOTn)/16
// DIV_VAL = (PCLK / (bps x 16)) - 1
UBRDIV0 = 0x23;
UDIVSLOT0 = 0x808;
return;
}
char uart_getchar(void)
{
char c;
// 查询状态寄存器,直到有有效数据
while (!(UTRSTAT0 & (1<<0)));
c = URXH0; // 读取接收寄存器的值
return c;
}
void uart_putchar(char c)
{
// 查询状态寄存器,直到发送缓存为空
while (!(UTRSTAT0 & (1<<2)));
UTXH0 = c; // 写入发送寄存器
return;
}
====================================================================
lib.c源码:
void delay(void)
{
volatile int i = 0x100000;
while (i--);
}
void putchar_hex(char c)
{
char * hex = "0123456789ABCDEF";
uart_putchar(hex[(c>>4) & 0x0F]);
uart_putchar(hex[(c>>0) & 0x0F]);
return;
}
int putchar(int c)
{
if (c == '\n') // 如果程序里面为\n
uart_putchar('\r'); // 则在终端里面回车,换行
uart_putchar(c);
return 0;
}
====================================================================
Makefile文件:
uart.bin:start.s main.c uart.c clock.c led.c lib.c
arm-linux-gcc -nostdlib -c start.s -o start.o
arm-linux-gcc -nostdlib -c main.c -o main.o
arm-linux-gcc -nostdlib -c uart.c -o uart.o
arm-linux-gcc -nostdlib -c lib.c -o lib.o
arm-linux-gcc -nostdlib -c clock.c -o clock.o
arm-linux-gcc -nostdlib -c led.c -o led.o
arm-linux-ld -Ttext 0xD0020010 start.o main.o uart.o lib.o clock.o led.o -o uart_elf
arm-linux-objcopy -O binary -S uart_elf uart.bin
clean:
rm -rf *.o *.bin uart_elf *.dis
====================================================================
源码分析:
1.初始化时钟:
代码如下:
void clock_init(void)
{
// 设置时钟为:
// ARMCLK=1000MHz, HCLKM=200MHz, HCLKD=166.75MHz
// HCLKP =133.44MHz, PCLKM=100MHz, PCLKD=83.375MHz,
// PCLKP =66.7MHz
// SDIV[2:0] : S = 1
// PDIV[13:8] : P = 0x3
// MDIV[25:16]: M = 0x7d
// LOCKED [29]: 1 = 使能锁
// ENABLE [31]: 1 = 使能APLL控制器
// 得出FoutAPLL = 1000MHz
APLL_CON = (1<<31)|(1<<29)|(0x7d<<16)|(0x3<<8)|(1<<0);
// 时钟源的设置
// APLL_SEL[0] :1 = FOUTAPLL
// MPLL_SEL[4] :1 = FOUTMPLL
// EPLL_SEL[8] :1 = FOUTEPLL
// VPLL_SEL[12]:1 = FOUTVPLL
// MUX_MSYS_SEL[16]:0 = SCLKAPLL
// MUX_DSYS_SEL[20]:0 = SCLKMPLL
// MUX_PSYS_SEL[24]:0 = SCLKMPLL
// ONENAND_SEL [28]:1 = HCLK_DSYS
CLK_SRC0 = (1<<28)|(1<<12)|(1<<8)|(1<<4)|(1<<0);
// 设置分频系数
// APLL_RATIO[2:0]: APLL_RATIO = 0x0
// A2M_RATIO [6:4]: A2M_RATIO = 0x4
// HCLK_MSYS_RATIO[10:8]: HCLK_MSYS_RATIO = 0x4
// PCLK_MSYS_RATIO[14:12]:PCLK_MSYS_RATIO = 0x1
// HCLK_DSYS_RATIO[19:16]:HCLK_DSYS_RATIO = 0x3
// PCLK_DSYS_RATIO[22:20]:PCLK_DSYS_RATIO = 0x1
// HCLK_PSYS_RATIO[27:24]:HCLK_PSYS_RATIO = 0x4
// PCLK_PSYS_RATIO[30:28]:PCLK_PSYS_RATIO = 0x1
CLK_DIV0 = (0x1<<28)|(0x4<<24)|(0x1<<20)|(0x3<<16)|(0x1<<12)|(0x4<<8)|(0x4<<4);
// SDIV[2:0] : S = 1
// PDIV[13:8] : P = 0xc
// MDIV[25:16]: M = 0x29b
// VSEL [27]: 0
// LOCKED [29]: 1 = 使能锁
// ENABLE [31]: 1 = 使能MPLL控制器
// 得出FOUTmpll = 667MHz
APLL_CON = (1<<31)|(1<<29)|(0x29d<<16)|(0xc<<8)|(1<<0);
}
我当初配置时钟的时候,自认为有一点基础了,就直接跑去看寄存器,进行相应的配置。但是一看寄存器,我就一头雾水了,S5PV210的寄存器太多,里面涉及的关系也太多,完全不知道来龙去脉,于是我就结合上下文看了看。发现一个很重要的参考资料,就是位于芯片手册的361页和362页的图,寄存器中涉及到的,在该图上面都能够找到,所以强烈推荐,配置S5PV210的时钟的时候,一定要参考该图。
上面给出的clock_init()函数,设置的寄存器参数,都是用的推荐值,在手册中都能够查到。
我以HCLKD为什么等于166.75MHz为例,来讲述如何借助芯片手册的361页和362页的图来配置时钟:
1.HCLKD是用于显示域(如LCD等)的时钟,因此在361页的途中找到HCLK_DSYS,顺着箭头方向往回找,发现它是由DIVhclkd分频而来,即HCLK_DSYS = MOUT_DSYS / (HCLK_DSYS_RATIO + 1),类似的分频系数对应于CLK_DIVn寄存器;
2.clock_init()函数将HCLK_DSYS_RATIO设置为了0x3;
3.从图可以得知,MOUT_DSYS要不是来源于SCLKmpll,就是是来源于SCLKa2m,而clock_init()函数将其设置为来源于SCLKmpll,类似于这样的选择时钟源,对应于CLK_SRCn寄存器;
4.从图可以得知,SCLKmpll要不来源于外部晶振的直接输入,要不来源于晶振经过锁相环倍频后的输出,而clock_init()函数将其设置为了来源于晶振经过锁相环倍频后的输出(即FOUTmpll = 667MHz);
最终:HCLKD = 667MHz / (0x3 + 1) = 166.75MHz
如果大家任然没有感悟到,可以再去看看声卡的日志中初始化时钟的部分。
2.初始化串口:
其实s5pv210的串口初始化和2440的串口初始化操作几乎一样:主要设置波特率,8位数据位,1位停止位,无校验,无流控,查询或者中断方式,这些我在提供的代码里面已经有详细的注释。
二者的主要差别就是波特率的设置,S5PV210多出了一个寄存器,是的波特率设置更加精确,描述如下:
分频系数 = ( 串口时钟源的频率 / (波特率 * 16)) - 1;
分频系数 = UBRDIVn寄存器的值 + (UDIVSLOTn寄存器中1的个数) / 16;
以时钟源为PCLKP =66.7MHz,波特率设为115200为例:
分频系数 = ( 66700000 / (115200 * 16)) - 1 = 35.2;
所以:
UBRDIVn寄存器的值 = 35;
UDIVSLOTn寄存器的值 = 0x808;
注意:
问:从这以后,在写Makefile的时候,一定要指明连接地址为0xD0020010,为什么?
答:1.在以后的写的代码将不再是位置无关码,相关知识,请看一期视频;
2.我在上篇学习日记中已经讲解了启动流程,请再参考一下;
史海拾趣
|
本帖最后由 paulhyde 于 2014-9-15 09:00 编辑 电子竞赛人应该具备的经验 1.许多人死在第一天上午,却毫无知觉. 第一天上午是一个选题的日子.一般每个组都有一个能力比较强的担任组长.往往由于缺乏经验,在方案选定上走上歧路. 到底 ...… 查看全部问答> |
|
在ceshell下的SHBindToParent函数有人能解释下如何用吗?请用中文解释,到msdn上抄袭没有意义。 在ceshell下的SHBindToParent函数有人能解释下如何用吗?请用中文解释,到msdn上抄袭没有意义。 … 查看全部问答> |
|
#define ADC2 ((ADC_TypeDef *) ADC2_BASE) #define ADC2 ((ADC_TypeDef *) ADC2_BASE) 中(ADC_TypeDef *) ADC2_BASE是什么啊定义一指针变量吗求解… 查看全部问答> |
|
人人都爱易电源——转发有礼! 活动主题:人人都爱易电源——转发有礼! 活动时间:10月11日—28日 活动链接:https://bbs.eeworld.com.cn/thread-352431-1-1.html … 查看全部问答> |
|
芯片是LM4F231, ARM M4的内核,开发环境是IAR 6.3。 遇到的问题,当定义局部变量时比如 float f=1.1; 时在IAR下察看local 局部变量值时发现f变量值为 也没有分配变量地址。 对于这一问题研究了很久, ...… 查看全部问答> |




