要实现的功能模块有四个: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;
}
}
}