本文介绍通过控制潘多拉开发板实现红外遥控的接收与发送。要实现红外遥控的接收与发送只需要使用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;
}
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);
}
}
我们通过对主程序中的代码进行追踪,发现函数app_ir_learn_and_send() 的内部调用LOG_I(“learning mode START.”);语句。通过查阅资料得知,是RT-Thread的ulog日志功能。讲一下
下面重点讲一下,关于RT-Thread的ulog相关的内容,我会将我在看代码过程中,查阅的一些延伸知识放进去,大家可以跳过不去了解,只掌握关于ulog的API函数应用就可以。废话少说,开始#%%¥……
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。
ulog日志组件的软件架构如下图所示:
前端层:用户接口层,提供了 syslog 及 LOG_X 两类 API 接口,方便用户在不同的场景中使用。
核心层:将上层应用层传过来的数据,根据日志的配置进行格式化和过滤生成相应的日志信息,并将此信息输出到底层。
后端层:接收核心层发过来的日志数据,将数据发送到相应的硬件设备上,例如:串口、网络等。
日志级别表示日志内容的重要性,在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 中每个文件(模块)也可以设定独立的日志级别。全局级别作用域大于模块级别,也就是模块级别只能管控那些高于或等于全局级别的模块日志。
为了避免日志被杂乱无章的输出出来,就需要使用标签(tag)给每条日志进行分类。标签的定义是按照模块化的方式。
#ifndef ULOG_USING_SYSLOG
#define LOG_TAG "example"
#define LOG_LVL LOG_LVL_DBG
#include
#else
#include
#endif /* ULOG_USING_SYSLOG */