0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

UART自动波特率识别程序设计方案

要长高 来源:痞子衡嵌入式 作者: 痞子衡 2022-06-21 15:31 次阅读

串口(UART)是嵌入式里最基础最常用也最简单的一种通讯(数据传输)方式,可以说是工程师入门通讯领域的启蒙老师,同时串口打印也是嵌入式项目里非常经典的调试与交互方式。

最精简的串口仅使用两根单向信号线:TXD、RXD,这两根信号线是独立工作的,因此数据收发既可分开也可同时进行,这就是所谓的全双工。串口没有主从机概念,并且没有专门的时钟信号 SCK,所以串口通信也属于异步传输。

说到异步传输,这就不得不提波特率(每秒钟传输bit数)的问题了,通信双方必须使用一致的波特率才能完成正确的数据传输。正常情况下,我们都是为两个串口设备事先约定好波特率,比如 MCU上位机通信,在 MCU 程序里按 115200 的波特率去初始化 UART 外设,然后上位机串口调试助手也设置 115200 波特率,双方再联合工作。

有时候,我们也希望能有一种灵活的波特率约定方式,比如建立通信前,在上位机串口调试助手里随意设置一种波特率,然后按这个波特率发送数据,MCU 端能自动识别出这个波特率,并用识别出来的波特率去初始化 UART 外设,然后再进行后续数据传输,这种方式就叫自动波特率识别。痞子衡今天要分享的就是在 MCU 里实现自动波特率识别的程序设计:

程序主页:https://github.com/JayHeng/cortex-m-apps/tree/master/components/autobaud

一、串口(UART)自动波特率识别程序设计

1.1 函数接口定义

首先是设计自动波特率识别程序头文件:autobaud.h ,这个头文件里直接定义如下 3 个接口函数原型。涵盖必备的初始化流程 init()、deinit(),以及最核心的波特率识别功能 get_rate()。

//!@brief初始化波特率识别

void

autobaud_init

(

void

);

//!@brief检测波特率识别是否已完成,并获取波特率值

bool

autobaud_get_rate

(

uint32_t

*rate);

//!@brief关闭波特率识别

void

autobaud_deinit

(

void

);

1.2 识别设计思想

关于识别,因为上位机数据是从 RXD 引脚过来的,所以在 MCU 里需要先将 RXD 引脚配置成普通数字输入 GPIO(这个引脚需要上拉,默认保持高电平),然后检测这个 GPIO 的电平跳变(一般用下降沿)并计时。

下图是典型的 UART 单字节传输时序,I/O 空闲状态是高电平,传输时总是由 1bit 低电平起始位开启,然后是从 LSB 到 MSB 的 8bit 数据位,校验位是可选项(我们暂不开启),最后由 1bit 高电平停止位结束,I/O 回归高电平空闲状态。

Note 1:检测下降沿跳变,是因为 I/O 空闲为高,起始位的存在保证了每 Byte 传输周期总是从下降沿开始。

Note 2:起始位和停止位两个 bit 的存在还兼有波特率容错的功能,通信双方波特率在 3% 的误差内数据传输均可以正常进行。

pYYBAGKxcyuAYRlbAACIRTDIzWc687.png

虽然我们不需要约定上位机波特率,但是要想实现波特率自动识别,上位机初始传输的数据却必须要事先约定好(可理解为接头暗号),这涉及到 MCU 里检测电平跳变次数与相应计时计算。MCU识别完成后将暗号发回给上位机确认。

痞子衡设计的接头暗号是 0x5A, 0xA6 两个字节,两字节暗号相比单字节暗号容错性更好一些(以防 I/O 上有干扰,导致误识别),根据指定的暗号和 UART 传输时序图,我们很容易得到如下常量定义:

enum

_autobaud_counts

{

//!0x5A字节对应的下降沿个数

kFirstByteRequiredFallingEdges=

4

,

//!0xA6字节对应的下降沿个数

kSecondByteRequiredFallingEdges=

3

,

//!0x5A字节(从起始位到停止位)第一个下降沿到最后一个下降沿之间的实际bit数

kNumberOfBitsForFirstByteMeasured=

8

,

//!0xA6字节(从起始位到停止位)第一个下降沿到最后一个下降沿之间的实际bit数

kNumberOfBitsForSecondByteMeasured=

7

,

//!两个下降沿之间允许的最大超时(us)

kMaximumTimeBetweenFallingEdges=

80000

,

//!对实际检测出的波特率值做对齐处理,以便于更好地配置UART模块

kAutobaudStepSize=

1200

};

上述常量定义里,kMaximumTimeBetweenFallingEdges 指定了两个下降沿之间允许的最大时间间隔,超过这个时间,自动波特率程序将丢掉前面统计的下降沿个数,重头开始识别,这个设计也是为了防止 I/O 上有电平干扰,导致误识别。

kAutobaudStepSize 常量是为了对检测出的波特率值做对齐处理,公式是 rounded = stepSize * (value/stepSize + 0.5),其中 value 是实际检测出的波特率值,rounded 是对齐后的波特率值,用对齐后的波特率值能更好地配置UART外设(这跟UART模块里波特率发生器SBR设计有关)。

最后就是 I/O 电平下降沿检测方法设计,这里既可以用软件查询(就是循环读取 I/O 输入电平,比较当前值与上一次值的差异),也可以使用GPIO模块自带的边沿中断功能。推荐使用后者,一方面计时更精确,另外也不用阻塞系统。检测到下降沿发生就调用一次如下 pin_transition_callback() 函数,在这个函数里统计跳变次数以及计时。

//!@brief管脚下降沿跳变回调函数

static

void

pin_transition_callback

(

void

);

1.3 主代码实现

根据上一小节描述的设计思想,我们很容易写出下面的主代码(autobaud_irq.c),代码里痞子衡都做了详细注释。有一点要提的是关于其中系统计时。

//!@brief使能GPIO管脚中断

extern

void

enable_autobaud_pin_irq

(

pin_irq_callback_t

func);

//!@brief关闭GPIO管脚中断

extern

void

disable_autobaud_pin_irq

(

void

);

//!< 已检测到的下降沿个数

static

uint32_t

s_transitionCount;

//!< 0x5A 字节检测期间内对应计数值

static

uint64_t

s_firstByteTotalTicks;

//!< 0xA6 字节检测期间内对应计数值

static

uint64_t

s_secondByteTotalTicks;

//!< 上一次下降沿发生时系统计数值

static

uint64_t

s_lastToggleTicks;

//!< 下降沿之间最大超时对应计数值

static

uint64_t

s_ticksBetweenFailure;

void

autobaud_init

(

void

)

{

s_transitionCount=

0

;

s_firstByteTotalTicks=

0

;

s_secondByteTotalTicks=

0

;

s_lastToggleTicks=

0

;

//计算出下降沿之间最大超时对应计数值

s_ticksBetweenFailure=microseconds_convert_to_ticks(kMaximumTimeBetweenFallingEdges);

//使能GPIO管脚中断,并注册中断处理回调函数

enable_autobaud_pin_irq(pin_transition_callback);

}

void

autobaud_deinit

(

void

)

{

//关闭GPIO管脚中断

disable_autobaud_pin_irq();

}

bool

autobaud_get_rate

(

uint32_t

*rate)

{

if

(s_transitionCount==(kFirstByteRequiredFallingEdges+kSecondByteRequiredFallingEdges))

{

//计算出实际检测到的波特率值

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

;

}

}

void

pin_transition_callback

(

void

)

{

//获取当前系统计数值

uint64_t

ticks=microseconds_get_ticks();

//计数这次检测到的下降沿

s_transitionCount++;

//如果本次下降沿与上次下降沿之间间隔过长,则从头开始检测

uint64_t

delta=ticks-s_lastToggleTicks;

if

(delta>s_ticksBetweenFailure)

{

s_transitionCount=

1

;

}

switch

(s_transitionCount)

{

case

1

:

//0x5A字节检测时间起点

s_firstByteTotalTicks=ticks;

break

;

case

kFirstByteRequiredFallingEdges:

//得到0x5A字节检测期间内对应计数值

s_firstByteTotalTicks=ticks-s_firstByteTotalTicks;

break

;

case

(kFirstByteRequiredFallingEdges+

1

):

//0xA6字节检测时间起点

s_secondByteTotalTicks=ticks;

break

;

case

(kFirstByteRequiredFallingEdges+kSecondByteRequiredFallingEdges):

//得到0xA6字节检测期间内对应计数值

s_secondByteTotalTicks=ticks-s_secondByteTotalTicks;

//关闭GPIO管脚中断

disable_autobaud_pin_irq();

break

;

}

//记录本次下降沿发生时系统计数值

s_lastToggleTicks=ticks;

}

二、串口(UART)自动波特率识别程序实现

前面讲的都是硬件无关设计,但最终还是要落实到具体 MCU 平台上的,其中 GPIO 中断部分是跟 MCU 紧相关的。我们以恩智浦 i.MXRT1011 为例来介绍硬件实现。

2.1 管脚中断方式实现(基于i.MXRT1011)

恩智浦 MIMXRT1010-EVK 有板载调试器 DAPLink,这个 DAPLink 中也集成了 USB 转串口的功能,对应的 UART 引脚是 IOMUXC_GPIO_09_LPUART1_RXD 和 IOMUXC_GPIO_10_LPUART1_TXD,我们就选用这个管脚 GPIO1[9] 做自动波特率检测,实现代码如下:

BSP程序:https://github.com/JayHeng/cortex-m-apps/tree/master/apps/autobaud_imxrt1011/bsp/src/pinmux_utility.c

typedef

void

(*

pin_irq_callback_t

)(

void

);

static

pin_irq_callback_t

s_pin_irq_func;

//!@briefUART引脚功能切换函数

void

uart_pinmux_config

(

bool

setGpio)

{

if

(setGpio)

{

IOMUXC_SetUartAutoBaudPinMode(IOMUXC_GPIO_09_GPIOMUX_IO09,GPIO1,

9

);

}

else

{

IOMUXC_SetUartPinMode(IOMUXC_GPIO_09_LPUART1_RXD);

IOMUXC_SetUartPinMode(IOMUXC_GPIO_10_LPUART1_TXD);

}

}

//!@brief使能GPIO管脚中断

void

enable_autobaud_pin_irq

(

pin_irq_callback_t

func)

{

s_pin_irq_func=func;

//开启GPIO1_9下降沿中断

GPIO_SetPinInterruptConfig(GPIO1,

9

,kGPIO_IntFallingEdge);

GPIO1->IMR|=(

1U

<< 

9

);

NVIC_SetPriority(GPIO1_Combined_0_15_IRQn,

1

);

NVIC_EnableIRQ(GPIO1_Combined_0_15_IRQn);

}

//!@briefGPIO中断处理函数

void

GPIO1_Combined_0_15_IRQHandler

(

void

)

{

uint32_t

interrupt_flag=(

1U

<< 

9

);

//仅当GPIO1_9中断发生时

if

((GPIO_GetPinsInterruptFlags(GPIO1)&interrupt_flag)&&s_pin_irq_func)

{

//执行一次回调函数

s_pin_irq_func();

GPIO_ClearPinsInterruptFlags(GPIO1,interrupt_flag);

}

}

2.2 在MIMXRT1010-EVK上实测

一切就绪,我们现在来实测一下,主函数流程很简单,测试结果也表明达到了预期效果,每次将 MCU 程序复位运行后,串口调试助手里可任意设置波特率。

int

main

(

void

)

{

//略去系统时钟配置...

//初始化定时器

microseconds_init();

//将GPIO1_9先配成输入GPIO

bool

setGpio=

true

;

uart_pinmux_config(setGpio);

//初始化波特率识别

autobaud_init();

//检测波特率识别是否已完成,并获取波特率值

uint32_t

baudrate;

while

(!autobaud_get_rate(&baudrate));

//关闭波特率识别

autobaud_deinit();

//配置UART1引脚

setGpio=

false

;

uart_pinmux_config(setGpio);

//初始化UART1外设

uint32_t

uartClkSrcFreq=BOARD_DebugConsoleSrcFreq();

DbgConsole_Init(

1

,baudrate,kSerialPort_Uart,uartClkSrcFreq);

PRINTF(

"Autobaudtestsuccess\r\n"

);

PRINTF(

"Detectedbaudrateis%d\r\n"

,baudrate);

while

(

1

);

}

pYYBAGKxc0aATmiiAAEJWjN4os0270.png

至此,嵌入式里串口(UART)自动波特率识别程序设计与实现痞子衡便介绍完毕了,

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    17083

    浏览量

    350741
  • 嵌入式
    +关注

    关注

    5081

    文章

    19077

    浏览量

    304360
  • uart
    +关注

    关注

    22

    文章

    1232

    浏览量

    101305
收藏 人收藏

    评论

    相关推荐

    什么是串口波特率?串口波特率的分类及应用详解

    什么是串口波特率串口波特率是指串口通信系统中传输数据的速率。在UART串口通信中,数据以位(bit)的形式传输,波特率是指每秒钟传输的位数。在串口通信中,
    的头像 发表于 08-08 10:09 5867次阅读
    什么是串口<b class='flag-5'>波特率</b>?串口<b class='flag-5'>波特率</b>的分类及应用详解

    芯海应用笔记:通用 MCU USART 自动波特率检测

    本文档介绍和说明芯海科技旗下 CS32 系列 MCU 的自动波特率检测功能,并为不具备硬件自动波特率检测的 MCU 产品提供替代软件方法 。*附件:应用笔记:芯海通用MCU USART自动波特率检测.pdf
    发表于 05-16 11:42

    自动波特率检测

    此代码尝试自动检测用户的波特率,并初始化8051的内置UART -。更具体而言,采取这些步骤:如果代码是正确的波特率组装的,跳到第5步。如果我们先前检​​测到
    发表于 07-20 16:21

    怎么使用UART设置中的自动波特率

    嗨,我试着用自动波特特性在PIC24F上设置UART。我缺少的是一种检测接收字符是否不同于“U”的方法,因为接收机FIFO没有更新。如果已经发送了另一个ASCII字符呢?我在汇编程序中感谢。
    发表于 09-09 14:38

    如何实现嵌入式里串口(UART)自动波特率识别程序设计

    如何实现嵌入式里串口(UART)自动波特率识别程序设计
    发表于 02-08 06:34

    在dsPIC30F器件上实现自动波特率检测 AN962中文资

    在dsPIC30F器件上实现自动波特率检测 AN962中文资料 目前所有dsPIC30F 器件都拥有一个具备自动波特率检测能力的UART 外设。UART 接收引脚(RX 引脚)上的信
    发表于 03-18 14:33 25次下载

    在dsPIC30F器件上实现自动波特率检测的详细中文资料概述

    目前所有 dsPIC30F 器件都拥有一个具备自动波特率检测能力的 UART 外设。UART 接收引脚(RX 引脚)上的信号能在内部传送至一个输入捕捉模块以获得输入信号边沿的时序。根据该时序,应用
    发表于 06-29 10:25 6次下载
    在dsPIC30F器件上实现<b class='flag-5'>自动波特率</b>检测的详细中文资料概述

    STM32的串口自动识别波特率

    STM32串口自动识别波特率
    的头像 发表于 03-04 09:53 9527次阅读

    在dsPIC30F器件上实现自动波特率检测

    目前所有 dsPIC30F 器件都拥有一个具备自动波特率检测能力的 UART 外设。UART 接收引脚(RX 引脚)上的信号能在内部传送至一个输入捕捉模块以获得输入信号边沿的时序。根据该时序,应用
    发表于 05-11 10:34 10次下载

    痞子衡嵌入式:嵌入式里串口(UART)自动波特率识别程序设计与实现(轮询)

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是嵌入式里串口(UART)自动波特率识别程序设计与实现。  本篇是 《串口(UAR
    发表于 12-05 12:51 10次下载
    痞子衡嵌入式:嵌入式里串口(<b class='flag-5'>UART</b>)<b class='flag-5'>自动波特率</b><b class='flag-5'>识别</b><b class='flag-5'>程序设计</b>与实现(轮询)

    SCI串口自动波特率介绍

    自中科昊芯推出专题讲解SCI串口通信以来,第一期主要讲解SCI串口FIFO通信原理,本期主要讲解SCI串口自动波特率,FIFO中断通信逻辑将在下期内容中讲解。
    的头像 发表于 04-26 09:26 3202次阅读
    SCI串口<b class='flag-5'>自动波特率</b>介绍

    STM32的USART自动波特率检测

    当在两个设备之间建立通信链路时,自动波特率检测十分有用,因为从设备能够检测到主控制器的波特率并进行相应的自我调整。这需要使用一种自动机制来确定波特率
    的头像 发表于 11-28 11:15 8582次阅读

    STM32 USART自动波特率检测

    电子发烧友网站提供《STM32 USART自动波特率检测.pdf》资料免费下载
    发表于 09-25 15:09 8次下载
    STM32 USART<b class='flag-5'>自动波特率</b>检测

    uart波特率和传输频率的关系 UART串口的常用波特率为多少

    UART(Universal Asynchronous Receiver/Transmitter)是一种广泛使用的异步串行通信技术,它允许两台设备之间进行双向数据传输。在UART通信中,波特率和传输
    的头像 发表于 10-06 16:12 2210次阅读
    <b class='flag-5'>uart</b><b class='flag-5'>波特率</b>和传输频率的关系 <b class='flag-5'>UART</b>串口的常用<b class='flag-5'>波特率</b>为多少

    MCU串口自动识别波特率原理分析

    现在的单片机资源越来越丰富了,其中我们常用的串口也是内部集成了多个,关键功能也越来越强了。 我们有些应用可能会用到串口自动识别波特率,今天就来讲讲MCU串口自动识别波特率底层的常见的原
    的头像 发表于 10-23 16:12 455次阅读
    MCU串口<b class='flag-5'>自动识别</b><b class='flag-5'>波特率</b>原理分析