ARM下S5P6818 串口程序编写

一、踩坑指南

        这部分有兴趣可以看,没兴趣直接最下面二章节

        先讲讲我遇见的坑:串口程序的编写同样是对寄存器的配置,这一块涉及到时钟,时钟的配置对于裸机来讲,需要看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--); 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值