一、踩坑指南
这部分有兴趣可以看,没兴趣直接最下面二章节
先讲讲我遇见的坑:串口程序的编写同样是对寄存器的配置,这一块涉及到时钟,时钟的配置对于裸机来讲,需要看uboot文件,这个文件涉及到对基础时钟的初始化,我在文件中查看到对pll0的配置希望其输出的是800mhz的频率,但是实际上并不是输出的800mhz,这里也算是我没解决的地方,我暂且将其归于时钟没初始化成功。(下面是nsih文件的一部分)
// CLKPWR registers
//------------------------------------------------------------------------------
100CC801 // 0x05C : PLL0 800MHz P:3 M:200 S:1
100CC801 // 0x060 : PLL1 800MHz P:3 M:200 S:1
100CC301 // 0x064 : PLL2 780MHz P:3 M:195 S:1 K:0
100CC801 // 0x068 : PLL3 800MHz P:3 M:200 S:1 K:0
66660104 // 0x06C : PLL2 SPREAD
00000104 // 0x070 : PLL3 SPREAD
00000601 // 0x074 : CPU G0 PLL1 /FCLK:800 /HCLK:200
00000208 // 0x078 : BUS PLL0 /BCLK:400 /PCLK:200
00208003 // 0x07C : MEM PLL3 /MDCLK:800 /MCLK:800 /MBCLK:400 /MPCLK:200
00000208 // 0x080 : GR3D PLL0 /GR3DBCLK:400
00000208 // 0x084 : MPEG PLL0 /MPEGBCLK:400 /MPEGPCLK:200
00000208 // 0x088 : DISP PLL0 /DISPBCLK:400 /DISPPCLK:200
00000038 // 0x08C : HDMI PLL0 /HDMIPCLK:100
00000601 // 0x090 : CPU G1 PLL1 /FCLK:800 /HCLK:200
00000208 // 0x094 : CCI4 PLL0 /CCI4BCLK:400 /CCI4PCLK:200
其中可以看到的是pll0配置800mhz,将100cc801的pms配置位读出来的值也是p:3 m:200 s:1
这里锁相环输出的值计算(m*fin)/(p*2s) fin是输入晶振的频率24mhz 2s是2的s次方
后面我对串口时钟的选择是pll0 对串口时钟相关寄存器的配置是选择pll0 对pll0的输出进行除这里是80那么正常得到的用到串口的时钟是10mhz,最后对波特率的相关整数部分和小数部分的因子也按照10mhz计算的(这里涉及到波特率计算公式(f/(btl816)-1),最后测试的结果不尽人意,我希望的波特率是115200最后测试76800左右的时候是正确的,根据这个76800的波特率进行反推,得到的是从ppl0下550mhz的输出频率。那这里我就暂时推测是时钟uboot的时候没成功,接下来只能从pll0配置寄存器去寻找答案,这里我查找手册后发现默认的pll0的输出是550mhz,查到这里,我就暂且将pll0输出的频率错误归结于初始化失败,这里个人理解仅作参考。下面是具体的程序。
二、编写程序逻辑
(1)首先查看原理图确认使用到哪一组串口(这里我们一般使用的是核心板,它对应有四组长排线,根据四组选择自己使用的uart,底板的话是自己制作,看自己的原理图即可),这里我使用的是uart1,确认对应的引脚D19Tx D15Rx
(2)查看引脚的复用功能,确定使用的复用几,然后对其初始化
(3)配置串口时钟,找到数据手册对应的章节,配置串口使用到那个锁相环输出的时钟,以及除数
(4)配置串口的基础设置,数据帧格式,也根据步骤三中的串口寄存器总览查看,应该配置哪些寄存器,配置完成后既可以编译烧写(这里可以参考我的其他文章制作sd卡启动)
三、程序源码
该程序是测试读寄存器地址的
//寄存器的定义
#define GPIODOUT (*(volatile unsigned int *)0xC001D000) //GPIOEOUT-->寄存器的内容
#define GPIODOUTENB (*(volatile unsigned int *)0xC001D004)
#define GPIODALTFN0 (*(volatile unsigned int *)0xC001D020)
#define GPIODALTFN1 (*(volatile unsigned int *)0xC001D024)
//时钟配置寄存器
#define PLLSETREG (*(volatile unsigned int *)0xC0010008) // PLL0 配置寄存器
#define UARTCLKENB (*(volatile unsigned int *)(0xC00A8000)) // UART 时钟使能寄存器
#define UARTCLKGEN0L (*(volatile unsigned int *)(0xC00A8004)) // UART 时钟源配置寄存器
//串口寄存器
#define S5P6818_UART1_BASE 0xC00A0000
#define UART_UBRDIV *(volatile unsigned int *)(S5P6818_UART1_BASE + 0x28) // 波特率整数部分寄存器
#define UART_UFRACVAL *(volatile unsigned int *)(S5P6818_UART1_BASE + 0x2C) // 波特率小数部分寄存器
#define UART_ULCON *(volatile unsigned int *)(S5P6818_UART1_BASE + 0x00) // 数据格式寄存器
#define UART_UCON *(volatile unsigned int *)(S5P6818_UART1_BASE + 0x04) // 控制寄存器
//状态相关 这部分寄存器可以参考数据手册查看内容
#define UART_UTRSTAT (0x10)
#define UART_UTRSTAT_TXFE (1<<1)
#define UART_UTRSTAT_RXDR (1<<0)
#define UART_UTXH (0x20)
#define UART_URXH (0x24)
void delay(unsigned value);
void uart_io_init();
void myputc(unsigned int addr);
void myputs(char *str);
void uartx_init();
void uartclk_init();
void mygetc();
void _start(void)
{
uart_io_init();
uartclk_init();
uartx_init();
while(1)
{
mygetc();
}
}
/*发送一个Byte的数据*/
void myputc(unsigned int tdata)
{
u32_t data,reldata;
u8_t part;
u8_t i=0;
volatile unsigned int *UTRSTAT_REG = (volatile unsigned int *)(S5P6818_UART1_BASE + UART_UTRSTAT);
volatile unsigned char *UTXH_REG = (volatile unsigned char *)(S5P6818_UART1_BASE + UART_UTXH);
for( i=0;i<4;i++){
// 等待上一次发送完成
while ((*UTRSTAT_REG & UART_UTRSTAT_TXFE) == 0);
// 取出每个字节并发送 (最低字节先发送,符合UART的LSB传输机制)
part = (tdata >> (i * 8)) & 0xFF;
*UTXH_REG = part;
}
}
void mygetc(){
volatile unsigned int *UTRSTAT_REG = (volatile unsigned int *)(S5P6818_UART1_BASE + UART_UTRSTAT);
volatile unsigned char *URXH_REG = (volatile unsigned char *)(S5P6818_UART1_BASE + UART_URXH);
//等待接收完成
u32_t data = 0x00000000; // 清零防止数据残留
u8_t i;
u32_t data1 ; // 清零防止数据残留
u32_t tdat;
for (i = 0; i < 4; i++) {
// 等待接收完成
while ((*UTRSTAT_REG & UART_UTRSTAT_RXDR) == 0);
// 数据拼接 (小端模式,低字节先接收)
data |= (*URXH_REG) << ((3-i) * 8);
}
tdata=*(volatile unsigned int *)(data);
myputc(tdata);
}
void uartx_init()
{
UART_ULCON |= (3<<0); /*8bit数据位*/
UART_ULCON &= (~(1<<2)); /*1个停止位*/
UART_ULCON &= (~(7<<3)); /*无奇偶校验*/
UART_ULCON &= (~(1<<6)); /*正常模式*/
/* 设置波特率为115200 (uartclk / (115200*16)) - 1 = 4.4210MHz */
UART_UBRDIV &= (~0xFFFF);
UART_UBRDIV = 4; /*整数部分*/
UART_UFRACVAL &= (~0xF);
UART_UFRACVAL = 7; /*小数部分,UFRACVAL=0.42×16 */
UART_UCON &= (~0xF);
UART_UCON |= (0x1); /*接收轮询模式 这里不采用中断,轮询也可以完成数据的发送和接受,但是每次只能接受或者发送一个字节*/
UART_UCON |= (0x1<<2); /*发送轮询模式 */
}
void uartclk_init()
{
UARTCLKENB &= ~(1<<2); /*关闭uartclk*/
UARTCLKGEN0L &= (~(7<<2));/*选择PLL0作为时钟源,550MHz pre6ok cur7 */
UARTCLKGEN0L &= (~(0xFF<<5));
//80-->53
UARTCLKGEN0L |= ((55-1)<<5); /*PLL0通过53分频得到,即uartclk频率为10MHz pre53ok cur55*/
UARTCLKENB |= (1<<2); /*使能uartclk*/
}
void uart_io_init()
{
/*GPIOD的19引脚复用为TXD1,ALT1的[7:6]bit=01,GPIOD的引脚15复用为RXD1,ALT0的[31:30]bit=01*/
GPIODALTFN1 &= (~(0x3<<6));
GPIODALTFN1 |= (0x1<<6);
GPIODALTFN0 &= (~(0x3<<30));
GPIODALTFN0 |= (0x1<<30);
}
void delay(unsigned value)
{
while(value--);
}