引言
在系统使用过程中,可能会遇到系统资源(如串口)不够的情况,此时可以使用 IO 口模拟串口,实现同样的功能。本应用笔记介绍了在 APM32F4xx 系列上通过外部中断和定时器如何实现IO 口模拟串口。
APM32F4xxUSART简介
USART(通用同步异步收发器)是一个可以灵活地与外部设备进行全双工、半双工数据交换的串行通信设备,且同时满足外部设备对工业标准NRZ 异步串行数据格式的要求。USART 还提供宽范围的波特率选择,且支持多处理器通信。USART 不仅支持标准的异步收发模式,也支持一些其他的串行数据交换模式,如LIN 协议、智能卡协议、IrDA SIR ENDEC 规范和硬件流控制模式。USART 还支持使用DMA 功能,以实现高速数据通信。
串口模式
串行通讯是指将数据逐位顺序传送的通信方式。串口分为同步串行接口和异步串行接口。
同步模式:一次传输的数据块中包含的数据较多,所以接收时钟与发送时钟严格同步,比异步模式多了一个可以输出同步时钟的信号线USART_CK。适用于大批量数据需要传输的情况。下图为USART 同步发送时序图,分别给出极性和相位自由组合成的四种情况,图1USART_CTRL1寄存器的DBLCFG=0,对应1位起始位,8位数据位,一位停止位的情况,图2USART_CTRL1寄存器的DBLCFG=1,对应1位起始位,9位数据位,1位停止位的情况。其中USART_CK 的时钟极性由USART_CTRL2 寄存器的CPOL位决定:CPOL为0时,CK 引脚的空闲状态为低电平;CPOL为1时,CK引脚的空闲状态为高电平。USART_CK的相位由USART_CTRL2寄存器的CPHA位决定:CPHA为0时,表明在第1个时钟边沿进行采样;CPOL为1时,表明在第2个时钟边沿进行采样。
同步模式:一次传输的数据块中包含的数据较多,所以接收时钟与发送时钟严格同步,比异步模式多了一个可以输出同步时钟的信号线USART_CK。适用于大批量数据需要传输的情况。下图为USART 同步发送时序图,分别给出极性和相位自由组合成的四种情况,图1USART_CTRL1寄存器的DBLCFG=0,对应1位起始位,8位数据位,一位停止位的情况,图2USART_CTRL1寄存器的DBLCFG=1,对应1位起始位,9位数据位,1位停止位的情况。其中USART_CK 的时钟极性由USART_CTRL2 寄存器的CPOL位决定:CPOL为0时,CK 引脚的空闲状态为低电平;CPOL为1时,CK引脚的空闲状态为高电平。USART_CK的相位由USART_CTRL2寄存器的CPHA位决定:CPHA为0时,表明在第1个时钟边沿进行采样;CPOL为1时,表明在第2个时钟边沿进行采样。

图 1 USART 同步发送时序图(DBLCFG=0)

图 2 USART 同步发送时序图(DBLCFG=1)
异步模式是串行,异步,全双工的通信。通过收发双方约定相同波特率实现同步通信,数据以相同的帧格式进行发送。通常运用在短距离、速率不高的工业实际应用中。本文主要讨论异步串口通信情况。
UART 通信协议概述
异步通信需要收发双方约定波特率,确定每位比特位的持续时间,以保证双方的时序同步。其中,波特率是指单位时间内传送的码元的符号个数。
如图 3,图 4 所示,串口的报文格式为:起始位(1bit)+ 数据位(5~8 bits)+ 奇偶校验位(0/1 bit)+停止位(0.5~1.5 bits)。
起始位:使用串口时,在发送有效,自动产生 1bit 的低电平起始位。
数据位:通信时有效数据的长度,通常为 5~8 位。在进行数据收发前,应先完成相应的配置。
奇偶校验位:增加校验位以检验传输过程中是否因干扰出现数据传输错误的情况。设置为奇校验,确保传输数据逻辑高位的个数为奇数;设置为偶校验,确保传输数据逻辑高位的个数为偶数。
停止位:有效数据发送完毕后,发送设定位数的高电平,表示一帧数据传输结束。空闲位:在下一个起始位逻辑低位到来之前,数据线一直保持高位状态。空闲位不属于报文内容。

图 3 串口帧结构图(含空闲位)

图 4 串口帧结构图(不含空闲位)
软件模拟串口
硬件设计
本次设计使用 APM32F407ZGT6 的 GPIO PB9,PB10 分别模拟串口的发送线 TX 和接收线 RX。借助 USB 转 TTL 线,将 RX 连接开发板的 PB9,TX 连接 PB10,同时将两边地线相连。

图 5 硬件连接图
软件设计
本次设计实现功能为接收串口助手发送的数据后,发送相同数据进行回显。设置波特率为 9600 bps,起始位 1 bit,数据位 8 bit,不设校验位,停止位 1 bit。
设计思路为:将功能实现分为接收和发送两个部分。
接收功能的实现,需要以精确的延时为前提;从而获得正确的数据。例程使用通用定时器定时104us(t = 1/9600 s = 104us)进入中断,在中断服务函数中按位接收数据。
将接收到的数据存入缓存区,并将其发送到串口助手进行回显。
发送功能实现相对简单:将数据线置 0,模拟起始位;利用滴答定时器进行延时 104us;利用 for循环按位发送并延时。完成 8 位数据位传输后,将数据线置 1,模拟停止位。

图 6 软件实现流程图
GPIO 配置
函数完成了 GPIO PB9,PB10 的初始化配置。
将发送引脚 TX PB9 配置为推挽输出模式,输出速度为 50MHz。串口在空闲时,数据位为高电平状态,因此,完成配置后,需将 PB9 置 1。
将接收引脚 RX PB10 配置为上拉输入模式,同时配置外部中断,数据开始传输前,一定会产生一个下降沿。因此将 PB10 中断触发方式设定为下降沿触发。
- void Soft_Usart_Init(void)
- {
- GPIO_Config_T gpioConfig;
- EINT_Config_T eintConfig;
- RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
- /* Tx GPIOB PIN9 */
- GPIO_ConfigStructInit(&gpioConfig);
- gpioConfig.mode = GPIO_MODE_OUT;
- gpioConfig.otype = GPIO_OTYPE_PP;
- gpioConfig.pin = GPIO_PIN_9;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- GPIO_Config(GPIOB,&gpioConfig);
- GPIO_SetBit(GPIOB,GPIO_PIN_9);
- /* Rx GPIOB PIN10 */
- GPIO_ConfigStructInit(&gpioConfig);
- gpioConfig.mode = GPIO_MODE_IN;
- gpioConfig.pin = GPIO_PIN_10;
- gpioConfig.pupd = GPIO_PUPD_UP;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- GPIO_Config(GPIOB,&gpioConfig);
- SYSCFG_ConfigEINTLine(SYSCFG_PORT_GPIOB,SYSCFG_PIN_10);
- eintConfig.line = EINT_LINE_10;
- eintConfig.mode = EINT_MODE_INTERRUPT;
- eintConfig.lineCmd = ENABLE;
- eintConfig.trigger = EINT_TRIGGER_FALLING;
- EINT_Config(&eintConfig);
- NVIC_EnableIRQRequest(EINT15_10_IRQn,2,3);
- }
TMR 配置
例程使用 TMR4 通用定时器。根据波特率 9600 bps 可知,每一位数据的持续时间为 104.16us配置一次计数时间为 1us,周期设定为 104 可达到所需定时效果。
TMR4 时钟线为 APB1,如图 7,图 8 和图 9 所示,根据用户手册和系统时钟初始化函数可知APB1 为 AHB1 进行 4 分频后得到,最大频率为 42MHz。TMR 时钟频率为 42 * 2 = 84MHz。根据计算公式将预分频系数设置为 83,得到计数器驱动时钟为 84 /(83+1)= 1MHz,即计数一次时间为 1/1M s = 1 us。一次中断产生的时间间隔为自动重装载值 104 * 计数一次所需时间 1us=104us。
表格 1 计算公式说明及使用


图 7 RCM 寄存器配置

图 8 APB1PSC 位域说明

图 9 时钟树
- void TMR4_Init(void)
- {
- TMR_BaseConfig_T TMRBaseConfig;
- RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR4);
- TMRBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
- TMRBaseConfig.countMode = TMR_COUNTER_MODE_UP;
- TMRBaseConfig.division = 84 - 1;
- TMRBaseConfig.period = 104;
- TMR_ConfigTimeBase(TMR4,&TMRBaseConfig);
- TMR_ClearStatusFlag(TMR4,TMR_FLAG_UPDATE);
- TMR_Enable(TMR4);
- /* Configure NVIC_IRQRequest */
- TMR_EnableInterrupt(TMR4,TMR_INT_UPDATE);
- NVIC_EnableIRQRequest(TMR4_IRQn,2,1);
- }
|