大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是嵌入式里串口(UART)自动波特率识别程序设计与实现。
本篇是 《串口(UART)自动波特率识别程序设计与实现(中断)》 的续集,上一篇我们利用 GPIO 模块自带的下降沿中断功能实现了 RXD 电平跳变捕捉与计时,今天我们再试试古老的轮询 RXD 管脚电平的方法去实现同样的功能。
轮询法最大的缺点是会阻塞系统(不考虑 RTOS 调度),但是它也有中断法所没有的特点(或者说不太方便做到的),在做轮询时,我们可以采取一些经典的管脚电平软件消抖措施,从而降低误识别率。
- 程序主页:https://github.com/JayHeng/cortex-m-apps/tree/master/components/autobaud
一、串口(UART)自动波特率识别程序设计
1.1 函数接口定义
轮询法与中断法函数接口保持一致,详见 《串口(UART)自动波特率识别程序设计与实现(中断)》 1.1 小节,两者共享头文件:autobaud.h,这样方便项目设计时自由切换自动波特率识别方法。
1.2 识别设计思想
关于识别的思路,轮询法与中断法也是一致的,详见 《串口(UART)自动波特率识别程序设计与实现(中断)》 1.2 小节,但是轮询法里多了手动检测 RXD 引脚电平下降沿跳变的过程。
引脚电平跳变检测其实也很简单,就是不断读取引脚输入电平值,并比较相邻两次输入电平值,如果发现不一致,则是跳变发生之时。如果前一次电平值是高,那么此时便是下降沿。
1.3 主代码实现
根据上一小节描述的设计思想,我们很容易写出下面的主代码(autobaud_poll_v2.1.c),代码里痞子衡都做了详细注释。相比中断法源代码,我们其实只需要修改 autobaud_get_rate() 函数实现如下:
//! @brief 读取GPIO管脚输入电平
extern uint32_t read_autobaud_pin(void);
bool autobaud_get_rate(uint32_t *rate)
{
// 仅当电平为低(非空闲态)时才开始识别
uint32_t currentEdge = read_autobaud_pin();
if (currentEdge != 1)
{
pin_transition_callback();
uint32_t previousEdge = currentEdge;
while (s_transitionCount < kFirstByteRequiredFallingEdges + kSecondByteRequiredFallingEdges)
{
// 检查是否有电平翻转
currentEdge = read_autobaud_pin();
if (currentEdge != previousEdge)
{
// 仅当电平翻转是下降沿时
if (previousEdge == 1)
{
pin_transition_callback();
}
previousEdge = currentEdge;
}
}
// 计算出实际检测到的波特率值
uint32_t calculatedBaud =
(microseconds_get_clock() * (kNumberOfBitsForFirstByteMeasured + kNumberOfBitsForSecondByteMeasured)) /
(uint32_t)(s_firstByteTotalTicks + s_secondByteTotalTicks);
// 对实际检测出的波特率值做对齐处理
// 公式:rounded = stepSize * (value/stepSize + .5)
*rate = ((((calculatedBaud * 10) / kAutobaudStepSize) + 5) / 10) * kAutobaudStepSize;
return true;
}
else
{
return false;
}
}
二、串口(UART)自动波特率识别程序实现
前面讲的都是硬件无关设计,但最终还是要落实到具体 MCU 平台上的,其中 GPIO 读取部分是跟 MCU 紧相关的。我们以恩智浦 i.MXRT1011 为例来介绍硬件实现。
2.1 软件消抖实现
恩智浦 MIMXRT1010-EVK 有板载调试器 DAPLink,这个 DAPLink 中也集成了 USB 转串口的功能,对应的 UART 引脚是 IOMUXC_GPIO_09_LPUART1_RXD 和 IOMUXC_GPIO_10_LPUART1_TXD,我们就选用这个管脚 GPIO1[9] 做自动波特率检测,引脚电平读取函数代码如下:
#define AUTOBAUD_PIN_DEBOUNCE_READ_COUNT (20U)
uint32_t read_autobaud_pin(void)
{
// 多次读取管脚输入电平值
uint32_t readCount = 0;
for (uint32_t i = 0; i < AUTOBAUD_PIN_DEBOUNCE_READ_COUNT; i++)
{
readCount += GPIO_PinRead(GPIO1, 9);
}
// 如某电平值出现几率超过半数,则认定为有效电平
return (readCount < (AUTOBAUD_PIN_DEBOUNCE_READ_COUNT / 2)) ? 0 : 1;
}
关于 I/O 软件消抖,一般有两种实现:一、是两次 I/O 读取之间加一定延时(us级别),如两次值一样,则认定有效,否则重复此过程;二、是多次读取 I/O 值,取其中出现几率超过一半的那个电平值。前者主要适用按键的场景,后者更适用本文轮询法自动波特率识别场景。
2.2 在MIMXRT1010-EVK上实测
最后就是在板子上实测,因为在设计上轮询法与中断法接口是一致的,因此测试主函数代码完全不用修改,详见 《串口(UART)自动波特率识别程序设计与实现(中断)》 2.2 小节。测试结果同样达到了预期效果。
至此,嵌入式里串口(UART)自动波特率识别程序设计与实现痞子衡便介绍完毕了,掌声在哪里~~~