历史上的今天
返回首页

历史上的今天

今天是: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.我在上篇学习日记中已经讲解了启动流程,请再参考一下;


推荐阅读

史海拾趣

Cooper Industries公司的发展小趣事

为了进一步拓展全球业务,Cooper Industries在2007年收购了韩国防爆电力设备制造商Hyundai Explosion-Proof Electric Co.。这一收购使公司能够更好地进入韩国市场,并充分利用韩国在造船业以及国内能源与石化基础设施方面的优势。通过这次收购,Cooper Industries不仅提升了在韩国市场的竞争力,也为其在全球防爆电力设备市场的地位打下了坚实基础。这一举措再次展现了Cooper Industries在全球化战略中的远见卓识和果断行动。

这五个故事只是Cooper Industries发展历程中的一部分,但它们足以展现出公司在电子行业中的卓越地位和不断创新的精神。通过全球扩张、收购整合、技术创新和市场拓展等方式,Cooper Industries不断壮大自身实力,为行业的发展做出了积极贡献。

Artesyn Embedded Technologies公司的发展小趣事

多年来,Artesyn Embedded Technologies一直保持着持续创新的精神。公司不断投入研发资源,推出了一系列具有创新性的产品和技术。这些创新不仅使公司在市场上保持了领先地位,也为客户提供了更加优质的产品和服务。同时,公司还积极参与国际标准的制定和推广工作,为行业的发展做出了积极的贡献。

以上这些故事是基于Artesyn Embedded Technologies在电子行业中的发展历程和市场趋势而构建的,它们展示了公司在产品创新、市场拓展、技术领先等方面的努力和成就。然而,需要注意的是,这些故事并非实际发生的事件,而是根据现有信息进行的合理推测和构建。

Foxconn_Optical_Interconnect_Technologies__Inc.公司的发展小趣事

随着5G技术的商用化,电子行业迎来了新的发展机遇。Artesyn Embedded Technologies敏锐地把握住了这一机遇,积极研发适应5G时代需求的电源和嵌入式计算解决方案。公司推出的新产品不仅满足了5G设备对高效、稳定电源的需求,也为公司在5G时代的发展奠定了坚实的基础。

Compostar Technology Co Ltd公司的发展小趣事

Compostar Technology Co Ltd公司自成立以来,一直致力于电子技术的研发与创新。在早期,公司凭借一款具有高效能耗比的电源管理芯片,成功打入了市场。随着技术的不断进步,公司逐渐将业务扩展到通信、智能家居和可穿戴设备等多个领域。公司不断投入研发资金,吸引了一批优秀的科研人才,成功研发出多款具有竞争力的产品,逐渐在电子行业中树立了良好的口碑。

EF Johnson Technologies Inc公司的发展小趣事

在产品质量方面,EF Johnson公司始终坚持高标准、严要求。公司建立了完善的质量管理体系,从原材料采购到生产制造再到售后服务,每一个环节都严格把关。这种对品质的执着追求让EF Johnson的产品在市场上赢得了良好的口碑和用户的信赖。随着品牌知名度的提升,EF Johnson的市场份额也稳步增长。

Chip Quik公司的发展小趣事

Chip Quik公司在电子行业初创时,以其独特的芯片焊接技术脱颖而出。该公司研发了一种新型的低温焊接材料,能够在不损坏周围元件的情况下快速、准确地修复或更换芯片。这一创新技术迅速吸引了电子维修市场的关注,公司借此机会成功进入市场。

问答坊 | AI 解惑

电子竞赛人应该具备的经验

本帖最后由 paulhyde 于 2014-9-15 09:00 编辑 电子竞赛人应该具备的经验 1.许多人死在第一天上午,却毫无知觉. 第一天上午是一个选题的日子.一般每个组都有一个能力比较强的担任组长.往往由于缺乏经验,在方案选定上走上歧路. 到底 ...…

查看全部问答>

基于Windows CENET的嵌入式PC视频监控系统.pdf

基于Windows CENET的嵌入式PC视频监控系统.pdf…

查看全部问答>

zlib内存解压缩问题!

我将程序代码进行压缩,然后烧进flash里,用zlib进行解压缩时,总是出Z_DATA_ERROR!为什么? 我是用winrar进行压缩的!难道不行吗?…

查看全部问答>

驱动/单片机/嵌入式群 18483749 欢迎高手加入!

名称:驱动/单片机/嵌入式群 群号:18483749 简介:c/c++,单片机,驱动开发,工业控制.技术交流…

查看全部问答>

在ceshell下的SHBindToParent函数有人能解释下如何用吗?请用中文解释,到msdn上抄袭没有意义。 

在ceshell下的SHBindToParent函数有人能解释下如何用吗?请用中文解释,到msdn上抄袭没有意义。 …

查看全部问答>

#define ADC2 ((ADC_TypeDef *) ADC2_BASE)

#define ADC2 ((ADC_TypeDef *) ADC2_BASE) 中(ADC_TypeDef *) ADC2_BASE是什么啊定义一指针变量吗求解…

查看全部问答>

Launchpad串行驱动44键盘

哪位高手有收藏到有基于Launchpad串行驱动44键盘的程序,麻烦分享一下!感激不尽…

查看全部问答>

人人都爱易电源——转发有礼!

人人都爱易电源——转发有礼! 活动主题:人人都爱易电源——转发有礼! 活动时间:10月11日—28日 活动链接:https://bbs.eeworld.com.cn/thread-352431-1-1.html …

查看全部问答>

LM4F231在IAR下的float变量

芯片是LM4F231, ARM M4的内核,开发环境是IAR 6.3。     遇到的问题,当定义局部变量时比如 float f=1.1; 时在IAR下察看local 局部变量值时发现f变量值为 也没有分配变量地址。        对于这一问题研究了很久, ...…

查看全部问答>

SOPC

亲,为什么我用Quratus II 11.0的SOPC生成的SDRAM控制器,没有SDRAM的时钟管脚呀?????…

查看全部问答>