单片机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)
{
...
}
}