一 背景
最近在調試紅外通信功能的時候遇到了很多問題,在此總結一下,希望能幫到未來對此有疑問的自己,如果有幸能幫到其他人也算是做了一件有意義的事情了。
二 紅外發射頭與紅外接收頭
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("trn");
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("rn");
}
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}
評論
查看更多