- 背景
之前较早的一篇博文已经介绍了模拟串口的使用:博文地址,距今已经过去几年了,这几天由于工作原因,受限于资源再次需要用到模拟串口。相比之前,这次是收发都需要用到,故在原来基础上添加发送代码,并对原来的框架进行了一定的优化,原来是使用接收控制时间的轮询模式接收数据,但是影响到数据接收实时性,需要考虑到数据长度和波特率,故而想到采用IDLE空闲机制,其信号直接在中断中产生,故直接使用IDLE标志即可读取数据,已经完美实现。
-
空闲的定义:
是总线上在一个字节的时间内没有再接收到数据,空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。 -
整个框架需要采用的硬件资源 : 外部中断 + 更新定时器,支持含校验位通信参数,空闲信号是从更新定时器产生,可以看作中断信号。
-
代码实现如下:
#ifndef __VUART_H__
#define __VUART_H__
/*模拟串口逻辑实现框架*/
typedef enum
{
STATE_START = 0, /* startbit*/
STATE_BIT0,
STATE_BIT1,
STATE_BIT2,
STATE_BIT3,
STATE_BIT4,
STATE_BIT5,
STATE_BIT6,
STATE_BIT7,
STATE_BITC, /*校验位*/
STATE_STOP /* stopbit*/
} rxsta_em; //数据枚举状态位
typedef enum
{
NONE = 0, //无
ODD, //奇校验
EVEN, //偶校验
} ck_em; //校验选择枚举
typedef struct
{
unsigned short int baudrate; //波特率
ck_em ck_type; //校验类型
} config_st; //串口信息配置结构体
typedef struct
{
rxsta_em state; //
} bit_rx_st; //数据位结构体
typedef struct
{
bit_rx_st bit_rx;
config_st config;
void (*bit_write)(unsigned char);
unsigned char (*bit_read)(void);
void (*gpio_init)(void);
void (*recv_open)(unsigned char);
void (*bit_timer_open)(unsigned char);
void (*bit_delay)(unsigned short int);
} vuart_st; //虚拟串口信息结构体
extern vuart_st vuart;
#define IDLE_MODE (0)
#define VUART_MAX_SIZE (32) //虚拟串口最大接收缓存字节数
extern unsigned char VUART_RX_SIZE ;
extern unsigned char VUART_RX_BUF[VUART_MAX_SIZE];
/*模拟串口驱动底层接口API*/
void vuart_bsp_init(void);
void vuart_recv_open(unsigned char );
void vuart_bit_timer_open(unsigned char );
void vuart_bit_write(unsigned char);
unsigned char vuart_bit_read(void);
void vuart_bit_delay(unsigned short int );
/*模拟串口应用API*/
void vuart_init(vuart_st *const me);
void vuart_parameter_config(vuart_st *const me,unsigned short int baudrate,ck_em ck_type);
void vuart_sendbyte(vuart_st *const me,unsigned char );
void vuart_sendbytes(vuart_st *const me,unsigned char *, unsigned int );
/*虚拟串口缺省配置参数*/
#define VUART_DEFAULT_CFG \
{ \
.bit_rx.state = STATE_STOP,\
.config.baudrate = 4800,\
.config.ck_type = ODD,\
.recv_open = vuart_recv_open,\
.gpio_init = vuart_bsp_init,\
.bit_read = vuart_bit_read,\
.bit_write = vuart_bit_write,\
.bit_delay = vuart_bit_delay, \
.bit_timer_open = vuart_bit_timer_open,\
}
#endif
- C文件
#include "vuart.h"
#include "gd32_timer.h"
#include "string.h"
#include "delay.h"
unsigned char VUART_RX_SIZE = 0;//接收字节数计数
unsigned char VUART_RX_BUF[VUART_MAX_SIZE] = {0};//接收缓存区
vuart_st vuart=VUART_DEFAULT_CFG;//缺省配置。
/*
**虚拟串口的GPIO配置
*/
void vuart_bsp_init(void)
{
/* enable the clock */
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_CFGCMP);
/* configure GPIO as input */
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_6);//上拉输入。
/* configure GPIO as output */
gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
gpio_bit_set(GPIOF,GPIO_PIN_7);//初始状态必须拉高(STOP状态信号),否则第一发送帧错误。
}
/*
**虚拟串口接收打开(使能外部中断)
*/
void vuart_recv_open(unsigned char set)
{
/* connect EXTI line to GPIO pin */
syscfg_exti_line_config(EXTI_SOURCE_GPIOF, EXTI_SOURCE_PIN6);
/* configure EXTI line */
exti_init(EXTI_6, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/* enable and set EXTI interrupt */
set?nvic_irq_enable(EXTI4_15_IRQn, 0U, 0U):nvic_irq_disable(EXTI4_15_IRQn);
exti_interrupt_flag_clear(EXTI_6);
}
/*
**数据位bit定时器
*/
void vuart_bit_timer_open(unsigned char set)
{
//开启定时器前,必须清除当前定时值,防止干扰时序。
set?\
(timer_counter_value_config(TIMER2,0),timer_enable(TIMER2)):\
timer_disable(TIMER2);
}
/*
**bit数据写
*/
void vuart_bit_write(unsigned char set)
{
set?\
gpio_bit_set(GPIOF, GPIO_PIN_7):\
gpio_bit_reset(GPIOF,GPIO_PIN_7);
}
/*
**bit数据读
*/
unsigned char vuart_bit_read(void)
{
return gpio_input_bit_get(GPIOF,GPIO_PIN_6);
}
/*
** bit时宽
*/
void vuart_bit_delay(unsigned short int baudrate)
{
unsigned short int bit_time =0;
switch (baudrate)
{
case 2400:
bit_time = (792); //---bit/417us
break;
case 4800:
bit_time =(398); //---bit/209us[396]
break;
case 9600:
bit_time =(198); //---bit/106us
break;
}
xdelay_us(bit_time);
}
/*
** 模拟串口通讯参数配置
*/
void vuart_parameter_config(vuart_st *const me,unsigned short int baudrate,ck_em ck_type)
{
me->config.baudrate = baudrate;
me->config.ck_type = ck_type;
}
/*
**模拟串口配置初始化
*/
void vuart_init(vuart_st *const me)
{
me->gpio_init();
switch (me->config.baudrate)
{
case 2400:
timer2_init(417); //---bit/417us
break;
case 4800:
timer2_init(210); //---bit/209us
break;
case 9600:
timer2_init(106); //---bit/106us
break;
}
me->bit_timer_open(DISABLE); //使能数据位计时器
me->recv_open(ENABLE); //打开串口(开启外部中断,开始接收数据)
}
/*
** 求整数中bit为1的个数
*/
unsigned char bit_count (unsigned char dat)
{
unsigned char nvz_count=0 ;
while (dat)
{
nvz_count++ ;
dat &= (dat - 1) ;
}
return nvz_count ;
}
/*
** 模拟串口发送单字节数据
*/
void vuart_sendbyte(vuart_st *const me,unsigned char byte_dat)
{
/* 串口协议时序图:
_B0_ __ _P_
| | | | |C |
|_S_| |_//_|_B7_|K_|
*/
const unsigned short int baudrate=me->config.baudrate;
/*发送数据禁止开启接收*/
__set_PRIMASK(1);//关闭总中断
//1.START_STATE
me->bit_write(0); //起始位,拉低电平
me->bit_delay(baudrate);//bit延时时长
//2.BITx_STATE
for (unsigned char i = 0; i < 8; i++) // 8位数据位
{
//LSB First
((byte_dat>>i) & 0x01) ? (me->bit_write(1)) : (me->bit_write(0));
me->bit_delay(baudrate);//bit延时时长
}
//3.BITC_STATE
const unsigned char nzv_num = bit_count(byte_dat);
switch(me->config.ck_type)
{
case EVEN://偶校验
//字节数据含有奇数个"1",校验位置高,反之置低
(nzv_num%2)?me->bit_write(1):me->bit_write(0);
me->bit_delay(baudrate);//bit延时时长
break;
case ODD://奇校验
//字节数据含有奇数个"1",校验位置低,反之置高
(nzv_num%2)?me->bit_write(0):me->bit_write(1);
me->bit_delay(baudrate);//bit延时时长
break;
case NONE:
break;
}
//4.STOP_STATE
me->bit_write(1); //停止位,拉高电平
me->bit_delay(baudrate);//bit延时时长
/*发送完成开启接收*/
__set_PRIMASK(0);//打开总中断
}
/*
** 模拟串口发送多字节
*/
void vuart_sendbytes(vuart_st *const me,unsigned char *tx_buf, unsigned int tx_len)
{
for (unsigned int i = 0; i < tx_len; i++)
{
vuart_sendbyte(me,tx_buf[i]);
}
}
/*
**字节数定时接收
*/
void vuart_byte_recv(vuart_st *const me) //定时读取
{
static unsigned char bit_num;
static unsigned char byte_value;//一字节数据
static unsigned char byte_nzv_num;//字节数据中含1的个数
static unsigned char byte_ck_pass; //数据校验成功标志
static unsigned char bus_idle_timer;//总线空闲时间计时
static unsigned char byte_xfer_over_state;//单字节传输完毕状态
//--START_BIT->BIT0->BIT1->BIT2->BIT3->BIT4->BIT5->BIT6->BIT7->BITC->STOP_BIT->START_BIT
if (STATE_STOP > me->bit_rx.state) me->bit_rx.state++;//状态位切换
if (STATE_BITC == me->bit_rx.state) //当前已至校验位
{
switch (me->config.ck_type) //---是否有校验
{
case NONE: //无校验
me->bit_rx.state = STATE_STOP; //将校验位直接当作停止位处理
byte_ck_pass = 1;//校验OK
break;
case EVEN: //偶校验
//校验规则判断:字节数据含有奇数个"1",校验位置高,反之置低
if((byte_nzv_num % 2)&&(me->bit_read()))
{
byte_ck_pass = 1;
}
else if((0==byte_nzv_num % 2)&&(0==me->bit_read()))
{
byte_ck_pass = 1;
}
break;
case ODD: //奇校验
//校验规则判断:字节数据含有奇数个"1",校验位置低,反之置高
if((byte_nzv_num % 2)&&(0==me->bit_read()))
{
byte_ck_pass = 1;
}
else if((0==byte_nzv_num % 2)&&(me->bit_read()))
{
byte_ck_pass = 1;
}
break;
default:
break;
}
}
//停止位状态
if (STATE_STOP == me->bit_rx.state) //定时读取209us/bit
{
while(0==me->bit_read()){};//确认为高电平。
/*
**空闲的定义是总线上在一个字节的时间内没有再接收到数据,
**空闲中断是检测到有数据被接收后,
**总线上在一个字节的时间内没有再接收到数据的时候发生的。
*/
if(byte_xfer_over_state)//单字节传输完毕状态
{
if(2<bus_idle_timer++)//超过IDLE标志时间
{
gb_vuart_rx_frame_flag=1;//标记收到一帧数据,根据此全局变量可以在任务/线程中读取数据了!
me->bit_timer_open(DISABLE); //关闭计数时基器
byte_xfer_over_state=0;//清除状态
}
else
{
gb_vuart_rx_frame_flag=0;//清除IDLE标志。
}
}
else
{
if (byte_ck_pass) //数据位检验通过,一个字节完成
{
if (VUART_MAX_SIZE > VUART_RX_SIZE) //未
{
VUART_RX_BUF[VUART_RX_SIZE++] = byte_value; //接收串口数据送入缓存区
}
byte_ck_pass = 0; //清除本次校验通过标志
byte_xfer_over_state=1;//字节传输完毕
}//当前字节数据校验不通过,则放弃之前收到的数据
}
byte_ck_pass = 0;
byte_nzv_num = 0; //重置
byte_value = 0;
return;
}
//---有效数据位[--介于起始位与校验位间--]
if (STATE_START < me->bit_rx.state && STATE_BITC > me->bit_rx.state)
{
byte_xfer_over_state=0;
bit_num = me->bit_rx.state;
if (0 != me->bit_read()) //高电平
{
byte_nzv_num++;
byte_value |= (1 << (bit_num - 1));
}
else //低电平
{
byte_value &= ~(1 << (bit_num - 1));
}
}
}
/*
**数据位状态切换
*/
__inline void vuart_state_handle(vuart_st *const me) //外部中断脉冲
{
//下降沿中断
if (0 == me->bit_read()) //检测到低电平
{
if (STATE_STOP <= me->bit_rx.state) //已超过停止位
{
me->bit_rx.state = STATE_START; //重置为起始位
me->bit_timer_open(ENABLE); //开启bit数据计时器
}
}
}
/*
**模拟串口硬件外部中断位处理
*/
void EXTI4_15_IRQHandler(void) //
{
if (RESET != exti_interrupt_flag_get(EXTI_6))
{
vuart_state_handle(&vuart);
}
exti_interrupt_flag_clear(EXTI_6);
}
/*
**数据位定时中断接收里
*/
void TIMER2_IRQHandler(void) //
{
if(RESET != timer_interrupt_flag_get(TIMER2, TIMER_INT_UP))
{
vuart_byte_recv(&vuart);
/* clear update interrupt bit */
timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
}
}