单片机GPIO中断+定时器 实现模拟串口接收
- 解决思路
- 代码示例
解决思路
串口波特率9600bps,每个bit约为1000000us/9600=104.16us; 定时器第一次定时时间设为52us即半个bit的时间,其目的是偏移半个bit时间,之后的每104us采样并读取1bit数据。使得采样点搞好在每位数据脉宽的中间点。

代码示例
- 串口波特率9600bps,每个bit约为1000000us/9600=104.16us;
- 定时器开始时定时时间设为52us,即半个bit的时间,并关闭定时器;
- GPIO设为中断模式下降沿触发,测到第一个下降沿(即串口的S信号),定时器设定52us开启定时,关闭GPIO中断,并设置状态为E_IO_UART_STATE_IDLE。
- 定时器触发52us定时E_IO_UART_STATE_IDLE状态改为E_IO_UART_STATE_START。定时器设置为104us,之后每触发一次104us定时中断读取1bit串口数据,直到8bits数据全部解析完成,重置为下次接收状态;
#if TCFG_SENSOR_CO_MODULE_EN
struct io_uart_t {
    uint8_t state;
    uint8_t index;
    uint8_t _data;
    uint8_t ready;
    uint8_t rxlen;
    uint8_t _recv[7];
};
static struct io_uart_t io_uart;
void timer1_init(void);
#endif
#if TCFG_SENSOR_CO_MODULE_EN
enum {
    E_IO_UART_STATE_IDLE=0,     // 空闲状态
    E_IO_UART_STATE_START,      // 开始接收数据
    E_IO_UART_STATE_END,        // 数据接收完成
};
#define TIMER1_TICK_HEAD         60  // 52us    104/2=52us  半个开始信号的时间
#define TIMER1_TICK_DATA         139 // 104us   1000000/9600=104.16us 一个数据位的时间
void timer1_init(void)
{
    SCCM1 |= RCC_SCCM1_TIMER1;
    TMOD  |=Bit5_En ;
    TMOD  &=Bit4_Dis;
    TH1=256-TIMER1_TICK_HEAD;        
    TL1=256-TIMER1_TICK_HEAD; 
   
    TR1=0;        //定时器1使能运行
    EAL=1;        //总中断打开
    ET1=1;        //定时器1中断打开
    IRQ_Vic_Set(INT_TIMER1, pritrity_level_fourth);     // 设置定时器优先级最高
    // IRQ_Enable(IT_ALL);
}
void Interrupt_GPIO0 (void) interrupt 20    //GPIO0中断服务程序
{
    // P07=~P07;
    if(IO_GetIntState(GPIO_P0,GPIO_Pin_6))
    {
        // P07=~P07;    // for test
        // TODO: 检测到第一个下降沿:S信号,设置定时器初值52us,启动定时器,关闭外部中断
        TH1=256-TIMER1_TICK_HEAD;  
        TL1=256-TIMER1_TICK_HEAD;
        TR1=1;
        io_uart.state = E_IO_UART_STATE_IDLE;
        IO_INT_Disable(  GPIO_P0,GPIO_Pin_6);
        // IRQ_Disable(IT_GPIO0);
        IO_CleanIntState(GPIO_P0,GPIO_Pin_6);
    }
    P0_INT_REG = 0xff;
}
/*中断方式*/
void Interrupt_TIMRT1 (void) interrupt 3    //TIMRT1中断服务程序
{	
    uint8_t checksum = 0;
	TF1 = 1; //清标志
	// TIMER01_SetPeriod(TIMER1,TIMERMODE_VALUE,TIMER1_TICK);
	// P07=~P07;   // for test
    if (io_uart.state == E_IO_UART_STATE_START) {
        P07=~P07; // FOR TEST
        io_uart._data = io_uart._data >> 1; // 数据右移一位
        if (P06 == 1) { 
            io_uart._data |= 0x80;    // 如读取到的串口GPIO电平为高,高位位或运算
        }
        io_uart.index++;
        if (io_uart.index == 8) {
            io_uart.state = E_IO_UART_STATE_END;
            IO_INT_Enable(  GPIO_P0,GPIO_Pin_6);
            // IRQ_Enable(IT_GPIO0);
            TR1=0;
            if (io_uart._data == 0xAA) {
                io_uart.rxlen = 0;
                memset(io_uart._recv, 0, sizeof(io_uart._recv));
            }
            io_uart._recv[io_uart.rxlen] = io_uart._data;
            io_uart.rxlen++;
            if (io_uart.rxlen == 7)
            {
                checksum = (uint8_t)(io_uart._recv[1]+io_uart._recv[2]+io_uart._recv[3]+io_uart._recv[4]);
                if (checksum == io_uart._recv[5]) {
                    // TODO: 和校验正确
                    // P07=~P07;   // for test
                    _this->sensor_co.covol = (uint16_t)(io_uart._recv[1]<<8 | io_uart._recv[2]);
                    io_uart.ready = 1;
                }
            }
        }
    }
    
    if (io_uart.state == E_IO_UART_STATE_IDLE) {
        io_uart.state = E_IO_UART_STATE_START;
        io_uart.index = 0;
        io_uart._data = 0;
        TH1=256-TIMER1_TICK_DATA;        
        TL1=256-TIMER1_TICK_DATA;
    }
}
#endif
void main ()
{
#if TCFG_SENSOR_CO_MODULE_EN
    timer1_init();
    
    RCC_Sccm1_ClockCmd(RCC_SCCM1_GPIO,ENABLE);
    IO_FUN_Config(GPIO_P0,GPIO_Pin_7,GPIO_FUNCTION_DF0); //配置引脚为GPIO功能    	
    IO_OUT_Enable(GPIO_P0, GPIO_Pin_7);
    IO_PU_Enable(GPIO_P0, GPIO_Pin_7);
    // TOODO: 模拟串口接收
	/*只有P0和P1口可以配置为电平触发,其他端口只能配置为沿触发*/
	IO_FUN_Config( 	GPIO_P0,GPIO_Pin_6,GPIO_FUNCTION_DF0); //配置引脚为GPIO功能
    IO_PU_Enable(   GPIO_P0,GPIO_Pin_6);
	IO_INPUT_Enable(GPIO_P0,GPIO_Pin_6);            //配置引脚为GPIO输入模式
	IO_INT_Config(  GPIO_P0,GPIO_Pin_6, falling);	//需要外接接下拉电阻	
	IO_INT_Enable(  GPIO_P0,GPIO_Pin_6);
	IRQ_Enable(IT_GPIO0);
#endif
while(1)
{
	...
}
}



















