应用时间片实现多任务的单片机代码

时间:2022-04-26 08:03:33
要实现的功能模块有四个: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;
        }
        
    }
    
}