要实现的功能模块有四个:ir信号解码,待机信号重复按键判断,i2c从设备中断处理,LED&KEYPAD扫描。其中这四个模块中除了i2c从设备中断处理,其他都需要用到定时器。
不巧的是,我们用到的单片机是HT46R22,只有一个定时器。我参考了操作系统时间片的概念,最终写了这个目前还觉得完美的代码。从测试结果来看,模块之间没有互相干扰且工作良好。没采用时间片之前,ir信号到达时会导致LED闪烁,这样每次ir信号到达都会打断timer,而LED&Keypad的扫描又依赖于timer调度,所以自然会产生闪烁现象)。但用时间片来解码ir信号,显著有一个缺点,就是脉冲宽度计算时误差取值范围太大--达到一个时间片的大小。
写完这个代码后,我还写了一个应用于单片机的task schedule和message box,简单的来说就是在这个程序上封装了一层,实现了insert_task_2_queue()和schedule_task()之类的函数。但是很不巧,仿真器到期要归还给HT公司,这个代码虽已大致完成,但还没调试通过,这里就暂时保留吧。
关于代码,关于各模块原理,恕我这里不详述,因为涉及方面很广,我想谈却无从谈起。如果需要了解这份代码而又有各种困惑的,feel free to contact me by email。
另外请不要轻易责怪电子工程师喜欢用全局变量,喜欢用数组而不是指针。我以前也这样,直到我用了HT-IDE 3000这个开发平台,不支持static变量,不支持指针,没有malloc和free。何况单片机程序最主要的是稳定,尤其当你面对的是OTP(一次性编程)芯片。
//Ht46r22.c // //Body: Ht46r22 // //Mask option following: //SysVolt: 5.0V //SysFreq: 8000KHz //Wake-up PA0-7: All Non-Wake-up //Pull-high PA0-5: All Pull-high //IIC: Enable //Pull-high PB: Pull-high //Pull-high PC: Pull-high //Pull_high PD: Pull-high //the others use the default value #include <Ht46r22.h> /********************************* BASIC TYPE ********************************/ #define uint8_t unsigned char #define uint16_t unsigned long /********************************* IR SIGNAL *********************************/ #define crystal 8000000 // Crystal frequency #define PSC 16 // Prescaler stage #define nMS 0.25 // Timer interval: 0.25ms #define MS_IR_LEADER 5.44 // Leader code: 5.44ms #define MS_IR_CODE_0 0.76 // Code-0: 0.76ms,=(0.25ms * 3) #define MS_IR_CODE_1 1.73 // Code-1: 1.73ms,=(0.25ms * 7) #define IR_CUSTOM_CODE1 (0x80) // customer-code 1, Need convert BIT7~0 to bit0~7 #define IR_CUSTOM_CODE2 (0xff) // customer-code 2 #define IR_CUSTOM_CODE3 (0xff) // customer-code 3 #define IR_CUSTOM_CODE4 (0x80) // customer-code 4 #define IR_KEYVALUE_PWR1 (0x46) // standby-code 1 #define IR_KEYVALUE_PWR2 (0x31) // standby-code 2 /********************************* TMR VALUE *********************************/ #define TMR_nMS 0x82 // (0xff-((nMS*crystal/1000)/PSC)) Timer value for timing 0.25ms #define TMR_IR_0_nTIMES 3 // Code-0: 0.76ms=0.25ms*3 #define TMR_IR_1_nTIMES 7 // Code-1: 1.73ms=0.25ms*7 #define TMR_IR_SCALE 1 // Error range uint8_t tmr_ir_decode; uint8_t tmr_ir_repeat; uint8_t tmr_led_refresh; /********************************* IR DECODE *********************************/ #define IR_FRAME_BITS 48 // Total bits of one frame data #define IR_FRAME_BYTES (IR_FRAME_BITS/8) // Length of one frame data uint8_t ir_bits_cnt; // Counter for data bits uint8_t ir_data[IR_FRAME_BYTES];// Ir data buffer uint8_t ir_data_ptr; bit ir_data_ready; // 1-Data be ready /********************************** PIN DEF **********************************/ #define STANDBY _pc0 #define LOCK _pd0 /******************************** STANDBY DEF ********************************/ uint8_t pwr_repeat_cnt; bit pwr_flag; /********************************** CMD DEF **********************************/ #define I2C_CMD_LED 0x80 // Led Refresh Command #define I2C_CMD_LOCK 0x40 // LOCK Refresh Command uint8_t i2c_cmd; // Commandword: rx_data[0] /********************************** LED DEF **********************************/ #define LED_NUM 4 #define LED_SET_DATA(x) do{_pbc = 0x00; _pb = x;}while(0) #define LED_EN_COM(x) do{_pa |= 0x0f; _pa &= ~(1<<(x));}while(0) #define LED_DIS_ALL_COMS() do{_pa |= 0x0f;}while(0) bit lock_status; // LOCK-Led status: rx_data[1] uint8_t led_data[LED_NUM]; // Led display data buffer: rx_data[1:4] uint8_t refresh_timeslice; // Refresh timeslice /********************************** I2C DEF **********************************/ #define I2C_SLAVE_ADDR 0x0c #define RX_LEN (LED_NUM+1) // commandword + data[0] + data[1] + data[2] + data[3] uint8_t tx_data; uint8_t rx_data[RX_LEN]; uint8_t rx_data_ptr; /***************************** interrupt vector ******************************/ #pragma vector isr_ext @ 0x4 #pragma vector isr_tmr @ 0x8 #pragma vector isr_i2c @ 0x10 /******************************* PRIVATE FUNC ********************************/ uint8_t byte_reverse(uint8_t dat) { /* Convert bit[7:0] to bit[0:7] */ uint8_t i; uint8_t src, dsn; src = dat; dsn = src & 0x1;; for(i = 0; i < 7; i++) { src >>= 1; dsn <<= 1; dsn |= src & 0x1; } return dsn; } /************************************ ISR ************************************/ void isr_ext() { /* External ISR */ /* Interval between two external interrupt triggered by falling edge of ir-signal. */ uint8_t int_interval; int_interval = tmr_ir_decode; tmr_ir_decode = 0; //decode ir signal if ((int_interval >= (TMR_IR_1_nTIMES-TMR_IR_SCALE)) && (int_interval <= (TMR_IR_1_nTIMES+TMR_IR_SCALE))) { // Code 1 if (ir_data_ptr == IR_FRAME_BYTES) ir_data_ptr = 0; ir_data[ir_data_ptr] = (ir_data[ir_data_ptr]<<1) + 1; ir_bits_cnt++; if ((ir_bits_cnt%8) == 0) ir_data_ptr++; } else if ((int_interval >= (TMR_IR_0_nTIMES-TMR_IR_SCALE)) && (int_interval <= (TMR_IR_0_nTIMES+TMR_IR_SCALE))) { // Code 0 if (ir_data_ptr == IR_FRAME_BYTES) ir_data_ptr = 0; ir_data[ir_data_ptr] = (ir_data[ir_data_ptr]<<1); ir_bits_cnt++; if ((ir_bits_cnt%8) == 0) ir_data_ptr++; } else { // Other, invalid signal, reset ir_data_ptr = 0; ir_bits_cnt = 0; ir_data_ready = 0; } if (ir_bits_cnt == IR_FRAME_BITS) { // Receive over, set ir_data_ready ir_data_ptr = 0; ir_bits_cnt = 0; ir_data_ready = 1; } } void isr_tmr() { /* Timer ISR, time 0.25ms */ // Check ir repeat. Repeat interval=0.25ms*200*6 tmr_ir_repeat++; if (tmr_ir_repeat > 200) { // time 50ms, 50ms = 200*0.25ms tmr_ir_repeat = 0; if (pwr_flag) { pwr_repeat_cnt++; if( pwr_repeat_cnt >= 6 ) pwr_flag = 0; } } // Timeslice for Led & Keypad refresh tmr_led_refresh++; if (tmr_led_refresh > 8) { // time 2ms, 2ms = 8*0.25ms tmr_led_refresh = 0; refresh_timeslice++; refresh_timeslice %= (LED_NUM+1); } // Interval for ir decoding. When ir signal coming, read it then reset it. tmr_ir_decode++; } void isr_i2c() { if(_haas == 1) { // Address Match if(_srw == 1) { // Transmit Mode _htx = 1; _hdr = tx_data; } else { // Receive Mode _htx = 0; _txak = 0; rx_data[rx_data_ptr] = _hdr; } } else { // Transfer Completed if(_htx == 1) { // Transmitter. Continue to Transimit or Not if(_rxak == 1) { _htx = 0; // _rxak=1, NO ACK _txak = 0; rx_data[rx_data_ptr] = _hdr; // Dummy read from hdr } else { _hdr = tx_data; // _rxak=0; with ACK } } else { // Receiver ,htx=0 rx_data[rx_data_ptr] = _hdr; rx_data_ptr++; if (rx_data_ptr == RX_LEN) { // Reveive over rx_data_ptr = 0; // rx_data[0]is command word i2c_cmd = rx_data[0]; switch (i2c_cmd) { case I2C_CMD_LED: // Refresh LED command,save display data led_data[0] = rx_data[1]; led_data[1] = rx_data[2]; led_data[2] = rx_data[3]; led_data[3] = rx_data[4]; break; case I2C_CMD_LOCK: // Refresh LOCK command, save lock_status lock_status = rx_data[1]; break; } _txak = 1; // end of receive } else { // Continue to Receive _txak = 0; } } } } /********************************* FUNCTION **********************************/ void init_hw() { /* intc0: +------------------------------------------------------+ |bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | +------------------------------------------------------+ | 0 | ADF | TF | EIF | EADI | ETI | EEI | EMI | +------------------------------------------------------+ */ _intc0 = 0; /* intc1: +------------------------------------------------------+ |bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | +------------------------------------------------------+ | 0 | 0 | 0 | HIF | 0 | 0 | 0 | EHI | +------------------------------------------------------+ */ _intc1 = 0; /* tmrc: +------------------------------------------------------+ |bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | +------------------------------------------------------+ | TM1 | TM0 | 0 | TON | TE | PSC2 | PSC1 | PSC0 | +------------------------------------------------------+ TM[1:0] = 10, timer mode,using internal fsys PSC[2:0]= 100, PSC=16 */ _tmrc = 0x84; _tmr = TMR_nMS; /* hcr: +------------------------------------------------------+ |bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | +------------------------------------------------------+ | HEN | 0 | 0 | HTX | TXAK | 0 | 0 | 0 | +------------------------------------------------------+ */ _hcr = 0; /* _pa[0:3]-->out, LED_COM[1:4] _pa4 -->out, CTRL _pa5 -->1, using for INT_ _pa[6:7]-->11, using for i2c */ _pac = 0x20; /* _pb[0:7]-->out, LED */ _pbc = 0x00; /* _pc0 -->out, STANDBY _pc1 -->out, REQ */ _pcc = 0x00; /* _pd0 -->out, LOCK */ _pdc = 0x00; _hadr = I2C_SLAVE_ADDR; _htx = 0; // Set i2c bus to be receive mode _txak = 0; _hen = 1; // Enable i2c bus _ehi = 1; // Enable i2c interrupt _eti = 1; // Enable timer interrupt _eei = 1; // Enable external interrupt _emi = 1; // Enable global interrupt _ton = 1; // Start timer } void init_sys() { LOCK = 0; // LOCK-Led off STANDBY = 1; // PWR-Led on ir_data_ptr = 0; ir_data_ready = 0; ir_bits_cnt = 0; tmr_ir_decode = 0; tmr_ir_repeat = 0; tmr_led_refresh = 0; pwr_flag = 0; pwr_repeat_cnt = 0; tx_data = 0xff; rx_data_ptr = 0; // Leds display "boot" when system booting led_data[0] = 0x7c; led_data[1] = 0x5c; led_data[2] = 0x5c; led_data[3] = 0x78; i2c_cmd = 0xff; lock_status = 0; refresh_timeslice = 0; } void main() { uint8_t ir_custom_code[4]; uint8_t ir_keyvalue_pwr[2]; init_hw(); init_sys(); ir_custom_code[0] = byte_reverse(IR_CUSTOM_CODE1); ir_custom_code[1] = byte_reverse(IR_CUSTOM_CODE2); ir_custom_code[2] = byte_reverse(IR_CUSTOM_CODE3); ir_custom_code[3] = byte_reverse(IR_CUSTOM_CODE4); ir_keyvalue_pwr[0] = byte_reverse(IR_KEYVALUE_PWR1); ir_keyvalue_pwr[1] = byte_reverse(IR_KEYVALUE_PWR2); while (1) { if (refresh_timeslice < LED_NUM) { // Leds's turn, refresh Led display LED_EN_COM(refresh_timeslice); LED_SET_DATA(led_data[refresh_timeslice]); } else { // Keypad's turn, read keypad value LED_DIS_ALL_COMS(); // First disable all Leds _pb = 0xff; // Pull-High port(b) _pbc= 0x7e; // Set port(b) to be input mode tx_data = _pb; // Save status of port(b) to tx_data // Refresh LOCK-Led's status LOCK = lock_status; } if (ir_data_ready) { // Keycode is valid? if ((ir_data[0] == ir_custom_code[0]) && (ir_data[1] == ir_custom_code[1]) &&(ir_data[2] == ir_custom_code[2]) && (ir_data[3] == ir_custom_code[3]) &&(ir_data[4] == ir_keyvalue_pwr[0])&& (ir_data[5] == ir_keyvalue_pwr[1])) { // Check whether Key-PWR is pressed repeatedly. if (pwr_flag == 0) { STANDBY = ~STANDBY; if (STANDBY == 1) { // System on,Leds display "boot" led_data[0] = 0x7c; led_data[1] = 0x5c; led_data[2] = 0x5c; led_data[3] = 0x78; } else { // System off,Leds display "----",LOCK-Led off led_data[0] = led_data[1]= led_data[2] = led_data[3] = 0x40; lock_status = 0; } } pwr_flag = 1; pwr_repeat_cnt = 0; } ir_data_ready = 0; } } }