一 背景
最近在调试红外通信功能的时候遇到了很多问题,在此总结一下,希望能帮到未来对此有疑问的自己,如果有幸能帮到其他人也算是做了一件有意义的事情了。
二 红外发射头与红外接收头
2.1 发射头
发射管也属于二极管,只有两个脚,通过控制二极管的导通来发射信号
2.2 接收头
接收管一般有三个脚,一个VCC,一个GND,还有一个信号脚。
2.3 起始信号、逻辑0、逻辑1的定义
通常在控制发射端时,以38KHz的频率来发送方波,此时发送端需要以高低电平来控制,接收头收到的是一个低电平,其他情况下为高电平。
2.3.1 起始信号
参考红外遥控器中引导码
-发送端波形
9ms发送方波,4.5ms不发送方波
-接收端波形
9ms是低电平,4.5ms是高电平
2.3.2 逻辑1
2.3.3 逻辑0
三 发送与接收处理
3.1 延时API
rtthread官方提供了一个微妙延时函数rt_hw_us_delay,在延时低于1000us时会有延时不准的问题,这里稍作一些修改,如果想要更准确的延时可能要用定时器的方式了。
void rt_hw_us_delay_2(rt_uint32_t us)
{
rt_uint32_t ticks;
rt_uint32_t told, tnow, tcnt = 0;
rt_uint32_t reload = SysTick->LOAD;
ticks = us * reload / (1000000UL / RT_TICK_PER_SECOND);
told = SysTick->VAL;
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow;
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
}
void rt_hw_us_delay(rt_uint32_t us)
{
if (us < 1000)
{
__IO uint32_t currentTicks = SysTick->VAL;
/* Number of ticks per millisecond */
const uint32_t tickPerMs = SysTick->LOAD + 1;
/* Number of ticks to count */
const uint32_t nbTicks = ((us - ((us > 0) ? 1 : 0)) * tickPerMs) / 1000;
/* Number of elapsed ticks */
uint32_t elapsedTicks = 0;
__IO uint32_t oldTicks = currentTicks;
do
{
currentTicks = SysTick->VAL;
elapsedTicks += (oldTicks < currentTicks) ? tickPerMs + oldTicks - currentTicks :
oldTicks - currentTicks;
oldTicks = currentTicks;
} while (nbTicks > elapsedTicks);
}
else
{
rt_hw_us_delay_2(us);
}
}
3.2 时间相关的宏定义
#define CONFIG_IR_FREQUENCY_HZ ((uint32_t)38000)
#define CONFIG_IR_FREQUENCY_US ((uint32_t)(1000000UL*1/CONFIG_IR_FREQUENCY_HZ))
#define CONFIG_IR_DELAY_US (CONFIG_IR_FREQUENCY_US/2)
#define ROUND_UP(M,N) (((M*10/N)+5)/10)
#define CONFIG_IR_TIME_ERROR_PERCENT (30)
#define TIME_GET_ERROR_MIN(T) (T-((T*CONFIG_IR_TIME_ERROR_PERCENT)/100))
#define TIME_GET_ERROR_MAX(T) (T+((T*CONFIG_IR_TIME_ERROR_PERCENT)/100))
#define CONFIG_IR_START_LOW_US ((uint32_t)9000)
#define CONFIG_IR_START_HIGH_US ((uint32_t)4500)
#define CONFIG_IR_START_HIGH_US_MIN TIME_GET_ERROR_MIN(CONFIG_IR_START_HIGH_US)
#define CONFIG_IR_START_HIGH_US_MAX TIME_GET_ERROR_MAX(CONFIG_IR_START_HIGH_US)
#define CONFIG_IR_COMMON_LOW_US ((uint32_t)500)
#define CONFIG_IR_COMMON_LOW_US_MIN TIME_GET_ERROR_MIN(CONFIG_IR_COMMON_LOW_US)
#define CONFIG_IR_COMMON_LOW_US_MAX TIME_GET_ERROR_MAX(CONFIG_IR_COMMON_LOW_US)
#define CONFIG_IR_LOGIC_0_HIGH_US ((uint32_t)800)
#define CONFIG_IR_LOGIC_0_HIGH_US_MIN TIME_GET_ERROR_MIN(CONFIG_IR_LOGIC_0_HIGH_US)
#define CONFIG_IR_LOGIC_0_HIGH_US_MAX TIME_GET_ERROR_MAX(CONFIG_IR_LOGIC_0_HIGH_US)
#define CONFIG_IR_LOGIC_1_HIGH_US ((uint32_t)1500)
#define CONFIG_IR_LOGIC_1_HIGH_US_MIN TIME_GET_ERROR_MIN(CONFIG_IR_LOGIC_1_HIGH_US)
#define CONFIG_IR_LOGIC_1_HIGH_US_MAX TIME_GET_ERROR_MAX(CONFIG_IR_LOGIC_1_HIGH_US)
3.3 信号发送API
#define IR_H() {GPIOE->BSRR = GPIO_PIN_0;}
#define IR_L() {GPIOE->BRR = GPIO_PIN_0;}
void ir_send_signal(uint16_t wave_us,uint16_t high_us)
{
if (wave_us)
{
wave_us = ROUND_UP(wave_us,CONFIG_IR_FREQUENCY_US);
while (wave_us--)
{
IR_H();
rt_hw_us_delay(CONFIG_IR_DELAY_US);
IR_L();
rt_hw_us_delay(CONFIG_IR_DELAY_US);
}
}
if (high_us)
{
high_us = ROUND_UP(high_us,CONFIG_IR_FREQUENCY_US);
while (high_us--)
{
rt_hw_us_delay(CONFIG_IR_FREQUENCY_US);
}
}
}
3.4 红外通信指令的定义
3.4.1 指令组成
起始信号+cmd+data+sum
3.4.2 高位先发
3.5 发送指令API
void ir_send_data(uint8_t set_type,uint8_t set_data)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
if (set_data & 0x80)//先发送高位
{
ir_send_signal(CONFIG_IR_COMMON_LOW_US,CONFIG_IR_LOGIC_1_HIGH_US);
}
else
{
ir_send_signal(CONFIG_IR_COMMON_LOW_US,CONFIG_IR_LOGIC_0_HIGH_US);
}
set_data <<= 1;
}
}
void ir_send_cmd(uint8_t set_cmd,uint8_t set_data)
{
uint8_t send_idx = 0;
//start
ir_send_signal(CONFIG_IR_START_LOW_US,CONFIG_IR_START_HIGH_US);
ir_send_data(set_cmd);
ir_send_data(set_data);
ir_send_data(set_cmd+set_data);
ir_send_signal(CONFIG_IR_COMMON_LOW_US,0);
}
3.6 接收处理
stm32可以使用定时器输入捕获的方式来获取上升沿的时间,从而得到当前的信号类型
3.6.1基于红外遥控修改
void ir_timer_init(void)
{
TIM_IC_InitTypeDef TIM3_Config;
htim3.Instance=TIM3;
htim3.Init.Prescaler=(72-1); //预分频器,1M的计数频率,1us加1.
htim3.Init.CounterMode=TIM_COUNTERMODE_UP;
htim3.Init.Period=10000;
htim3.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_IC_Init(&htim3);
TIM3_Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获
TIM3_Config.ICSelection=TIM_ICSELECTION_DIRECTTI;
TIM3_Config.ICPrescaler=TIM_ICPSC_DIV1;
TIM3_Config.ICFilter=0x03;
HAL_TIM_IC_ConfigChannel(&htim3,&TIM3_Config,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
__HAL_TIM_ENABLE_IT(&htim3,TIM_IT_UPDATE);
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (htim->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(TIM3_IRQn,1,3); //设置中断优先级,抢占优先级1,子优先级3
HAL_NVIC_EnableIRQ(TIM3_IRQn); //开启ITM4中断
}
}
void TIM3_IRQHandler(void)
{
rt_interrupt_enter();
HAL_TIM_IRQHandler(&htim3);
rt_interrupt_leave();
}
enum
{
ST_NONE = 0,
ST_START = 1,
ST_LOGIC_0,
ST_LOGIC_1,
ST_ERROR,
};
typedef struct
{
uint8_t type:3;//0-2
uint8_t rising_capture_ok:1;//3
uint8_t start_capture_ok:1;//4-7
uint8_t reserve:3;//4-7
}ir_signal_t;
typedef struct
{
union
{
uint8_t byte;
ir_signal_t ir_signal;
}val;
}status_val_t;
volatile status_val_t ir_check = {0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
static uint16_t count = 0;
if (1 == ir_check.val.ir_signal.start_capture_ok)
{
ir_check.val.ir_signal.rising_capture_ok = 0;
if (count>=30)
{
count = 0;
ir_check.val.ir_signal.start_capture_ok = 0;
}
else
{
count++;
}
}
}
}
volatile uint8_t temp_byte = 0;
volatile uint8_t byte_length = 0;
volatile uint8_t bit_cnt = 0;
volatile uint8_t ir_data_buf[3] = {0};
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance==TIM3)
{
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6))
{
TIM_RESET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
__HAL_TIM_SET_COUNTER(&htim3,0);
ir_check.val.ir_signal.rising_capture_ok = 1;
}
else //
{
uint32_t rising_time = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1);
TIM_RESET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
if (1 == ir_check.val.ir_signal.rising_capture_ok)
{
if (1 == ir_check.val.ir_signal.start_capture_ok)
{
if ((rising_time>=CONFIG_IR_LOGIC_0_HIGH_US_MIN) && (rising_time<=CONFIG_IR_LOGIC_0_HIGH_US_MAX))
{
temp_byte <<= 1;
bit_cnt++;
}
else if ((rising_time>=CONFIG_IR_LOGIC_1_HIGH_US_MIN) && (rising_time<=CONFIG_IR_LOGIC_1_HIGH_US_MAX))
{
temp_byte <<= 1;
temp_byte += 1;
bit_cnt++;
}
}
else if ((rising_time>=CONFIG_IR_START_HIGH_US_MIN) && (rising_time<=CONFIG_IR_START_HIGH_US_MAX))
{
ir_check.val.ir_signal.start_capture_ok = 1;
temp_byte = 0;
byte_length = 0;
bit_cnt = 0;
}
}
if (8 == bit_cnt)
{
ir_data_buf[byte_length++] = temp_byte;
temp_byte = 0;
bit_cnt = 0;
}
ir_check.val.ir_signal.rising_capture_ok = 0;
}
}
}
int main(void)
{
while(1)
{
if (3 == byte_length)
{
uint8_t idx = 0;
uint8_t check_sum = 0;
for (idx = 0; idx < (LENGTH_OF_ARRAY(ir_data_buf) - 1); idx++)
{
check_sum += ir_data_buf[idx];
}
APP_MAIN_PRINTF("\\t\\r\\n");
if (check_sum == ir_data_buf[byte_length - 1])
{
for(idx = 0; idx < LENGTH_OF_ARRAY(ir_data_buf); idx++)
{
APP_MAIN_PRINTF("{%02x} ",ir_data_buf[idx]);
}
APP_MAIN_PRINTF("\\r\\n");
}
byte_length = 0;
temp_byte = 0;
bit_cnt = 0;
}
}
return 0;
}
四 测试
将发射头的信号脚接到PE0,再将接收头的信号脚接到PA6进行测试,
将发射头对准接收头发送指令,可以看到发送与接收的数据完全一致。
msh >ir aa 01
TX:
[aa] [01] [ab]
msh >
RX:
{aa} {01} {ab}
全部0条评论
快来发表一下你的评论吧 !