2010年11月24日 星期三

紅外線遙控器(2/2) IR(Infrared ) NEC Protocol 實作

要準備的實驗器材如(圖1)
  1. LPCXPresso
  2. DIY Base board
  3. USB A to mini(接PC/NB 與LPCXpresso)
  4. RS232 cable接PC/NB 與 DIY Base board)
  5. 新的IR Receiver線路(圖2)
  6. 紅外線遙控器
(圖1)



DIY 線路請參考(圖2)

(圖2)

我接一個座,方便日後可替換IR Receiver (圖3)
(圖3)

由上一篇文章  紅外線遙控器(1/2) IR(Infrared ) NEC Protocol
幾個波形

  • 邏輯 "1"  一個560us的high接著low,總長2.25ms
  • 邏 輯 "0"  一個560us的high接著low 560us,總長1.12ms
  • 開始        9ms的high,接著4.5ms low,總長 13.5ms
  • 重複        9ms的high,接著2.25ms low,總長 11.25ms
  • 由 protocol得知,完成的一串傳輸會有 一個 開始訊號+4個BYTE,又因為傳輸兩次作增加可靠 性,兩次位址互為1的補數,操作命令亦同.所以4個BYTE綜合為16個'1'與16個'0',因此得到完整傳輸資料總長為 13.5+2.25X16+1.12X16=67.42ms
於是決定使用 timer 中的Capture功能,來擷取IR Receiver 的data out訊號,再作解碼動作.

依下列原理:
  • timer中的capture可以設定在pulse的上升緣(Rising edge)或是下降緣 (Failing edge)的時候中斷,並且擷取中斷時候的timer counter值
  • 因為timer counter值是cpu 時脈的某種倍數關係.所以每counter 累積1次就是經過固定的時間
  • 兩個Rising/Falling edge之間的counter之差就是這個pulse的時間寬度
  • 再由時間寬度來判斷,這個pulse是 start , '1' ,'0' or repeat

(圖4)
Projedt IR_Capture 介紹:

main.c中:
初始化cpu clock 48Mhz.


SystemInit();


週邊設定:
uart 115200


UARTInit(115200);



32 bit timmer 0,每一個counter 同於一個cpu clock ,啟動 capture pin1_5/CT32B0_CAP0 ,啟動timer0


init_timer32( 0,period);
enable_timer32(0);





若IR 解碼有效,從uart送出資料,並且讓LED閃爍


while (1) /* Loop forever */
{
#if(UART_ENABLE==1)

if(valid_key ==1)
{
GPIOSetValue(0,7,1);
printf("Address = %x \a",ir_address);
printf("Command = %x \n",ir_command);
valid_key = 0;
GPIOSetValue(0,7,0);
}
#endif
}


Timer32.c中:
fuction void TIMER32_0_IRQHandler(void)s內,
Faling edge時候會進入這段code,

if ( LPC_TMR32B0->IR & (0x1<<4) ) { LPC_TMR32B0->IR = 0x1<<4; /* clear interrupt flag */ IR_decoder(LPC_TMR32B0->CR0);
}


接著就做解碼動作.

ir_decoder_nec.c 中

void IR_decoder(uint32_t cnt)

計算前次與今次counter誤差,並紀錄counter值,

count_new= cnt;
temp = count_new-count_old;
count_old=count_new;



判斷兩個Faling edge的寬度

if((temp>=BIT_START_MIN)&&(temp<=BIT_START_MAX)) { data_decoded = DATA_START; } else if((temp>=BIT_ONE_MIN)&&(temp<=BIT_ONE_MAX)) { data_decoded = DATA_ONE; } else if((temp>=BIT_ZERO_MIN)&&(temp<=BIT_ZERO_MAX)) { data_decoded = DATA_ZERO; } else if((temp>=BIT_REPEAT_MIN)&&(temp<=BIT_REPEAT_MAX)) { data_decoded = DATA_REPEAT; } else data_decoded = DATA_INVALID;


state machine 來做不狀態處理

switch(ir_state)
{


Idle模式處理,
若decode為start訊號,進入下個state


case IR_IDLE:

if(DATA_START==data_decoded)
{
ir_state = IR_START;
timer32_0_capture=0;
valid_key = 0;
}

break;


若進入start 階段,
decode為 '1' or '0' 進入Address decode狀態
若否則回到idle狀態


case IR_START:

if((DATA_ONE!=data_decoded)&&(DATA_ZERO!=data_decoded))
{
ir_state = IR_IDLE;
break;
}
else
ir_state = IR_ADDRESS;



進入Address or Commad decode狀態時,處理方式概念是相同.


case IR_ADDRESS:
case IR_COMMAND:

// timer16_1_capture2[timer32_0_capture] = temp;

timer32_0_capture++;


若是前8個bit,且若decode為1,因為訊號的的順序是MSB ot LSB.
若以就進1往MSB推一位元入data變數中

if(timer32_0_capture<=8)
{
data <<=1;
if(DATA_ONE==data_decoded)
data |= 0x01;
}


若是後8個bit,且若decode為1,因為訊號的的順序是MSB ot LSB.
若以就進1往MSB推一位元入n_data變數中


else if(timer32_0_capture<=16)
{
n_data <<=1;
if(DATA_ONE==data_decoded)
n_data|= 0x01;
}


當讀滿16個bit先判斷data與n_data是否互為1的補數
若是就進入下個階段,Address decode完成就往 Command decode階段.
若Command decode完成就進入idle階段,並且將有效解碼的flag設為1.

若非互為1的補數,資料有誤.
放棄解這次的封包,並且進入idle狀態.等待下次的start 訊號.


if(timer32_0_capture==16)
{
if((data+n_data)==0xff)
{
if(ir_state == IR_ADDRESS)
{
ir_address = data;
}
else
{
ir_command = data;
valid_key =1;
}
}
else
ir_state = IR_IDLE;

if(ir_state == IR_ADDRESS)
ir_state = IR_COMMAND;
else
ir_state = IR_IDLE;

data=0;
n_data=0;
timer32_0_capture = 0;
}
break;


若發生未預期狀態則回到idle狀態


default:
ir_state = IR_IDLE;
break;
}



用putty (putty不知道怎麼設定使用,可參考 Hello World! UART )來試試看遙控器的解碼結果吧.

(圖5)

見圖5中,Address有兩種值,這是拿了不同遙控器測試解出來的結果.
通常Address就是來區分不同廠家或是不同設備,而Command代表的當然就是不同按鈕的功能.

參考範例請至此下載  Download


0 留言:

張貼留言

Related Posts Plugin for WordPress, Blogger...