本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:IRDA是一种红外数据传输标准,广泛用于短距离、低速率的数据交换。本文基于STM8单片机实现IRDA红外通信的完整源码,涵盖中断方式下的自发自收功能。通过深入讲解STM8的中断系统、IRDA硬件模块配置、波特率设置、中断服务程序编写、数据帧格式及低功耗管理等内容,帮助开发者掌握在STM8平台上构建稳定红外通信系统的方法。配套测试工程“TFBS4711的IrDA测试工程源码”可用于验证通信功能的正确性,是学习嵌入式红外通信的理想实践资料。
STM8_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的内存单元

逐行分析

  1. LD A, #0x55 :使用立即寻址方式,将0x55赋值给累加器A;
  2. 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
}

逐行分析

  1. PUSH A :将A寄存器内容压入堆栈,SP减1;
  2. 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模块

逐行分析

  1. USART_Init(...) :设置通信参数;
  2. USART_CR4 |= USART_CR4_IREN :启用IRDA模式;
  3. USART_CR3 |= USART_CR3_HDSEL :设置为半双工模式;
  4. 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模式并切换为半双工通信是基本配置步骤之一。半双工通信适用于设备之间交替发送与接收的场景,非常适合红外遥控器等应用场景。

配置流程:
  1. 使能IRDA模式: 设置 USART_CR1.IREN = 1
  2. 设置半双工模式: 设置 USART_CR1.EDIT = 1
  3. 关闭其他模式干扰: 清除 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位
配置步骤:
  1. 计算USART_BRR值
    如前所述,先根据系统时钟和目标波特率计算出USART_BRR值。

  2. 拆分高低字节
    将计算出的USART_BRR值拆分为高8位和低4位:

  • 高8位 = USART_BRR >> 4
  • 低4位 = USART_BRR & 0x0F
  1. 写入寄存器
    将上述值写入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 测试流程与通信性能验证

测试流程如下:

  1. 初始化IRDA模块
    c IRDA_Init(9600);

  2. 发送测试数据
    c IRDA_SendString("Hello IRDA\n");

  3. 接收并解析数据
    c // 接收中断中调用IRDA_FrameHandler()

  4. 验证通信质量
    - 使用示波器观察红外发送信号的波形;
    - 在接收端通过串口打印接收内容;
    - 测试不同波特率下的通信稳定性。

graph TD
    A[开始] --> B[系统初始化]
    B --> C[IRDA模块初始化]
    C --> D[等待发送/接收数据]
    D --> E{是否发送数据?}
    E -->|是| F[调用发送函数]
    E -->|否| G[等待接收中断]
    G --> H[接收数据]
    H --> I[解析数据帧]
    I --> D

通过以上步骤和模块化设计,开发者可以在STM8平台上快速构建稳定可靠的IRDA红外通信系统。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:IRDA是一种红外数据传输标准,广泛用于短距离、低速率的数据交换。本文基于STM8单片机实现IRDA红外通信的完整源码,涵盖中断方式下的自发自收功能。通过深入讲解STM8的中断系统、IRDA硬件模块配置、波特率设置、中断服务程序编写、数据帧格式及低功耗管理等内容,帮助开发者掌握在STM8平台上构建稳定红外通信系统的方法。配套测试工程“TFBS4711的IrDA测试工程源码”可用于验证通信功能的正确性,是学习嵌入式红外通信的理想实践资料。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐