模拟串口收发驱动(采用IDLE信号机制)

  • 背景
    之前较早的一篇博文已经介绍了模拟串口的使用:博文地址,距今已经过去几年了,这几天由于工作原因,受限于资源再次需要用到模拟串口。相比之前,这次是收发都需要用到,故在原来基础上添加发送代码,并对原来的框架进行了一定的优化,原来是使用接收控制时间的轮询模式接收数据,但是影响到数据接收实时性,需要考虑到数据长度和波特率,故而想到采用IDLE空闲机制,其信号直接在中断中产生,故直接使用IDLE标志即可读取数据,已经完美实现。

串口数据帧结构
串口数据帧结构1

  • 空闲的定义:
    是总线上在一个字节的时间内没有再接收到数据,空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。

  • 整个框架需要采用的硬件资源 : 外部中断 + 更新定时器,支持含校验位通信参数,空闲信号是从更新定时器产生,可以看作中断信号。

  • 代码实现如下:

#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);			
    }	 
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值