RT-Thread源码详解(二)------红外遥控

本文介绍通过控制潘多拉开发板实现红外遥控的接收与发送。要实现红外遥控的接收与发送只需要使用stm32内部定时器控制外部管脚变化产生38kHz的方波,以此控制相应的红外接收与发射接口,就可以实现红外控制。
代码讲解:

//#define PERIPH_BASE           (0x40000000UL) /*!< Peripheral base address */
//#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000UL)
//#define RCC_BASE              (AHB1PERIPH_BASE + 0x1000UL)
//#define RCC                 ((RCC_TypeDef *) RCC_BASE)
/*
typedef struct
{
  __IO uint32_t CR;          /*!< RCC clock control register,                                              Address offset: 0x00 */
  __IO uint32_t ICSCR;       /*!< RCC internal clock sources calibration register,                         Address offset: 0x04 */
  __IO uint32_t CFGR;        /*!< RCC clock configuration register,                                        Address offset: 0x08 */
  __IO uint32_t PLLCFGR;     /*!< RCC system PLL configuration register,                                   Address offset: 0x0C */
  __IO uint32_t PLLSAI1CFGR; /*!< RCC PLL SAI1 configuration register,                                     Address offset: 0x10 */
  __IO uint32_t PLLSAI2CFGR; /*!< RCC PLL SAI2 configuration register,                                     Address offset: 0x14 */
  __IO uint32_t CIER;        /*!< RCC clock interrupt enable register,                                     Address offset: 0x18 */
  __IO uint32_t CIFR;        /*!< RCC clock interrupt flag register,                                       Address offset: 0x1C */
  __IO uint32_t CICR;        /*!< RCC clock interrupt clear register,                                      Address offset: 0x20 */
  uint32_t      RESERVED0;   /*!< Reserved,                                                                Address offset: 0x24 */
  __IO uint32_t AHB1RSTR;    /*!< RCC AHB1 peripheral reset register,                                      Address offset: 0x28 */
  __IO uint32_t AHB2RSTR;    /*!< RCC AHB2 peripheral reset register,                                      Address offset: 0x2C */
  __IO uint32_t AHB3RSTR;    /*!< RCC AHB3 peripheral reset register,                                      Address offset: 0x30 */
  uint32_t      RESERVED1;   /*!< Reserved,                                                                Address offset: 0x34 */
  __IO uint32_t APB1RSTR1;   /*!< RCC APB1 peripheral reset register 1,                                    Address offset: 0x38 */
  __IO uint32_t APB1RSTR2;   /*!< RCC APB1 peripheral reset register 2,                                    Address offset: 0x3C */
  __IO uint32_t APB2RSTR;    /*!< RCC APB2 peripheral reset register,                                      Address offset: 0x40 */
  uint32_t      RESERVED2;   /*!< Reserved,                                                                Address offset: 0x44 */
  __IO uint32_t AHB1ENR;     /*!< RCC AHB1 peripheral clocks enable register,                              Address offset: 0x48 */
  __IO uint32_t AHB2ENR;     /*!< RCC AHB2 peripheral clocks enable register,                              Address offset: 0x4C */
  __IO uint32_t AHB3ENR;     /*!< RCC AHB3 peripheral clocks enable register,                              Address offset: 0x50 */
  uint32_t      RESERVED3;   /*!< Reserved,                                                                Address offset: 0x54 */
  __IO uint32_t APB1ENR1;    /*!< RCC APB1 peripheral clocks enable register 1,                            Address offset: 0x58 */
  __IO uint32_t APB1ENR2;    /*!< RCC APB1 peripheral clocks enable register 2,                            Address offset: 0x5C */
  __IO uint32_t APB2ENR;     /*!< RCC APB2 peripheral clocks enable register,                              Address offset: 0x60 */
  uint32_t      RESERVED4;   /*!< Reserved,                                                                Address offset: 0x64 */
  __IO uint32_t AHB1SMENR;   /*!< RCC AHB1 peripheral clocks enable in sleep and stop modes register,      Address offset: 0x68 */
  __IO uint32_t AHB2SMENR;   /*!< RCC AHB2 peripheral clocks enable in sleep and stop modes register,      Address offset: 0x6C */
  __IO uint32_t AHB3SMENR;   /*!< RCC AHB3 peripheral clocks enable in sleep and stop modes register,      Address offset: 0x70 */
  uint32_t      RESERVED5;   /*!< Reserved,                                                                Address offset: 0x74 */
  __IO uint32_t APB1SMENR1;  /*!< RCC APB1 peripheral clocks enable in sleep mode and stop modes register 1, Address offset: 0x78 */
  __IO uint32_t APB1SMENR2;  /*!< RCC APB1 peripheral clocks enable in sleep mode and stop modes register 2, Address offset: 0x7C */
  __IO uint32_t APB2SMENR;   /*!< RCC APB2 peripheral clocks enable in sleep mode and stop modes register, Address offset: 0x80 */
  uint32_t      RESERVED6;   /*!< Reserved,                                                                Address offset: 0x84 */
  __IO uint32_t CCIPR;       /*!< RCC peripherals independent clock configuration register,                Address offset: 0x88 */
  uint32_t      RESERVED7;   /*!< Reserved,                                                                Address offset: 0x8C */
  __IO uint32_t BDCR;        /*!< RCC backup domain control register,                                      Address offset: 0x90 */
  __IO uint32_t CSR;         /*!< RCC clock control & status register,                                     Address offset: 0x94 */
} RCC_TypeDef;
*/
#if defined(TIM3)
#define __HAL_RCC_TIM3_CLK_ENABLE()            do { \
                                                 __IO uint32_t tmpreg; \__IO代表的是volatile
                                                 SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_TIM3EN); \//写相应寄存器的位,使能定时器3
                                                 /* Delay after an RCC peripheral clock enabling */ \
                                                 tmpreg = READ_BIT(RCC->APB1ENR1, RCC_APB1ENR1_TIM3EN); \
                                                 UNUSED(tmpreg); \
                                               } while(0)
#endif /* TIM3 */

主程序代码如下:

int main(void)
{
    unsigned int count = 1;
    app_infrared_init();
    while (count > 0)
    {
        app_ir_learn_and_send();
        count++;
    }
    return 0;
}

app_infrared_init()函数:

int app_infrared_init(void)
{
    infrared_init();

    /* set beep pin mode to output */
    rt_pin_mode(PIN_BEEP, PIN_MODE_OUTPUT);//设置蜂鸣器对应的PIN为输出模式
    /* set KEY  pin mode to input */
    rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT);//设置按键对应的PIN为输入模式
    rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT);
    rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT);
    /* set RGB  pin mode to input */
    rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);//设置LED灯对应的PIN为输出模式
    rt_pin_mode(PIN_LED_G, PIN_MODE_OUTPUT);
    rt_pin_mode(PIN_LED_B, PIN_MODE_OUTPUT);

    rt_pin_write(PIN_LED_R, PIN_HIGH);//关闭LED灯,注意这里原理图设计为低电平LED灯亮,具体看下面原理图
    rt_pin_write(PIN_LED_G, PIN_HIGH);
    rt_pin_write(PIN_LED_B, PIN_HIGH);

    return 0;
}

RT-Thread源码详解(二)------红外遥控_第1张图片app_ir_learn_and_send()函数:

void app_ir_learn_and_send(void)
{
    volatile rt_size_t size;
    rt_int16_t key;
    rt_kprintf("\n");
    LOG_I("learning mode START.");
    rgb_ctrl(LED_RED);

    size = infrared_receive(buf, 1000, RT_WAITING_FOREVER, 750);
    if (size == 0)
    {
        LOG_W("nothing receive.");
    }

    LOG_I("learning mode STOP.");
    rgb_ctrl(LED_ALL_OFF);

    LOG_I("sending  mode .       | Press KEY2 exit send mode or Press KEY0 send infrared sign.");
    rgb_ctrl(LED_GREEN);
    while (1)
    {
        key = key_scan();
        if (key == PIN_KEY0)
        {
            rgb_ctrl(LED_BLUE);

            if (infrared_send(buf, size) == size)
            {
                LOG_I("send ok.");
            }
            else
            {
                LOG_I("send fail.");
            }
            rgb_ctrl(LED_GREEN);
        }
        else if (key == PIN_KEY2)
        {
            LOG_I("sending  mode EXIT.");
            break;
        }
        rt_thread_mdelay(10);
    }
}

RT-Thread源码详解(二)------红外遥控_第2张图片

二、RT-Thread中的ulog日志功能

我们通过对主程序中的代码进行追踪,发现函数app_ir_learn_and_send() 的内部调用LOG_I(“learning mode START.”);语句。通过查阅资料得知,是RT-Thread的ulog日志功能。讲一下
下面重点讲一下,关于RT-Thread的ulog相关的内容,我会将我在看代码过程中,查阅的一些延伸知识放进去,大家可以跳过不去了解,只掌握关于ulog的API函数应用就可以。废话少说,开始#%%¥……

1、ulog简介

ulog是一个RT-Thread的日志组件,它能做到最低ROM<1K, RAM<0.2K的资源占用。具有如下特点:
a、 日志后端的输出多样性,例如:串口、网络、文件等等;
b、 日志输出被设计为线程安全的方式,并支持异步输出;
c、 日志系统高可靠,在中断ISR、Hardfault等复杂环境下依旧可用;
d、 日志支持在运行期和编译器设置输出级别;
e、日志内容支持按关键字及标签方式进行全局过滤;
f、API和日志格式可兼容linux syslog;
h、支持以hex格式dump调试数据到日志中;
i、兼容rtdbg(RTT 早期的日志头文件)及EasyLogger的日志输出API。

2、软件架构

ulog日志组件的软件架构如下图所示:
RT-Thread源码详解(二)------红外遥控_第3张图片
前端层:用户接口层,提供了 syslog 及 LOG_X 两类 API 接口,方便用户在不同的场景中使用。
核心层:将上层应用层传过来的数据,根据日志的配置进行格式化和过滤生成相应的日志信息,并将此信息输出到底层。
后端层:接收核心层发过来的日志数据,将数据发送到相应的硬件设备上,例如:串口、网络等。

3、日志级别

日志级别表示日志内容的重要性,在ulog中的日志级别如下:

/* logger level, the number is compatible for syslog */
#define LOG_LVL_ASSERT                 0         //断言, 发生无法处理、致命性的的错误,以至于系统无法继续运行的断言日志
#define LOG_LVL_ERROR                  3		//错误, 发生严重的、不可修复的错误时输出的日志属于错误级别日志
#define LOG_LVL_WARNING                4		//警告, 出现一些不太重要的、具有可修复性的错误时,会输出这些警告日志
#define LOG_LVL_INFO                   6		//信息, 给本模块上层使用人员查看的重要提示信息日志,例如:初始化成功,当前工作状态等。该级别日志一般在量产时依旧保留
#define LOG_LVL_DBG                    7		//调试, 给本模块开发人员查看的调试日志,该级别日志一般在量产时关闭

在 ulog 中日志级别还有如下分类:
** 静态级别与动态级别: **按照日志是否可以在运行阶段修改进行分类。可在运行阶段修改的称之为动态级别,只能在编译阶段修改的称之为静态级别。比静态级别低的日志(这里特指使用 LOG_X API 的日志)将不会被编译到 ROM 中,最终也不会输出、显示出来。而动态级别可以管控的是高于或等于静态级别的日志。在 ulog 运行时,比动态级别低的日志会被过滤掉。
**全局级别与模块级别:**按照作用域进行的分类。在 ulog 中每个文件(模块)也可以设定独立的日志级别。全局级别作用域大于模块级别,也就是模块级别只能管控那些高于或等于全局级别的模块日志。

4、日志标签

为了避免日志被杂乱无章的输出出来,就需要使用标签(tag)给每条日志进行分类。标签的定义是按照模块化的方式。

#ifndef ULOG_USING_SYSLOG
#define LOG_TAG              "example"
#define LOG_LVL              LOG_LVL_DBG
#include 
#else
#include 
#endif /* ULOG_USING_SYSLOG */

你可能感兴趣的:(RT-Thread)