新手自学嵌入式 | 学习日记:EPIT/GPT定时器、输入捕获与PWM、UART通信与Modbus协议

今天的学习围绕着嵌入式系统中极其重要的两个外设:定时器 和串口(UART)。从用定时器实现精准延时和按键消抖,到利用输入捕获和输出比较控制电机,再到多机通信的Modbus协议与硬件流控,信息量很大,梳理一下今天的核心知识点。


一、 定时器双雄:EPIT与GPT

在IMX6ULL中,常用的定时器有EPIT和GPT,它们的计数机制和应用场景大不相同。

1. 什么是EPIT?

EPIT (Enhanced Periodic Interrupt Timer) 是增强型周期中断定时器。

  • 特性 :它是一个 32位自减型 定时器(计数器从设定值递减到0)。默认时钟源通常是 24MHz(也有32.768kHz的RTC提供低速时钟)。

  • 工作模式:

    • Free-Running Mode (自由运行模式) :计数器从 0xFFFFFFFF 开始往下减,减到0后自动重装载 0xFFFFFFFF,循环往复,永不停止。图示中的抛物线很好地展示了这种溢出(0xFFFFFFFF -> 0x00000000)。

    • Set and Forget Mode (设置-遗忘模式):减到0后触发中断,或者停止,或者重新加载初始设定值,只需配置一次即可。

  • 应用:常用来提供系统的系统滴答定时(如Linux内核的时钟节拍)、精准的延时函数等。

2. 什么是GPT?

GPT (General Purpose Timer) 是通用定时器。

  • 特性 :它是一个 32位自增型 定时器(计数器从0向上递增,直到 0xFFFFFFFF)。

  • 应用 :除了基础的定时中断功能,GPT最强大的地方在于它拥有输入捕获 和输出比较通道。

3. 什么是输入捕获和输出比较?(Q4解答)
  • 输入捕获 (Input Capture) :从某个引脚捕获两次电平跳变(如上升沿、下降沿)之间的时间间隔。通过捕获定时器当前的计数值,可以算出脉冲宽度、频率。应用场景:检测电机转速(图示中:4个脉冲、1000个脉冲、250圈,通过计算脉冲频率得出转速)。

  • 输出比较 (Output Compare) :定时器的计数值与设定的比较值相等时,自动翻转引脚电平。应用场景:输出指定频率和占空比的方波(PWM),用于控制电机转速、调节LED亮度等。

⏱️ 避坑小记(时间溢出问题) :

在计算两次捕获的时间差时,如果遇到自由运行模式溢出(0xFFFFFFFF -> 0x00000000),直接 new - old 会出错。必须判断:如果 new < old,说明发生了溢出,时间差 = (0xFFFFFFFF - old) + new。


二、 按键消抖与延时函数

  • 硬件消抖 :并联一个电容。利用电容阻断直流、导通交流的特性,将按键按下瞬间产生的机械抖动毛刺滤除。

  • 软件消抖 :使用定时器延时。图片中的 void delayus(unsigned int n) 内部空循环 while(1){} 虽然能实现延时,但在现代处理器中极其不精准,且浪费CPU。实际开发中应使用EPIT配置精准的微秒级延时。


三、 UART通信与Modbus协议

1. 什么是UART?

UART (Universal Asynchronous Receiver/Transmitter) 是通用异步收发传输器。它是一种异步 (没有时钟线,依靠约定的波特率同步)、全双工(发送和接收独立)的串行通信接口。

  • 常用参数 :图示中的 115200 8 N 1 即:波特率115200,8个数据位,无校验(None),1个停止位。
2. Modbus 通信拓扑

图中的"单主机多从机拓扑 Modbus"是工业控制最常用的协议。

  • 架构:一个 Master (IMX6ULL) 连接多个 Slave (IMX6ULL)。主节点轮询发送,从节点应答。

  • 抗干扰与校验 :在强干扰、强电场环境下,数据极易出错。Modbus常用累加和校验、奇偶校验、CRC校验来保证数据完整性。

  • 自动检测从机地址:每个从机有唯一ID,主节点发报文带上ID,只有匹配的从机才会响应。

3. 流控 (Flow Control)

防止接收方缓冲区溢出导致丢包。

  • RTS_B (Request To Send):请求发送。告诉对方,"我这边的接收缓冲区准备好了,你可以发数据了"。

  • CTS_B (Clear To Send):清除发送(允许发送)。告诉对方,"我这边的接收缓冲区满了,暂停发送数据"。


四、 UART 代码实现(核心框架)

结合IMX6ULL的寄存器思想,这里整理一份UART初始化和收发代码的逻辑框架(基于裸机寄存器操作):

c

复制代码
// 1. UART 初始化函数
void uart_init(void)
{
    // (1) 配置UART的时钟源 (CCM模块)
    // 比如:设置UART1时钟源为PLL3/6 或者 80MHz

    // (2) 配置引脚复用 (IOMUXC)
    // 将对应的引脚配置为 UART1_TX 和 UART1_RX 功能
    // 配置电气属性(如驱动能力、上下拉等)

    // (3) 配置UART控制器寄存器
    // 1. 关闭UART:UART1->UCR1 &= ~(1 << 0);
    // 2. 配置波特率:设置 UART1->UFCR (参考时钟分频), UART1->UBIR, UART1->UBMR
    // 3. 配置数据格式:UART1->UCR2 (8位数据,无校验,1停止位)
    // 4. 使能UART:UART1->UCR1 |= (1 << 0);
}

// 2. 发送一个字符
void putc(unsigned char c)
{
    // 等待发送缓冲区为空
    while (!(UART1->USR2 & (1 << 3))); // USR2_TXDC位
    // 将数据写入发送寄存器
    UART1->UTXD = c;
}

// 3. 发送字符串
void puts(char *str)
{
    while (*str)
    {
        putc(*str++);
    }
}

// 4. 接收一个字符 (轮询方式)
unsigned char getc(void)
{
    // 等待接收数据就绪
    while (!(UART1->USR2 & (1 << 0))); // USR2_RDR位
    // 读取接收寄存器
    return (unsigned char)(UART1->URXD & 0xFF);
}

今日总结与反思

今天的内容非常硬核。

  1. EPIT 和GPT各有千秋,理解了定时器的本质就是"数数",输入捕获算频率,输出比较发PWM,电机的测速和控制就迎刃而解了。

  2. UART通信看似简单(两根线TX/RX),但背后的Modbus多机协议和硬件流控(RTS/CTS)才是工业级应用的精髓。

  3. 代码实现上,寄存器操作虽然繁琐,但把底层逻辑理清楚后,再去看库函数或Linux驱动,就能一眼看穿其本质。

相关推荐
云水初1 小时前
【项目篇】基于LanGraph的自动处理客服邮件的 Agent汇总
开发语言·笔记·python·学习·fastapi·langgraph
笨笨饿1 小时前
#142_VS Code.map文件插件的用法
git·python·stm32·单片机·嵌入式硬件·物联网·vim
三佛科技-187366133971 小时前
HN62512是什么芯片?HN62512多规格5V/12V500mA,24V350mA芯片解析
单片机·嵌入式硬件
早八晚十啊2 小时前
双MCU架构:基于视觉反馈的简易自行瞄准装置(激光云台控制)
单片机·嵌入式硬件
铅笔小新z2 小时前
【stm32】RTC(实时时钟)
stm32·单片机·实时音视频
笨鸟先飞的橘猫2 小时前
skynet热更新实现问题与排错记录
学习·lua·skynet
andrsted2 小时前
练题簿在线免费刷题:将资料、Word 和 PDF导入小程序练习
学习·微信小程序·小程序·pdf·word·学习方法
新手小新3 小时前
通信工程师学习笔记5-通信系统
笔记·学习
我命由我123453 小时前
刚性防水与柔性防水
学习·职场和发展·产品运营·求职招聘·职场发展·学习方法·产品