[单片机芯片] # CH32V003 与 TFDU4101 红外通信测试:低成本红外数据传输方案

[复制链接]
347|0
gaoshanmicai 发表于 2025-11-6 11:23 | 显示全部楼层 |阅读模式
# CH32V003 与 TFDU4101 红外通信测试:低成本红外数据传输方案
## 一、测试目的
官方没有给出历程,想学习一下配置irDA模式的串口
## 二、硬件准备
### 1. 核心器件清单
主控 MCU:CH32V003F4P6(SOP8 封装,72MHz 主频,内置 USART、GPIO 等外设)
红外模块:TFDU4101(集成红外发送器与接收器,支持 38kHz 载波,3.3V 供电)
辅助元件:10kΩ 上拉电阻(按键用)、1kΩ 限流电阻(LED 用)、3.3V 电源模块(或 USB 转 3.3V)
工具:烧录器、杜邦线、示波器(可选,用于信号调试)

TFDU4101 引脚定义

引脚定义.png
原理图如下
schematic.png
### 2. 硬件连接表
CH32V003 引脚        TFDU4101 引脚        功能说明        备注
PD5        RX        红外模块接收端(数据输入)        USART1_TX,
PD6        TX        红外模块发送端(数据输出)        USART1_RX,
PD3        LED 正极        接收成功指示灯        LED 负极串 1kΩ 电阻接 GND
3.3V        VCC        模块供电        不得接 5V,避免烧毁芯片
GND        GND        共地        必须共地,否则通信不稳定
## 三、软件设计
### 1. 核心功能架构
基于 CH32V003 的 USART 外设,启用IrDA 模式(红外专用编码协议),适配 TFDU4101 的 38kHz 载波调制。
按键触发发送固定字符串,接收数据时翻转 LED 指示灯,并回显接收内容(便于直观验证)。
采用中断接收方式,提升数据接收效率,避免主循环阻塞。
### 2. 关键代码实现(完整可运行)
(1)头文件与宏定义
c
```c
#include "debug.h"
#include "SoftUART.h"

/* Global define */

/* Global Variable */
vu8 val;

uint8_t tmp[20] = {0x01, 0x80, 0xaa, 0x55};
// 串口接收配置
#define RX_BUF_SIZE 64
uint8_t rx_buf[RX_BUF_SIZE];
uint16_t rx_len = 0;
uint8_t rx_complete = 0;  // 接收完成标志(换行符结束)



/*********************************************************************
* @fn      USART1_Config
* @brief   初始化串口1(PD5/PD6引脚 + IrDA模式,适配TFDU4101)
********************************************************************/
void USART1_Config(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    USART_InitTypeDef USART_InitStructure = {0};

    // 1. 使能USART1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD |RCC_APB2Periph_USART1, ENABLE);


    // 2. 配置PD5(TX)和PD6(RX)为串口功能
    // PD5:复用推挽输出(IrDA发送端)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出(USART1 TX)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);



    // PD6:浮空输入(IrDA接收端)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入(USART1 RX)
    GPIO_Init(GPIOD, &GPIO_InitStructure);



    // 3. 配置串口基本参数(8N1)
    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  // 收发模式
    USART_Init(USART1, &USART_InitStructure);  // 调用库函数初始化

    USART1->CTLR2 &=0x87FF;
    USART_SmartCardCmd(USART1,DISABLE);
    USART_HalfDuplexCmd(USART1,DISABLE);



    // 4. 配置IrDA模式(TFDU4101必须启用,红外信号需要IrDA编码)



    // 5. 使能接收中断(收到数据后触发中断)
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  // 接收非空中断

    // 6. 配置NVIC中断优先级
    NVIC_InitTypeDef NVIC_InitStructure = {0};
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  // 抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         // 子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    /* must clean LINEN,STOP,CLKEN,SCEN,HDSEL */
    //USART_LINCmd(USART1, DISABLE);





    // 7. 使能串口1
    USART_Cmd(USART1, ENABLE);
    USART_SetPrescaler(USART1,1);//很重要


    USART_IrDAConfig(USART1, USART_IrDAMode_Normal);  // 标准模式(非低功耗)
//    USART_IrDACmd(USART1, DISABLE);  // 开启IrDA功能
    USART_IrDACmd(USART1, ENABLE);  // 开启IrDA功能



}
/*********************************************************************
* @fn      GPIO_Toggle_INIT
*
* @brief   Initializes GPIOA.0
*
* @return  none
*/
void GPIO_Toggle_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_RESET);
}



//#define  SENDDEV

#define  RECVDEV
/*********************************************************************
* @fn      main
*
* @brief   Main program.
*
* @return  none
*/
int main(void)
{
    uint8_t i=0;
    uint8_t strarr[64];
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    SystemCoreClockUpdate();
    Delay_Init();
#if (SDI_PRINT == SDI_PR_OPEN)
    SDI_Printf_Enable();
#else
    USART_Printf_Init(115200);
#endif
    GPIO_Toggle_INIT();
    USART1_Config(9600);
   // printf("SystemClk:%d\r\n", SystemCoreClock);
   // printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());

    SoftUARTFunc.Init(115200);

   // USART1_SendString("IrDA Ready (PD5/PD6)\n");  // 初始化提示
    uint8_t flag =0;

    while (1)
    {

#ifdef SENDDEV
        USART1_SendString("Send:irDA test\n");
#endif
        Delay_Ms(1000);

#ifdef RECVDEV
        if (rx_complete)
       {
           rx_complete = 0;  // 清除标志
           // 翻转LED提示接收完成
           GPIO_WriteBit(GPIOD, GPIO_Pin_3, (i == 0) ? (i = Bit_SET) : (i = Bit_RESET));

           // 回显接收数据

         //  printf("Received:%s\n",rx_buf);

           sprintf(strarr,"Received: ");
           SoftUARTFunc.TransmitBytes(strarr,strlen(strarr));

           memcpy(strarr,rx_buf,strlen(rx_buf));
           SoftUARTFunc.TransmitBytes(strarr,strlen(strarr));

           memset(rx_buf,0,64);
           memset(strarr,0,64);

       }
#endif
//        if (SoftUARTFunc.ReceiveBytes(tmp, 0xff))
//            SoftUARTFunc.TransmitBytes(tmp, 4);
    }
}


/*********************************************************************
* @fn      USART1_IRQHandler
* @brief   串口1中断服务函数(PD6接收数据处理)
********************************************************************/
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART1_IRQHandler(void)
{
    uint8_t data = 0;
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  // 接收非空中断触发
    {
        data = USART_ReceiveData(USART1);  // 读取接收数据

        // 接收逻辑:缓冲区未满且未到结束符
        if (rx_len < RX_BUF_SIZE - 1)
        {
            rx_buf[rx_len++] = data;
            if (data == '\n')  // 以换行符作为一包数据结束
            {
                rx_buf[rx_len] = '\0';  // 字符串结束符
                rx_complete = 1;        // 置位接收完成标志
                rx_len = 0;             // 重置接收长度
            }
        }
        else
        {
            // 缓冲区满,强制结束
            rx_buf[RX_BUF_SIZE - 1] = '\0';
            rx_complete = 1;
            rx_len = 0;
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);  // 清除中断标志
    }
}


```

注意在串口初始话的过程中一定要把**分频设置成1** 。把这句话加初始化程序中


## 四、过程与结果
recv.png
test7.png
关键现象验证
接收端收到数据后,LED 稳定翻转,串口回显内容完整,无乱码。
遮挡红外窗口时,通信立即中断;移除遮挡后,通信恢复正常。
低波特率(2400)下,即使有轻微遮挡或角度偏差,仍能稳定通信。
### 五、常见问题与解决方法
问题现象        可能原因        解决方法
接收端无反应        1. IrDA 模式未开启;2. 引脚重映射配置错误;3. 硬件接线反        1. 确认调用USART_IrDACmd(USART1, ENABLE);3. 核对 PD5/PD6 与 TFDU4101 的 TX/RX 连接 确定初始话分频
通信距离短        1. 波特率过高;2. 红外窗口未对准;3. 电源电压不稳定        1. 降低波特率至 2400;2. 调整设备角度,避免遮挡;3. 更换稳定的 3.3V 电源
接收数据乱码        1. 波特率不匹配;2. 时钟配置错误        1. 确保两套设备波特率一致;
六、总结
1. 测试结论
CH32V003 与 TFDU4101 的组合实现了低成本、低功耗、短距离的红外通信,整套方案硬件成本<5 元,完全满足玩具、简易传感器、遥控器等场景的需求。其中,2400 波特率是兼顾距离与效率的最优选择。
2. 关于CH32V003 单片机,单线下载,MR stdio软件做的很好,比其他厂家基于eclipse开发的启动快多了


您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

40

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部