STM8单片机实现IRDA红外通信完整源码解析
IRDA(Infrared Data Association)是一种基于红外线的短距离无线通信协议,广泛应用于嵌入式系统中的设备间数据交互。其工作波长通常位于可见光与远红外之间,采用半双工通信方式,适用于低功耗、低成本、短距离(通常为1米以内)的通信场景。相较于蓝牙、Wi-Fi等无线技术,IRDA具备功耗低、无频段干扰、硬件成本低等优势。尤其在家电控制、智能卡、工业传感器等场景中,IRDA因其无需
简介:IRDA是一种红外数据传输标准,广泛用于短距离、低速率的数据交换。本文基于STM8单片机实现IRDA红外通信的完整源码,涵盖中断方式下的自发自收功能。通过深入讲解STM8的中断系统、IRDA硬件模块配置、波特率设置、中断服务程序编写、数据帧格式及低功耗管理等内容,帮助开发者掌握在STM8平台上构建稳定红外通信系统的方法。配套测试工程“TFBS4711的IrDA测试工程源码”可用于验证通信功能的正确性,是学习嵌入式红外通信的理想实践资料。 
1. IRDA红外通信协议简介
IRDA(Infrared Data Association)是一种基于红外线的短距离无线通信协议,广泛应用于嵌入式系统中的设备间数据交互。其工作波长通常位于可见光与远红外之间,采用半双工通信方式,适用于低功耗、低成本、短距离(通常为1米以内)的通信场景。
相较于蓝牙、Wi-Fi等无线技术,IRDA具备功耗低、无频段干扰、硬件成本低等优势。尤其在家电控制、智能卡、工业传感器等场景中,IRDA因其无需射频认证,成为一种理想的通信方案。同时,IRDA协议支持多种数据传输速率,从9600bps到高达115.2kbps的SIR(Serial Infrared)模式,使其具备良好的灵活性。
在嵌入式开发中,IRDA模块常集成于MCU(如STM8)的USART外设中,通过特定寄存器配置即可实现红外通信功能。
2. STM8单片机架构与外设概述
在嵌入式系统设计中,微控制器的架构和外设配置直接影响系统的性能、功耗与开发效率。STM8系列单片机以其高性能、低成本和丰富的外设资源,广泛应用于工业控制、消费电子、智能传感器等领域。本章将深入探讨STM8的CPU架构、存储器映射机制,以及关键外设的功能特性,尤其是与红外通信(IRDA)相关的模块集成,为后续章节中实现IRDA通信打下坚实的基础。
2.1 STM8核心架构简介
STM8是意法半导体推出的一款8位增强型RISC架构MCU,其核心设计兼顾了高性能与低功耗,适用于多种嵌入式应用场景。理解其核心架构有助于开发者更好地进行系统级优化与资源管理。
2.1.1 STM8 CPU结构与指令集特性
STM8 CPU采用增强型哈佛架构,指令和数据分别通过独立的总线访问,提升了执行效率。其指令集包含160多条指令,支持直接寻址、间接寻址、寄存器寻址等多种方式。
- 核心寄存器 :包括8位A累加器、X和Y 16位索引寄存器、堆栈指针(SP)、程序计数器(PC)和状态寄存器(CCR)。
- 指令集特点 :
- 支持单周期指令,提高执行速度;
- 指令长度为1~5字节不等;
- 支持位操作,便于控制GPIO等外设;
- 内置乘法指令,提升数学运算效率。
以下是一段简单的汇编代码示例,展示了STM8指令的基本结构:
LD A, #0x55 ; 将立即数0x55加载到累加器A
LD (0x00), A ; 将A的值写入地址0x00的内存单元
逐行分析 :
LD A, #0x55:使用立即寻址方式,将0x55赋值给累加器A;LD (0x00), A:将累加器A的内容写入内存地址0x00处。
这种指令结构在嵌入式系统中常用于初始化寄存器或内存操作,是底层驱动开发的基础。
2.1.2 存储器映射与堆栈机制
STM8的存储器采用统一编址方式,程序存储器(Flash)、数据存储器(RAM)和外设寄存器都映射在同一个地址空间中,地址范围为0x0000 ~ 0xFFFF。
- 程序存储器 :通常位于高地址区域,用于存放程序代码;
- 数据存储器 :位于低地址段,存放变量和堆栈;
- 外设寄存器 :位于特定地址段,通过内存映射方式进行访问。
堆栈机制采用向下增长方式,堆栈指针(SP)默认指向0xFF。堆栈用于函数调用、中断处理和局部变量存储。
下面是一个堆栈操作的示例:
void func() {
__asm__("PUSH A"); // 将累加器A压入堆栈
// ... 函数体
__asm__("POP A"); // 将堆栈顶部数据弹出到A
}
逐行分析 :
PUSH A:将A寄存器内容压入堆栈,SP减1;POP A:将SP指向的地址内容弹出到A,SP加1。
该机制在中断处理和子程序调用中至关重要,确保上下文的正确保存与恢复。
2.2 STM8常用外设功能概述
STM8集成了丰富的外设模块,包括定时器、看门狗、USART、SPI、ADC等,这些外设极大增强了系统的功能扩展性与灵活性。
2.2.1 定时器与看门狗模块
STM8的定时器模块包括基本定时器(TIM6)、通用定时器(TIM2/TIM3)和高级定时器(TIM1),支持PWM输出、输入捕获、输出比较等多种功能。
看门狗模块 (IWDG)用于监控系统运行状态,防止程序跑飞:
IWDG_Enable(); // 启动看门狗
IWDG_WriteAccessCmd(ENABLE); // 允许写入看门狗寄存器
IWDG_SetPrescaler(IWDG_Prescaler_256); // 设置预分频系数
IWDG_SetReload(0xFFF); // 设置重载值
IWDG_ReloadCounter(); // 重新加载计数器
参数说明 :
- IWDG_Prescaler_256 :将系统时钟分频256;
- 0xFFF :看门狗计数器的最大值;
- 若未在设定时间内调用 IWDG_ReloadCounter() ,系统将自动复位。
2.2.2 USART与IRDA模块的功能集成
STM8的USART模块支持异步串行通信,并集成了IRDA红外通信接口,支持半双工模式。IRDA模块通过调制3/16位周期的脉冲实现红外数据传输。
USART与IRDA共用寄存器资源 ,通过配置特定寄存器可切换模式。例如:
USART_Init(9600, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No, USART_Mode_Tx | USART_Mode_Rx);
USART_IRDACmd(ENABLE); // 启用IRDA模式
USART_HalfDuplexCmd(ENABLE); // 启用半双工模式
功能说明 :
- USART_IRDACmd(ENABLE) :启用红外通信模式;
- USART_HalfDuplexCmd(ENABLE) :设置为半双工通信,即收发不能同时进行;
- USART_Init() :初始化波特率、数据位、停止位等参数。
下表列出了STM8中USART与IRDA相关寄存器的部分配置位:
| 寄存器名 | 位字段 | 功能说明 |
|---|---|---|
| USART_CR1 | M | 数据位长度(8或9位) |
| USART_CR2 | REN / TEN | 接收使能 / 发送使能 |
| USART_CR3 | HDSEL | 半双工使能 |
| USART_CR4 | IREN | IRDA使能 |
2.3 IRDA通信在STM8中的实现基础
IRDA通信在STM8中主要依赖于USART模块的扩展功能,通过配置特定寄存器实现红外数据的发送与接收。
2.3.1 IRDA模块的硬件支持能力
STM8的IRDA模块具备以下硬件特性:
- 调制频率支持 :支持3/16位周期的脉冲调制;
- 传输速率 :支持SIR(标准红外速率,最高115.2kbps);
- 物理接口 :通过专用的IRDA引脚(如PA2/USART1_TX)输出红外信号;
- 接收解调 :内置解调电路,可识别红外信号中的数据位。
典型应用场景 :遥控器通信、嵌入式设备间短距离数据交换、智能家电控制等。
2.3.2 与红外通信相关的寄存器资源
IRDA通信主要涉及以下寄存器:
- USART_CR1 :控制数据位长度、校验方式等;
- USART_CR3 :HDSEL位控制半双工模式;
- USART_CR4 :IREN位启用IRDA模式;
- USART_BRR :设置波特率分频值;
- USART_DR :数据寄存器,用于发送和接收数据。
示例:IRDA初始化配置代码
// 设置波特率为115200,8位数据位,无校验,1位停止位
USART_Init(115200, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No, USART_Mode_Tx);
USART_CR4 |= USART_CR4_IREN; // 启用IRDA模式
USART_CR3 |= USART_CR3_HDSEL; // 启用半双工模式
USART_Cmd(ENABLE); // 启动USART模块
逐行分析 :
USART_Init(...):设置通信参数;USART_CR4 |= USART_CR4_IREN:启用IRDA模式;USART_CR3 |= USART_CR3_HDSEL:设置为半双工模式;USART_Cmd(ENABLE):启动USART模块,使配置生效。
IRDA通信流程图 (使用Mermaid语法):
graph TD
A[开始初始化] --> B[配置系统时钟]
B --> C[配置GPIO为IRDA引脚]
C --> D[设置USART通信参数]
D --> E[启用IRDA模式]
E --> F[启用半双工模式]
F --> G{是否启用中断?}
G -->|是| H[配置中断与缓冲区]
G -->|否| I[轮询方式发送/接收数据]
H --> J[进入主循环]
I --> J
该流程图清晰地展示了从初始化到数据收发的完整过程,为后续中断方式的实现提供了参考。
本章详细分析了STM8单片机的核心架构、存储器机制、常用外设模块及其与IRDA通信相关的硬件支持。通过对CPU结构、指令集、外设寄存器的深入解析,读者可理解STM8在嵌入式系统中的设计逻辑与资源配置方法,为后续实现IRDA红外通信功能奠定坚实基础。下一章节将深入探讨STM8的中断系统原理与配置,进一步提升系统响应效率与通信稳定性。
3. STM8中断系统原理与配置
中断系统是嵌入式系统中最核心的机制之一,尤其在STM8这样的8位微控制器中,中断机制对于实时性要求较高的应用至关重要。STM8的中断控制系统设计精巧,支持多个中断源、优先级管理以及嵌套中断处理。在IRDA红外通信中,中断常用于处理数据接收与发送,提升系统响应速度与效率。
本章将从中断机制的基础原理出发,逐步深入到STM8中断控制器的配置方法,并结合IRDA通信模块的应用,展示中断在实际数据收发中的关键作用。
3.1 中断机制基础
3.1.1 中断类型与优先级划分
STM8的中断系统支持多个中断源,主要包括外部中断(EXTI)、定时器中断(TIM)、串行通信中断(USART/IRDA)、ADC中断等。每个中断源都有对应的中断向量地址,并通过中断优先级寄存器进行优先级划分。
STM8支持两个中断优先级级别:主优先级(High Priority)和次优先级(Low Priority)。主优先级中断可以打断次优先级中断的执行,但次优先级中断不能打断主优先级中断。这种结构允许开发者在系统设计中合理分配中断优先级,确保关键任务能够及时响应。
以下是一个STM8中断优先级的典型配置表:
| 中断源名称 | 中断向量地址 | 默认优先级 | 说明 |
|---|---|---|---|
| EXTI0_IRQHandler | 0x8004 | 次优先级 | 外部引脚0中断 |
| TIM1_UP_IRQHandler | 0x8008 | 主优先级 | 定时器1更新中断 |
| USART1_RX_IRQHandler | 0x8014 | 主优先级 | USART1接收中断,常用于IRDA通信 |
| ADC1_EOC_IRQHandler | 0x8018 | 次优先级 | ADC转换完成中断 |
中断优先级设置逻辑说明 :
- 主优先级中断(High Priority):由ITC_SPR1寄存器控制。
- 次优先级中断(Low Priority):由ITC_SPR2寄存器控制。
- 开发者可以通过配置这些寄存器来动态调整中断优先级。
3.1.2 中断向量表与中断响应流程
STM8的中断向量表位于程序存储器的高端地址区域(0x8000 - 0x80FF),每个中断源都对应一个固定的中断向量地址,中断发生时,CPU会自动跳转到对应的地址执行中断服务函数(ISR)。
中断响应流程如下:
graph TD
A[中断请求发生] --> B{中断是否被屏蔽?}
B -- 是 --> C[继续执行主程序]
B -- 否 --> D[保存当前上下文]
D --> E[跳转到中断向量地址]
E --> F[执行中断服务函数ISR]
F --> G[清除中断标志位]
G --> H[恢复上下文]
H --> I[返回主程序继续执行]
在STM8中,中断标志位的清除通常由软件手动完成,例如在接收中断中需要读取数据寄存器来清除标志位。否则中断可能会重复触发,导致系统异常。
3.2 STM8中断控制器配置
3.2.1 中断使能与屏蔽控制
STM8的中断控制器允许开发者通过软件控制中断的使能与屏蔽。全局中断使能由 RIM 指令设置,全局中断屏蔽由 SIM 指令设置。此外,每个中断源都有独立的使能位,通常位于中断控制寄存器中。
以下是一个中断使能的代码示例(使用STM8S标准外设库):
#include "stm8s.h"
void EXTI0_IRQHandler(void) __interrupt(2) {
if (EXTI->SR1 & EXTI_SR1_SR0) { // 判断是否是EXTI0中断触发
// 执行中断处理逻辑
GPIO_WriteReverse(GPIOB, GPIO_PIN_5); // 翻转LED状态
EXTI->SR1 &= ~EXTI_SR1_SR0; // 清除中断标志位
}
}
void EXTI0_Init(void) {
// 1. 配置GPIO为输入
GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST); // 假设LED连接到PB5
GPIO_Init(GPIOC, GPIO_PIN_0, GPIO_MODE_IN_FL_NO_IT); // PC0为输入,不触发中断
// 2. 配置EXTI0
EXTI->CR1 |= EXTI_CR1_Px0_PC; // 设置PC0为EXTI0源
EXTI->CR2 |= EXTI_CR2_IGx0 | EXTI_CR2_IGx1; // 上升沿触发
// 3. 使能EXTI0中断
EXTI->IMR |= EXTI_IMR_IM0; // 使能EXTI0中断
// 4. 设置中断优先级为主优先级
ITC->ISPR1 |= ITC_ISPR1_VECT3SPR; // 设置中断向量3(EXTI0)为高优先级
// 5. 全局中断使能
enableInterrupts();
}
代码逻辑逐行分析 :
- 第5行 :定义
EXTI0_IRQHandler中断服务函数,并使用__interrupt(2)指定其为中断向量2(EXTI0对应的中断号)。 - 第6行 :检查中断标志位是否置位。
- 第9行 :翻转LED状态,表示中断处理发生。
- 第11行 :清除中断标志位,防止重复触发。
- 第16行 :配置GPIO为输出,用于LED状态指示。
- 第17行 :配置PC0为浮空输入,用于外部中断源。
- 第20行 :设置EXTI0的输入源为PC0。
- 第21行 :配置为上升沿触发中断。
- 第24行 :使能EXTI0中断。
- 第27行 :将EXTI0设置为主优先级。
- 第30行 :调用
enableInterrupts()启用全局中断。
3.2.2 中断优先级设置方法
STM8的中断优先级分为主优先级和次优先级,开发者可以通过配置中断优先级寄存器( ITC_SPR1 和 ITC_SPR2 )来调整每个中断的优先级。主优先级中断可以打断次优先级中断的执行,而次优先级中断则不具备此能力。
以下是设置中断优先级的代码片段:
// 设置USART1接收中断为高优先级
ITC->ISPR1 |= ITC_ISPR1_VECT17SPR; // USART1_RX对应的中断向量为17
说明 :
-ITC_ISPR1_VECT17SPR位表示向量17的中断是否为主优先级。
- 写入该位为1表示设置为主优先级,否则为次优先级。
通过合理配置中断优先级,可以实现对关键任务(如IRDA通信中的数据接收)的快速响应,提高系统实时性。
3.3 IRDA通信中的中断应用
3.3.1 接收与发送中断的触发机制
在STM8的IRDA通信中,使用USART模块的红外模式(IRDA Mode)进行数据收发。为了提高效率,通常采用中断方式处理数据接收和发送。
- 接收中断 :当接收缓冲区(USART_DR)有数据可读时,会触发
RXNE(Receive Not Empty)中断。 - 发送中断 :当发送缓冲区为空时,会触发
TXE(Transmit Empty)中断。
以下是接收中断的示例代码:
void USART1_RX_IRQHandler(void) __interrupt(17) {
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR; // 读取数据,自动清除RXNE标志
// 数据处理逻辑
}
}
参数说明 :
USART1->SR:状态寄存器,用于检测接收中断标志。USART_SR_RXNE:接收缓冲区非空标志。USART1->DR:数据寄存器,读取该寄存器可清除中断标志。
3.3.2 中断处理与数据缓冲策略
在IRDA通信中,为了防止数据丢失,通常使用环形缓冲区(Ring Buffer)来进行数据缓存。中断服务函数将接收到的数据写入缓冲区,主程序则从缓冲区中读取并处理。
以下是一个简单的环形缓冲区结构定义:
#define BUFFER_SIZE 128
typedef struct {
uint8_t buffer[BUFFER_SIZE];
uint8_t head;
uint8_t tail;
} RingBuffer;
RingBuffer rx_buffer;
void USART1_RX_IRQHandler(void) __interrupt(17) {
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
uint8_t next = (rx_buffer.head + 1) % BUFFER_SIZE;
if (next != rx_buffer.tail) { // 如果缓冲区未满
rx_buffer.buffer[rx_buffer.head] = data;
rx_buffer.head = next;
}
}
}
代码逻辑分析 :
- 第1~6行 :定义环形缓冲区结构,包含缓冲数组、读指针(head)和写指针(tail)。
- 第9行 :定义接收中断函数。
- 第10~11行 :读取数据并清除标志。
- 第13~16行 :判断缓冲区是否满,未满则将数据写入缓冲区并移动头指针。
该策略确保在高频率接收数据时,不会丢失任何数据,提高了系统的稳定性与可靠性。
通过本章的学习,我们深入了解了STM8的中断机制、配置方法以及其在IRDA通信中的实际应用。下一章将继续深入IRDA模块的寄存器配置,帮助开发者掌握如何精确控制红外通信行为。
4. IRDA硬件模块寄存器配置
在STM8系列单片机中,IRDA模块的硬件功能集成于USART模块中,通过特定的寄存器配置,可以实现红外通信的半双工模式。本章将围绕IRDA模块的寄存器配置展开深入讲解,包括控制寄存器、状态寄存器和数据寄存器的功能详解,以及通信模式设置和实际配置流程。通过本章的学习,读者可以掌握如何正确初始化IRDA模块,为后续的红外通信程序开发打下坚实基础。
4.1 IRDA模块相关寄存器介绍
IRDA模块的运行依赖于多个关键寄存器的配置,包括控制寄存器(CR)、状态寄存器(SR)和数据寄存器(DR)。这些寄存器分别用于控制模块状态、监控通信状态以及存储待发送或已接收的数据。
4.1.1 控制寄存器(CR)功能详解
在STM8的USART模块中,控制寄存器分为 USART_CR1、USART_CR2、USART_CR3 和 USART_CR4 四个部分。IRDA模式主要涉及以下位段的配置:
- USART_CR1.IREN :IRDA模式使能位
0:关闭IRDA模式(默认)1:启用IRDA模式- USART_CR1.EDIT :IRDA半双工双向数据流控制
0:全双工模式1:半双工模式(单线通信)- USART_CR1.ELSEL :IRDA低功耗模式选择
0:正常模式1:低功耗模式(低电平有效)
示例:启用IRDA半双工模式
USART1->CR1 |= USART_CR1_IREN | USART_CR1_EDIT; // 启用IRDA模式并设置为半双工
逐行分析:
USART1->CR1 |= ...:通过按位或操作设置特定控制位,避免影响其他配置。USART_CR1_IREN:启用IRDA通信功能。USART_CR1_EDIT:启用半双工模式,使IRDA模块只使用一根线进行数据收发。
4.1.2 状态寄存器(SR)与数据寄存器(DR)
状态寄存器(USART_SR) 用于反映当前通信状态,常见的关键位段如下:
- TXE (Transmit Data Register Empty):发送寄存器空标志
- RXNE (Read Data Register Not Empty):接收寄存器非空标志
- TC (Transmission Complete):发送完成标志
数据寄存器(USART_DR) 用于读写数据。发送数据时,写入USART_DR;接收数据时,从USART_DR读取。
示例:检查发送寄存器是否为空
while (!(USART1->SR & USART_SR_TXE)); // 等待发送寄存器为空
USART1->DR = 'A'; // 发送字符'A'
逐行分析:
while (!(USART1->SR & USART_SR_TXE)):循环等待,直到发送缓冲区为空。USART1->DR = 'A':将字符’A’写入数据寄存器,启动发送过程。
4.2 IRDA通信模式配置
IRDA通信模式的配置主要包括启用IRDA模块、设置半双工通信、配置数据格式和设定传输速率。这些配置都通过USART模块的寄存器实现。
4.2.1 启用IRDA模式与设置半双工通信
启用IRDA模式并切换为半双工通信是基本配置步骤之一。半双工通信适用于设备之间交替发送与接收的场景,非常适合红外遥控器等应用场景。
配置流程:
- 使能IRDA模式: 设置
USART_CR1.IREN = 1 - 设置半双工模式: 设置
USART_CR1.EDIT = 1 - 关闭其他模式干扰: 清除
USART_CR1.REN和USART_CR1.TEN,避免与IRDA模式冲突
USART1->CR1 &= ~USART_CR1_REN; // 关闭接收器
USART1->CR1 &= ~USART_CR1_TEN; // 关闭发送器
USART1->CR1 |= USART_CR1_IREN | USART_CR1_EDIT; // 启用IRDA半双工模式
逐行分析:
USART1->CR1 &= ~USART_CR1_REN:关闭接收功能,避免与IRDA模式冲突。USART1->CR1 &= ~USART_CR1_TEN:关闭发送功能,避免冲突。USART1->CR1 |= ...:启用IRDA并设置为半双工模式。
4.2.2 数据格式与传输速率的寄存器设定
IRDA通信的数据格式通常为8位数据位、1位停止位、无校验位(8N1),与标准的UART格式一致。波特率设置通过USART_BRR寄存器完成。
示例:设置8N1数据格式与波特率为9600
USART1->CR1 &= ~USART_CR1_M; // 数据位长度为8位(默认)
USART1->CR1 &= ~USART_CR1_PCE; // 禁用校验位
USART1->CR2 &= ~USART_CR2_STOP; // 停止位为1位(默认)
USART1->BRR = 0x0683; // 假设系统时钟为16MHz,波特率为9600
逐行分析:
USART_CR1_M:控制数据位长度,0表示8位数据位。USART_CR1_PCE:校验位使能位,0表示无校验。USART_CR2_STOP:停止位长度设置,00表示1位停止位。USART1->BRR = 0x0683:根据系统时钟和波特率计算得到的波特率寄存器值。
波特率计算公式(异步模式):
$$
\text{Baud Rate} = \frac{f_{\text{CLK}}}{16 \times \text{USARTDIV}}
$$
其中USARTDIV由 BRR 寄存器决定。
4.3 寄存器配置实例
为了更好地理解IRDA模块的寄存器配置流程,下面提供一个完整的初始化函数示例,涵盖GPIO配置、IRDA模式启用、波特率设置等关键步骤。
4.3.1 初始化IRDA模块的寄存器流程
void IRDA_Init(void) {
// 1. 使能GPIO和USART时钟
CLK->PCKENR1 |= (1 << 3); // 使能GPIOB时钟
CLK->PCKENR1 |= (1 << 4); // 使能USART1时钟
// 2. 配置GPIO为IRDA输出
GPIOB->DDR_DDR4 = 1; // 设置PB4为输出
GPIOB->CR1_C14 = 1; // 推挽输出模式
GPIOB->CR2_C24 = 1; // 高速输出
// 3. 设置波特率
USART1->BRR = 0x0683; // 波特率9600
// 4. 配置数据格式为8N1
USART1->CR1 &= ~USART_CR1_M; // 8位数据
USART1->CR1 &= ~USART_CR1_PCE; // 无校验
USART1->CR2 &= ~USART_CR2_STOP; // 1位停止位
// 5. 启用IRDA半双工模式
USART1->CR1 &= ~USART_CR1_REN;
USART1->CR1 &= ~USART_CR1_TEN;
USART1->CR1 |= USART_CR1_IREN | USART_CR1_EDIT;
// 6. 使能USART
USART1->CR1 |= USART_CR1_USARTD;
}
逐行分析:
CLK->PCKENR1 |= ...:开启GPIOB和USART1的时钟,确保外设可被访问。GPIOB->DDR_DDR4 = 1:设置PB4为输出,用于IRDA信号输出。GPIOB->CR1_C14 = 1和GPIOB->CR2_C24 = 1:配置为推挽输出和高速模式,适合红外信号驱动。USART1->BRR = 0x0683:设定波特率为9600。USART1->CR1 &= ~USART_CR1_M:设置数据位为8位。USART1->CR1 &= ~USART_CR1_PCE:关闭校验位。USART1->CR2 &= ~USART_CR2_STOP:设置停止位为1位。USART1->CR1 |= USART_CR1_IREN | USART_CR1_EDIT:启用IRDA并设置为半双工模式。USART1->CR1 |= USART_CR1_USARTD:启用USART模块。
4.3.2 常见配置错误与排查方法
在实际开发过程中,IRDA模块的寄存器配置容易出现以下常见错误:
| 错误类型 | 表现 | 原因 | 解决方案 |
|---|---|---|---|
| 波特率错误 | 数据接收乱码 | BRR值计算错误或系统时钟未配置正确 | 使用波特率计算工具重新计算,确认系统时钟 |
| IRDA未启用 | 无红外信号输出 | 未设置IREN位 | 检查USART_CR1.IREN是否置1 |
| 半双工通信失败 | 收发冲突或无响应 | EDIT位未设置 | 确保USART_CR1.EDIT被启用 |
| 数据格式不匹配 | 接收端解析失败 | 数据位、停止位或校验位设置不一致 | 统一双方数据格式为8N1 |
| GPIO配置错误 | 信号无法驱动红外LED | GPIO未设置为推挽输出或输出模式 | 检查GPIO配置为推挽输出并启用输出功能 |
流程图:IRDA配置流程图(mermaid格式)
graph TD
A[开始] --> B{系统时钟配置正确?}
B -- 是 --> C[配置GPIO为推挽输出]
C --> D[设置波特率寄存器BRR]
D --> E[设置数据格式为8N1]
E --> F[启用IRDA模式和半双工]
F --> G[使能USART模块]
G --> H[结束]
B -- 否 --> I[重新配置系统时钟]
I --> B
通过以上流程图可以清晰地看到IRDA模块配置的基本步骤及其依赖关系,有助于开发者排查和优化配置逻辑。
综上所述,IRDA模块的寄存器配置是实现红外通信的关键环节。本章从寄存器的功能入手,详细讲解了控制寄存器、状态寄存器和数据寄存器的作用,并结合实际代码示例演示了IRDA模式的启用、半双工设置、数据格式与波特率的配置流程。同时,通过配置实例与常见错误排查方法,帮助读者在实际开发中规避配置陷阱,提升开发效率与稳定性。下一章将围绕波特率发生器的实现与设置展开深入探讨。
5. 波特率发生器实现与设置
在红外通信中,波特率(Baud Rate)是决定数据传输速率的关键参数。波特率发生器的实现直接影响通信的稳定性和准确性,特别是在STM8单片机的IRDA模块中,如何基于系统时钟配置波特率寄存器(如USART_BRR)是实现高效红外通信的核心环节。本章将深入探讨波特率生成的数学原理、寄存器配置方法,以及不同波特率下的通信适配策略。
5.1 波特率计算原理
波特率指的是每秒传输的数据位数(bit/s),它决定了通信的速度。在STM8的IRDA模块中,波特率由系统时钟(fCLK)和波特率寄存器(USART_BRR)共同决定。
5.1.1 基于系统时钟的波特率生成
STM8的IRDA模块通常使用USART接口实现红外通信,其波特率由以下公式计算:
\text{Baud Rate} = \frac{f_{CLK}}{16 \times (USART_BRR)}
其中:
- $ f_{CLK} $:系统时钟频率(例如16MHz)
- $ USART_BRR $:波特率寄存器的设定值
为了得到指定的波特率,需要根据系统时钟频率反向计算出USART_BRR的值:
USART_BRR = \frac{f_{CLK}}{16 \times \text{Baud Rate}}
示例:
假设系统时钟为16MHz,目标波特率为9600bps:
USART_BRR = \frac{16,000,000}{16 \times 9600} = \frac{1,000,000}{9600} \approx 104.17
由于USART_BRR只能设置整数值,因此实际波特率会有微小误差。
5.1.2 波特率误差与通信稳定性分析
波特率的设置误差可能导致通信失败或数据错误。误差百分比可通过以下公式计算:
\text{误差} = \left| \frac{\text{理论波特率} - \text{实际波特率}}{\text{理论波特率}} \right| \times 100\%
误差分析示例:
继续使用上例,实际波特率计算如下:
\text{实际波特率} = \frac{16,000,000}{16 \times 104} \approx 9615.38
\text{误差} = \left| \frac{9600 - 9615.38}{9600} \right| \times 100\% \approx 0.16\%
误差在±2%以内通常可以接受。若误差过大,需考虑使用更精确的时钟源或调整分频系数。
5.2 STM8中的波特率寄存器配置
STM8的波特率配置主要通过USART_BRR寄存器实现,该寄存器位于USART模块中,分为高低两个字节(BRR1和BRR2),用于存储12位波特率分频值。
5.2.1 USART_BRR寄存器的设置方法
寄存器结构说明:
| 寄存器 | 位宽 | 描述 |
|---|---|---|
| BRR1 | 8位 | 存储波特率分频值的高8位 |
| BRR2 | 4位 | 存储波特率分频值的低4位 |
配置步骤:
-
计算USART_BRR值
如前所述,先根据系统时钟和目标波特率计算出USART_BRR值。 -
拆分高低字节
将计算出的USART_BRR值拆分为高8位和低4位:
- 高8位 = USART_BRR >> 4
- 低4位 = USART_BRR & 0x0F
- 写入寄存器
将上述值写入BRR1和BRR2寄存器:
USART1->BRR1 = (uint8_t)(USART_BRR >> 4); // 高8位
USART1->BRR2 = (uint8_t)(USART_BRR & 0x0F); // 低4位
代码解释与参数说明:
USART1->BRR1和USART1->BRR2:STM8标准外设库定义的寄存器访问方式。>> 4:将12位值右移4位,提取高8位。& 0x0F:使用掩码提取低4位。
完整配置代码示例:
void IRDA_SetBaudRate(uint32_t baudrate) {
uint32_t usart_brr;
uint8_t brr1, brr2;
// 计算波特率寄存器值
usart_brr = (16000000UL + (baudrate * 8)) / (baudrate * 16);
// 拆分高低字节
brr1 = (uint8_t)(usart_brr >> 4);
brr2 = (uint8_t)(usart_brr & 0x0F);
// 写入寄存器
USART1->BRR1 = brr1;
USART1->BRR2 = brr2;
}
逐行分析:
- 第5行:采用向上取整方式避免误差过大。
- 第9~10行:拆分高低字节。
- 第13~14行:写入寄存器,启用新的波特率设置。
波特率设置流程图(mermaid格式):
graph TD
A[开始设置波特率] --> B[输入目标波特率]
B --> C[获取系统时钟频率]
C --> D[计算USART_BRR值]
D --> E[拆分BRR1和BRR2]
E --> F[写入寄存器]
F --> G[完成波特率设置]
5.2.2 高低波特率下的通信适配
在不同波特率下,STM8的IRDA模块可能需要调整硬件配置或软件处理逻辑。以下是高低波特率下的一些适配策略:
低波特率(如9600bps):
- 优势: 抗干扰能力强,通信距离更远。
- 劣势: 数据传输速度慢,适用于低速控制或调试场景。
- 适配建议:
- 使用较低的系统时钟(如4MHz)以减少误差。
- 增加软件延时处理,提升稳定性。
高波特率(如115200bps):
- 优势: 数据传输速度快,适用于数据密集型应用。
- 劣势: 对时钟精度要求高,易受噪声干扰。
- 适配建议:
- 使用高精度外部晶振(如16MHz)。
- 启用中断机制处理数据接收,避免轮询导致的CPU占用过高。
- 在通信协议中加入重传机制和CRC校验,提高可靠性。
波特率适配对比表:
| 波特率 | 时钟要求 | 抗干扰能力 | 数据传输效率 | 适用场景 |
|---|---|---|---|---|
| 9600 | 低 | 高 | 低 | 调试、控制 |
| 19200 | 中 | 中 | 中 | 简单通信 |
| 115200 | 高 | 低 | 高 | 数据传输 |
提示: 在实际项目中,建议在初始化阶段通过串口调试工具测试不同波特率下的通信质量,并记录误差值,便于后续优化。
总结与延伸
波特率的设置是STM8 IRDA通信模块中不可忽视的一环。通过对系统时钟的合理配置和USART_BRR寄存器的精确操作,可以实现稳定可靠的红外通信。在实际应用中,还需结合硬件环境、通信距离、噪声干扰等因素进行综合评估,并通过软件手段进行补偿和优化。
在下一章中,我们将进一步探讨IRDA通信中数据帧的结构与解析方法,理解如何在接收到原始数据后正确提取有效信息。
6. 数据帧格式定义与解析
在嵌入式系统中,红外通信协议(IRDA)的数据帧格式是实现可靠通信的基础。IRDA协议规定了严格的帧结构,以确保数据的完整性和同步性。本章将从帧结构的基本组成出发,深入探讨起始位、数据位、校验位和停止位的功能定义,并详细解析数据帧的接收流程和解析机制。
6.1 IRDA数据帧结构
IRDA协议中定义的数据帧格式是一种异步串行通信结构,通常由以下几个部分组成:起始位(Start Bit)、数据位(Data Bits)、可选的校验位(Parity Bit)以及停止位(Stop Bit)。这种结构确保了发送端和接收端之间的同步与数据完整性。
6.1.1 起始位、数据位与停止位定义
在IRDA通信中,每个数据帧都以一个低电平的起始位开始,标志着数据传输的开始。起始位之后是若干个数据位,通常为5到8位,表示实际传输的数据内容。数据位的顺序是最低位(LSB)先传。最后是一个或多个高电平的停止位,用于表示数据帧的结束。
下表列出了IRDA数据帧的基本结构示例:
| 位类型 | 电平 | 描述 |
|---|---|---|
| 起始位 | 低电平 | 数据帧开始标志 |
| 数据位 | 可变 | 传输的数据内容(LSB优先) |
| 校验位(可选) | 可变 | 用于奇偶校验 |
| 停止位 | 高电平 | 数据帧结束标志 |
帧结构示意图(使用Mermaid):
graph LR
A[起始位] --> B[数据位]
B --> C[校验位]
C --> D[停止位]
6.1.2 校验位与数据完整性机制
校验位用于在数据传输过程中检测错误,常见的有偶校验和奇校验两种方式。在发送端,根据数据位的“1”数目来决定校验位的值,以使得整个数据位和校验位中“1”的总数为奇数(奇校验)或偶数(偶校验)。
例如,如果使用偶校验且数据位中有3个“1”,则校验位为1,使得总“1”数为4(偶数)。接收端在收到数据后,会进行同样的校验,若发现不一致,则认为传输过程中发生了错误。
6.2 数据帧的接收与解析流程
IRDA模块在接收数据时,需要对数据帧进行同步、提取数据位、校验数据完整性,并最终将解析后的数据送入应用层。该流程包括起始位检测、数据位采样、校验验证和数据缓存等步骤。
6.2.1 数据帧同步与起始位检测
当IRDA模块处于接收模式时,会持续监测输入引脚的电平变化。一旦检测到一个低电平的起始位,便开始进行帧同步操作。
以下是起始位检测的伪代码逻辑:
if (IRDA_RX_PIN == LOW) {
// 检测到起始位
start_bit_detected = TRUE;
// 启动定时器以采样后续位
start_timer();
}
逐行解析:
- 第1行:判断IRDA接收引脚是否为低电平,即起始位。
- 第2行:标记起始位已被检测到。
- 第3行:启动定时器,为后续的数据位采样做准备。
参数说明:
IRDA_RX_PIN:红外接收引脚的状态。start_timer():启动一个定时器,用于控制数据位的采样周期。
6.2.2 数据位提取与校验验证
在起始位被检测到后,IRDA模块会按照预设的波特率对后续的每一位进行采样。每采样一位后,将结果保存到数据缓冲区中。
以下是数据位提取和校验的伪代码实现:
for (int i = 0; i < DATA_BITS; i++) {
wait_for_half_bit_time(); // 半位时间用于稳定采样点
data_bit = IRDA_RX_PIN;
data_buffer |= (data_bit << i);
wait_for_full_bit_time(); // 等待下一位
}
// 校验位处理
if (parity_enabled) {
parity_bit = calculate_parity(data_buffer);
received_parity = get_received_parity_bit();
if (parity_bit != received_parity) {
error_flag = PARITY_ERROR;
}
}
逐行解析:
- 第1行:循环读取每个数据位。
- 第2行:等待半个位周期,以确保采样点在位的中点,提高采样准确性。
- 第3行:读取当前位的值。
- 第4行:将当前位按LSB顺序拼接到数据缓冲区。
- 第5行:等待一个完整的位周期,准备读取下一位。
- 第8行:如果启用了校验位,则进行校验计算。
- 第9行:获取接收到的校验位。
- 第10-12行:比较计算出的校验位与接收到的校验位,不一致则标记错误。
参数说明:
DATA_BITS:数据位的长度,通常为8位。wait_for_half_bit_time():等待半个位周期的时间函数。wait_for_full_bit_time():等待一个完整位周期的时间函数。calculate_parity():计算校验位的函数。get_received_parity_bit():获取接收到的校验位值。
数据接收流程图(使用Mermaid):
graph TD
A[开始接收] --> B{检测起始位?}
B -- 是 --> C[启动定时器]
C --> D[采样数据位]
D --> E{校验位使能?}
E -- 是 --> F[校验数据一致性]
F --> G{校验成功?}
G -- 是 --> H[数据缓存]
G -- 否 --> I[标记校验错误]
E -- 否 --> H
H --> J[通知应用层]
6.2.3 数据缓冲与应用层交互
在数据位被正确解析后,需要将其缓存到一个接收缓冲区中,以便上层应用处理。通常使用环形缓冲区(Ring Buffer)结构来管理接收数据。
以下是一个简单的环形缓冲区结构定义和数据入队函数:
#define RX_BUFFER_SIZE 128
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_head = 0, rx_tail = 0;
void rx_buffer_enqueue(uint8_t data) {
uint8_t next = (rx_head + 1) % RX_BUFFER_SIZE;
if (next != rx_tail) { // 防止缓冲区满
rx_buffer[rx_head] = data;
rx_head = next;
}
}
逐行解析:
- 第1-3行:定义环形缓冲区的大小、数组和读写指针。
- 第5行:入队函数,将接收到的数据添加到缓冲区。
- 第6行:计算下一个写入位置。
- 第7行:判断是否缓冲区已满,若未满则执行写入。
- 第8-9行:写入数据并更新写指针。
参数说明:
rx_buffer:接收数据的环形缓冲区。rx_head:写指针,指向下一个可写入的位置。rx_tail:读指针,指向下一个可读取的位置。rx_buffer_enqueue():将新数据加入缓冲区的函数。
在接收中断中,每当完整接收一个字节的数据帧后,调用该函数将数据压入缓冲区,等待主程序读取处理。
6.2.4 完整帧接收与状态机机制
为了更高效地处理数据帧,通常会设计一个状态机来管理接收过程的不同阶段。例如:
typedef enum {
IDLE,
START_BIT_DETECTED,
RECEIVING_DATA,
CHECK_PARITY,
FRAME_COMPLETE
} IRDA_RX_State;
IRDA_RX_State rx_state = IDLE;
在接收到起始位后,状态机切换为 START_BIT_DETECTED ,然后依次进入数据位接收、校验检查,最终到达帧完成状态。
这种状态机机制使得代码结构更清晰,便于扩展和维护,特别是在处理错误状态或超时机制时非常有用。
6.2.5 接收流程中的错误处理
在实际通信中,可能会出现起始位丢失、波特率不匹配、校验错误等情况。因此,在解析过程中需要加入错误检测与处理机制。
例如:
if (start_bit_timeout) {
rx_state = IDLE;
error_flag = START_BIT_TIMEOUT;
} else if (parity_error) {
error_flag = PARITY_MISMATCH;
}
逐行解析:
- 第1-3行:检测起始位超时错误,并重置状态机。
- 第4-5行:检测校验位不匹配错误,并设置错误标志。
参数说明:
start_bit_timeout:起始位未在指定时间内检测到。parity_error:校验位不一致。error_flag:错误标志位,供上层应用查询。
通过本章的深入解析,我们了解了IRDA数据帧的组成结构、接收流程以及错误处理机制。这些内容为后续的通信系统开发和调试提供了理论基础和实现指导。在下一章中,我们将结合STM8单片机,进入IRDA通信系统开发的实战环节。
7. 嵌入式红外通信系统开发实战
7.1 IRDA接口初始化函数实现
在STM8单片机上实现IRDA通信,首先需要完成硬件初始化。主要包括系统时钟配置、GPIO引脚设置以及IRDA模块的寄存器配置。
7.1.1 系统时钟与GPIO配置
STM8的系统时钟默认为内部16MHz RC振荡器。为了确保IRDA通信的稳定性,通常使用外部高速时钟(例如16MHz晶振)作为系统主时钟源。
// 初始化系统时钟
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 设置HSI不分频
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1); // CPU时钟不分频
IRDA通信使用的是USART模块的红外模式,因此需要将TX和RX引脚配置为复用推挽输出。
// 配置GPIO为复用推挽输出(以PA2为TX,PA3为RX为例)
GPIO_Init(GPIOA, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOA, GPIO_PIN_3, GPIO_MODE_IN_FL_NO_IT); // 输入浮空,无中断
7.1.2 IRDA模块初始化流程封装
将IRDA模块的初始化流程封装为一个函数,便于代码结构清晰和复用。
void IRDA_Init(uint32_t baudrate)
{
// 1. USART时钟使能
CLK_PeripheralClockConfig(CLK_PERIPHERAL_USART1, ENABLE);
// 2. 波特率设置
USART1->BRR = (uint16_t)(SystemCoreClock / (16 * baudrate));
// 3. 控制寄存器设置
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 使能发送、接收、串口
USART1->CR3 = USART_CR3_IRDAEN | USART_CR3_HDSEL; // 启用IRDA模式与半双工
// 4. 中断使能(可选)
USART1->CR2 |= USART_CR2_RXNEIE; // 接收中断使能
}
7.2 数据发送与接收中断控制流程
7.2.1 发送中断处理与数据队列管理
发送中断用于在发送寄存器空闲时触发,将发送队列中的下一个字节写入发送寄存器。可以使用环形缓冲区来管理发送数据。
uint8_t tx_buffer[64];
uint8_t tx_head = 0;
uint8_t tx_tail = 0;
void IRDA_SendByte(uint8_t data)
{
tx_buffer[tx_head] = data;
tx_head = (tx_head + 1) % 64;
USART1->CR2 |= USART_CR2_TXEIE; // 启动发送中断
}
// USART发送中断处理函数
@far @interrupt void USART1_TX_IRQHandler(void)
{
if (USART1->SR & USART_SR_TXE)
{
if (tx_tail != tx_head)
{
USART1->DR = tx_buffer[tx_tail];
tx_tail = (tx_tail + 1) % 64;
}
else
{
USART1->CR2 &= ~USART_CR2_TXEIE; // 关闭发送中断
}
}
}
7.2.2 接收中断处理与数据缓存机制
接收中断用于在数据寄存器非空时触发,将接收数据存入接收缓冲区。
uint8_t rx_buffer[64];
uint8_t rx_index = 0;
// USART接收中断处理函数
@far @interrupt void USART1_RX_IRQHandler(void)
{
if (USART1->SR & USART_SR_RXNE)
{
uint8_t data = USART1->DR;
rx_buffer[rx_index++] = data;
if (rx_index >= 64)
{
rx_index = 0; // 环形缓冲区重置
}
}
}
7.3 STM8_IRDA.C源码结构与功能分析
7.3.1 源码模块划分与函数接口设计
STM8_IRDA.c 是IRDA通信的核心源文件,包含以下模块:
- 初始化模块 :
IRDA_Init(),负责时钟、GPIO、寄存器等初始化。 - 发送模块 :
IRDA_SendByte()、IRDA_SendString(),支持单字节和字符串发送。 - 接收模块 :中断服务函数处理接收数据。
- 状态机模块 :解析数据帧,判断通信状态。
// 接口函数示例
void IRDA_Init(uint32_t baudrate);
void IRDA_SendByte(uint8_t data);
void IRDA_SendString(char *str);
7.3.2 通信状态机与错误处理机制
在IRDA通信中,可以通过状态机实现帧同步、数据校验等功能。
typedef enum {
IRDA_STATE_IDLE,
IRDA_STATE_RECEIVING,
IRDA_STATE_COMPLETE
} IRDA_State;
IRDA_State ir_state = IRDA_STATE_IDLE;
uint8_t rx_frame[32];
uint8_t frame_index = 0;
void IRDA_FrameHandler(uint8_t data)
{
switch(ir_state)
{
case IRDA_STATE_IDLE:
if(data == 0x55) // 帧头检测
{
ir_state = IRDA_STATE_RECEIVING;
frame_index = 0;
rx_frame[frame_index++] = data;
}
break;
case IRDA_STATE_RECEIVING:
rx_frame[frame_index++] = data;
if(frame_index >= 32)
{
ir_state = IRDA_STATE_COMPLETE;
// TODO: 校验与处理
}
break;
case IRDA_STATE_COMPLETE:
// 数据完整,进行处理
ir_state = IRDA_STATE_IDLE;
break;
}
}
7.4 TFBS4711测试工程功能与使用
7.4.1 硬件连接与测试环境搭建
TFBS4711是一款红外发射/接收一体化模块,支持IRDA协议。其与STM8的连接如下:
| STM8引脚 | TFBS4711引脚 | 功能 |
|---|---|---|
| PA2 | TXD | 发送红外数据 |
| PA3 | RXD | 接收红外数据 |
| GND | GND | 地 |
| VCC | VCC | 电源(3.3V或5V) |
7.4.2 测试流程与通信性能验证
测试流程如下:
-
初始化IRDA模块 :
c IRDA_Init(9600); -
发送测试数据 :
c IRDA_SendString("Hello IRDA\n"); -
接收并解析数据 :
c // 接收中断中调用IRDA_FrameHandler() -
验证通信质量 :
- 使用示波器观察红外发送信号的波形;
- 在接收端通过串口打印接收内容;
- 测试不同波特率下的通信稳定性。
graph TD
A[开始] --> B[系统初始化]
B --> C[IRDA模块初始化]
C --> D[等待发送/接收数据]
D --> E{是否发送数据?}
E -->|是| F[调用发送函数]
E -->|否| G[等待接收中断]
G --> H[接收数据]
H --> I[解析数据帧]
I --> D
通过以上步骤和模块化设计,开发者可以在STM8平台上快速构建稳定可靠的IRDA红外通信系统。
简介:IRDA是一种红外数据传输标准,广泛用于短距离、低速率的数据交换。本文基于STM8单片机实现IRDA红外通信的完整源码,涵盖中断方式下的自发自收功能。通过深入讲解STM8的中断系统、IRDA硬件模块配置、波特率设置、中断服务程序编写、数据帧格式及低功耗管理等内容,帮助开发者掌握在STM8平台上构建稳定红外通信系统的方法。配套测试工程“TFBS4711的IrDA测试工程源码”可用于验证通信功能的正确性,是学习嵌入式红外通信的理想实践资料。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐




所有评论(0)