ARM裸机开发——双机异步串行通信

写在前面

 本报告因为期末将至,后续还需要完成一次课程设计,故本次实验较为简单,完成的时间也非常匆忙,故文章内容较为单薄,也可能有着更多疏忽之处,还望大家海涵。


1. 项目任务

 1) 利用S3C2440手册,掌握嵌入式系统三个时钟关系(FCLK、HCLK、PCLK)和异步通信协议,分析解读异步通信寄存器的初始化;
 2) 利用ARM机自带键盘,用串行方式实现ARM机的数据传送程序;
 3) 实现两台ARM机之间异步通信。具体内容:实现带启动的三键控制控制对方三灯程序。即启动键有效时,一个键点亮另一台ARM机的三个灯,一个键关闭另一台ARM机的三个灯,一个键控制另一台ARM机的三灯跑马。启动键无效时,三键不起控制作用(单键用查询);
 4) 编写交叉编译文件Makefile;

2. 项目方案设计

 本实验为ARM裸机开发——双机异步串行通信,主要目的是通过ARM裸机开发实现双机异步串行通信。本项目的实现框图如图2.1所示。
在这里插入图片描述

图2.1 实验步骤方案设计图
 本报告以串行通信、嵌入式时钟原理、程序设计三部分阐述。

3. 串行通信原理

 本实验将以经典的UART通信,即全双工异步串行通信协议,进行通信协议的学习与运用,并且通过UART通信进一步理解嵌入式系统裸机开发原理。

3.1. 异步串行通信原理(UART)

 以最为经典的AT89C52单片机的异步串行通信为例,UART通信为一种全双工、异步、串行通信协议。全双工,意味着双方可以同时进行收发,即“我”可以接收到“你”的数据,同样可以把数据发送给“你”,也即一对一,无法多方通信;异步,实际是数据收发与时钟不同步的概念,即接收方不确定数据在何时到来;串行,数据需要依次通过数据线进行发送,如同绳串一样,发送完当前数据后,才可以发送下一个数据。
 在清晰UART通信协议含义后,如何具体实现UART通信,就需要清晰应用概念。如图3.1所示,介绍部分UART通信应用概念。
在这里插入图片描述

图3.1 UART数据通信协议

 1) 空闲位。当UART双方无数据收发时,双方引脚均保持高电平,即数据为‘1‘,处于空闲状态。
 2) 起始位。当UART开始收发数据时,发送方发送低电平,即逻辑‘0’,表示数据通信开始。因为空闲位为高电平,所以起始位需要区别于空闲位。
 3) 数据位。数据位可以设置为5至8位,对于有线通信而言,通常采用8位数据位一个字节方便读取存储;对于无线红外通信模式,使用较少的数据位可以减少丢包率。
 4) 奇偶校验位。奇偶校验是简单实用的数据传输校验方式,通过计数数据传输中逻辑‘1’的数量为奇数还是偶数,进行简单的数据校验。由于UART通信通常适用于传输速度低,数据量较少的情况下,利用简单的奇偶校验即可一定程度上保证数据准确。
 5) 停止位。当数据发送完成时,发送方发送高电平为停止位,以表示数据收发结束,可以设置1至2位停止位。
 由于UART通信无时钟线,那如何保证数据通信的准确性,换句话说,当第一位数据结束时,何时为下一位数据位,双方如何保证当前数据位为第几位数据位。打个比方,两人通话时,如果一方的语速超过另一方大脑接收语言的处理速度,那么就无法正常通话,在这里的语速,就是数据收发的速率,即波特率。
 实际上,波特率,即为每位数据位的传输时间的倒数,换句话说波特率的倒数即为每隔多少时间需要数据采样一次,如此才能保证在无时钟线的情况下,保证数据双方进行数据通信的准确性。由于串口通信协议的芯片电气传输特性限制的最大传输速率为115200bps,所以通常采用的波特率为115200的分频数,如9600、19200bps。例如本次实验波特率采用了19200bps,利用示波器测量串口通信数据,如图3.2所示。
在这里插入图片描述

图3.2 串口通信波特率测量
 根据波特率计算公式

b a u d r a t e = 1 b i t p e r i o d = 1 52.10 u s = 19194 b p s ≈ 19200 b p s baud rate= \frac{1}{bit period} = \frac{1}{52.10us} = 19194bps ≈ 19200bps baudrate=bitperiod1=52.10us1=19194bps19200bps

 根据上述的实验,可见实际波特率的测量与理论波特率误差极小,换句话说在数据传输较慢时,UART通信的丢包率较小。
 AT89C52单片机中的UART通信为最为经典的串口通信协议,嵌入式系统在除上述基本概念以外,引入了FIFO存储器、流控控制、状态检测等控制方式。
 FIFO存储器,即先进先出、后进后出存储器,为队列存储器,当UART收到数据后,随即将数据压入队列存储器中,直到队列存储器满。FIFO存储器的使用框图如图3.3所示。
在这里插入图片描述

图3.3 FIFO存储器使用框图
 当CPU进程占用时,如果不采用FIFO存储器,UART收到数据无法立即存储,会导致当前数据被下一次数据所覆盖,FIFO存储器相当于提供UART数据缓冲区,当CPU空闲时,读取UART缓冲区数据,以保证数据的完整性。

3.2. 寄存器配置方式

 与实验二中存储器外部设备寄存器初始化相类似,UART配置流程如图3.4所示。
在这里插入图片描述

图3.4 UART配置流程
 以本次实验为例,我们假设UART数据通信协议为8位数据位,1位起始位,1位停止位,无奇偶校验位;波特率为19200,无流控,不采用FIFO存储器。  编写UART_Init函数,如图3.5所示,并以此程序详细解读UART寄存器配置方式。

在这里插入图片描述

图3.5 串口通信初始化函数

 根据图3.4 UART配置流程。
 1) ULCON:UART线路控制寄存器,主要控制UART数据发送方式,即数据位长度与停止位个数,以及奇偶校验位选择。如ULCON=0x03;即8数据位,1起始位,1停止位,无奇偶校验位模式。
 2) UCON:UART控制寄存器,控制UART时钟源设置,以及时钟源分频设置,以及UART收发采用方式设置,由于中文数据手册对于该寄存器说明有所缺失,在此补充寄存器[3:0]位说明,如图3.6。如UCON=0x05;即均采用中断或者查询模式,时钟源为PCLK。
 3) UFCON:UART FIFO控制寄存器,控制FIFO存储器使用的相关配置,本实验未使用FIFO,为此UFCON=0x00。
 4) UMCON:UART Modem寄存器,即流控控制寄存器,本实验不使用流控方式,为此UMCON=0x00。
 5) UBRDIV:波特率寄存器,通过该寄存器具体设置波特率分频值,本实验中设置波特率为19200bps,根据公式UBRDIV=(int)(UART 时钟/(baud rate×16))-1
 综上,五个UART寄存器即可实现UART通信基本配置,但是由于在实际运行中,无法得知数据会在何时到来,所以通常对于接收数据采用接收中断的方式,而发送数据采用轮询的方式,为此需要配置接收中断的寄存器,以实验二中断配置为例,本实验不赘述。
同时,UART通信空闲位为1,所以需要配置UART管脚上拉,同时配置管脚准双向输入输出模式。

4. 嵌入式时钟原理

 UART通信中引入了时钟的设置,借以此引入嵌入式时钟树,并尝试与单片机时钟比较,探讨嵌入式时钟树的作用与意义,将以嵌入式时钟树原理、时钟树配置方式两方面阐述。

4.1. 嵌入式时钟树原理

 与单片机有所不同,嵌入式系统的高性能往往带来了外部设备众多,系统更为复杂,采用单一的时钟总线的方法,无法满足所有外部设备的时钟要求。对于51单片机而言,其运用场合往往是低速、简单控制、设备稳定的场合,采用低速时钟线,可以降低功耗。
 为满足低速信号、高速信号、CPU等设备的时钟需求,S3C2440将主要的时钟线为三种,即提供给CPU的FCLK,提供AHB总线外设的HCLK以及提供给APB总线外设的PCLK,其中AHB总线为高级高性能外设总线,APB为精简低功耗外设总线,以此来区分三种时钟线。打个比方,CPU为大脑,需要提供最为快速的信号处理与反应,所以需要提供最为快速的FCLK时钟线;AHB总线外设为神经系统,需要尽可能快地传递感受信息,需要高速HCLK;APB总线为执行器,无法做到与神经反应相同的反应速度,所以利用慢速PCLK时钟线即可。嵌入式整体的时钟处理框图如图4.1所示。

在这里插入图片描述

图4.1 时钟系统框图
 在大致理清三者时钟线的关系后,如图4.1具体来看时钟系统的产生方式。FCLK、HCLK、PCLK三时钟总线均可以通过同一个时钟源进行产生,时钟源的选择可以为外部晶振或者芯片内部的时钟振荡电路产生,通常采用外部晶振的方式提供时钟源,外部晶振产生的时钟信号更为稳定。UCLK主要为提供USB模块时钟,不是本嵌入式系统的主要时钟线,在此不详细分析,下面主要分析时钟FCLK、HCLK、PCLK产生与分频设置,绘制了时钟信号产生流程图如图4.2所示。

在这里插入图片描述

图4.2 时钟线产生

 1) 时钟源产生时钟信号作为 F i n F_{in} Fin,提供给PLL锁相环。
 2) PLL锁相环,得到 F i n F_{in} Fin后,将 F i n F_{in} Fin信号通过分频或倍频的方式,同时将信号相位同步后,得到时钟参考信号MPLL,具体MPLL频率计算公式为
M P L L = 2 × m × F i n p × 2 S MPLL=\frac{2×m×F_{in}}{p×2^S} MPLL=p×2S2×m×Fin

 3) Power Management,得到MPLL参考时钟信号后,通过设置分频器和电源管理模块使能,得到FCLK、HCLK、PCLK三时钟信号。
 以上便是时钟信号的具体产生过程,在下一部分具体分析时钟信号配置方法。

4.2. 时钟信号配置方法

 时钟线具体配置,实际即确定三时钟线的时钟频率,以本实验的时钟设定为具体演示。令FCLK=200MHz, HCLK=100MHz, PCLK=50MHz根据图4.2时钟产生方法,编写了clock_init函数,如图4.3所示

在这里插入图片描述

图4.3 clock_init函数

5. 嵌入式系统程序设计

 本部分主要演示代码框架和具体代码内容,程序设计将分为代码实现、代码调试问题两部分阐述。

5.1. 代码实现

 本实验整体框架与实验二类似,均采用了中断得到系统状态标志位,主循环根据状态标志位进行不同模式的程序执行,启动引导代码与实验二一致,故本程序设计不涉及启动引导代码,流程如图5.1所示

在这里插入图片描述

图5.1 程序设计流程图

 本次实验与实验二,整体思路与框架一致,实验较为简单,暂无太多代码改变之处,主要放出与UART相关代码,如下

/***********************************************
 * void UART_Init(void)
 * 函数功能:UART初始化函数,使用UART1,TXD-GPH4, RXD-GPH5
 * 协议:数据位8位,1位起始位,1位停止位,无奇偶校验位
 *       波特率19200
 *       无流控
 *       暂时不带FIFO
 *       无错误检查
 * 暂时固定式初始化,后续借鉴HAL库中的UART初始化方式
 * ***********************************************/
void UART_Init(void)
{
    // UART Line Control 线路控制寄存器,由于控制通信数据协议
    ULCON1 = 0X03;      // 8数据位,1起始位,1停止位,无奇偶校验位
    // UART Control 控制寄存器,控制时钟,中断等模式
    UCON1  = 0x05;      // 发送与接收均采用中断或者查询模式,时钟为PCLK
    //  中文数据手册中缺少了部分说明 其中UCON[3:0]控制UART的传输模式,如屏蔽,中断查询模式
    // UART FIFO控制寄存器,FIFO为队列存储器,先进先出,后进后出
    UFCON1 = 0x00;      // 不使用FIFO
    // Modem 控制器,控制流控
    UMCON1 = 0x00;      // 不使用流控
    // 波特率分频寄存器
    UBRDIV1 = UART_BRD; // 波特率为19200

    GPHCON |= 0x000a00;    // GPH4,GPH5用作TXD1,RXD1
    GPHUP  &= ~(0x030);    // GPH4,GPH5内部上拉

    // 设置接收中断
    // 配置中断模式,均为普通中断IRQ
    INTMOD &= ~(BIT_UART1); // BIT_xx 对应中断源位

    // 优先级采用复位值

    INTSUBMSK &= ~(BIT_SUB_RXD1);       // 接收中断使能
    INTMSK &= ~(BIT_UART1);
}


/****************
 * int fputc(int c, FILE *f)
 * 重定向printf函数,但是对于ARM裸机开发,调用标准库的过程暂未解决
 * **************/
// int fputc(int c, FILE *f)
// {
//     while(!(UTRSTAT1 & TXDREADY));

//     UTXH1 = c;
//     return c;
// }


/*****************************************
 * void void UART_SendData(unsigned char c)
 * UART单字节发送函数
 * *************************************/
void UART_SendData(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT1 & TXDREADY));
    
    /* 向UTXH1寄存器中写入数据,UART即自动将它发送出去 */
    UTXH1 = c;
}

/*
 * 接收字符
 */
unsigned char UART_GetData(void)
{
    /* 等待,直到接收缓冲区中的有数据 */
    while (!(UTRSTAT1 & RXDREADY));
    
    /* 直接读取URXH1寄存器,即可获得接收到的数据 */
    return URXH1;
}


/*****************************************
 * void UART1_IRQHandle(void)
 * 暂时仅为接收中断函数,对于发送通常采用轮询模式
 * *************************************/
void UART1_IRQHandle(void)
{
    SUBSRCPND = (1<<3 | 1<<4);
    // while(!(UTRSTAT1 & RXDREADY));
    flag = URXH1;
}

 具体的代码实现均放在附件,本次实验效果如图5.2所示。

在这里插入图片描述

图5.2 代码实现效果

5.2. 代码调试问题

 本次实验整体问题不大,但是在调试过程中,利用示波器和电脑串口测试通信过程中,发现当按键按下过程中,会出现按一次键,发送多次数据的情况,也即按键进入多次中断。针对该问题,尝试在外部中断内部添加延时函数,会发现数据明显减少,证明问题产生符合按键抖动的现象。但是,硬件中断函数中添加延时函数是不符合嵌入式开发思想的,中断程序为处理异常的程序,不应该作为主进程使用,对于查询方式的按键,消抖是较为简单的,但是对于外部中断函数而言,可以采用软件中断的方式进行按键消抖。但是这样较为复杂。
S3C2440提供更为简便的按键中断消抖的硬件方式,即采用双边沿触发中断的方式,如图5.3所示设置对应外部中断的触发方式为双边沿触发。即可得到较为稳定的机械消抖方式。

在这里插入图片描述

图5.3 中断触发方式
 但是对于UART通信而言,按键抖动对于控制状态影响较小。只是调式过程中的小问题,本次实验任务较为简单,在实验过程中,希望有所拓展。  在此前的STM32UART通信的学习过程中,常常利用keil中Mirco库进行Printf重定向,从而实现格式化输出通讯方式,为此,在ARM裸机开发的学习过程中,希望尝试使用gcc标准库实现printf重定向函数,这是本次实验过程遇到的主要问题。  深入理解ARM裸机开发,需要明确stdio.h为c99标准下的标准输入输出库,如果需要调用该标准库,即需要使用适配C99标准下的标准C语言编译器,但是ARM裸机开发采用了交叉编译工具链的方法,即编译器——链接器——反汇编器,即对应Makefile中命令。
objs := startup.o gpio.o init.o interrupt.o main.o nand.o uart.o

exit.bin: $(objs)
#	连接指令,将exit.lds链接文件,编译为exit_elf, 即各文件接口
	arm-linux-ld -Texit.lds -o exit_elf $^
#	将各个可执行文件根据exit_elf文件转译为二进制文件
	arm-linux-objcopy -O binary -S exit_elf $@
#	将各个可执行文件根据exit_elf文件转译为反汇编文件,即转译为汇编语言,根据ARM指令
	arm-linux-objdump -D -m arm exit_elf > exit.dis

#	将.c文件编译为可执行文件
%.o:%.c
	arm-linux-gcc -Wall -O2 -c -o $@ $<

#	将.S文件编译为可执行文件
%.o:%.S
	arm-linux-gcc -Wall -O2 -c -o $@ $<

#	删除当前目录下的编译文件
clean:
	rm -f exit.bin exit_elf exit.dis *.o		
		

 1) 编译器,即arm-linux-gcc,该编译器即为满足c99标准的arm-linux交叉C语言编译器,gcc中内置连接器,但是较为复杂。
 2) 链接器,即arm-linux-ld,将编译器得到的各可执行文件,连接为一个文件并生成二进制文件。
 3) 反汇编器,即arm-linux-objdump,将链接器生成二进制文件反汇编为汇编语言,以提供给程序分析。
 根据上述对于交叉工具链的分析,如果需要使用C语言标准库,则必须要将静态链接库与实验代码相链接,由于stdio.h为C语言标准库,编译器内置文件中无对应源码,仅包含二进制文件,采用ld链接器的方式,出现了gcc与链接器版本不兼容的情况。因此,希望采用gcc内置链接器进行代码链接,以得到标准静态链接库;只是发现ubuntu9版本较旧,其配置gcc编译器版本也与市面主流版本不同,暂时没有解决。

6. 实验总结

6.1. 实验结果

 本次实验内容较少,实验任务全部完成,深入了解了UART通信原理以及嵌入式系统时钟分配方法。

6.2. 心得体会

 本次实验内容较少,在实验本身实现过程中,没有太多的拓展空间,几乎与实验二内容相同,对于外部设备的使用而言,几乎所有的外部设备使用都有相似的工作流程,即设置设备工作方式(工作模式)——设置设备与CPU通信协议(数据传输方式)——设置CPU响应设备方式(中断或轮询等)——设备使能与管脚分配方式,所以在实验二中了解完外部设备的控制方式后,对于本次实验几乎没有区别。
但是,在本次实验探究C语言标准库的使用过程上,深入理解了ARM裸机开发的本质以及交叉编译的实质,但是受限于本次实验时间较短,无法完全实现printf重定向,略显遗憾,在日后的ARM裸机开发中,尝试解决该问题。

最后

 源代码将考虑以git的形式开源上传,在完成大作业后统一开源分享。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值