UART:
UART:Universal Asynchronous Receiver/Transmitter,通用异步接收/发送装置
异步
不知道数据什么时候来
接线
SOC和PC等设备都是TTL电平
0V 为 0
3.3V/5V为1
电平转换模块的作用:
因为串口用于远距离通信,会影响到电路的电压,如果还是如上面这样采用TTL电平, 就会出现问题;
以下是常用的两种总线:
R232总线
距离25米以内,全双工;每根线,大小在正负3-15V之间
R485总线
距离1200米,半双工;通过两根线的电压差表示大小
如何发送数据
- 信号协议(数据位、停止位、奇偶校验)
- 一定时序(波特率)
- 首先进行电平的装换,通知接收设备准备接收数据
一些经验之谈
- 串口接收,采用中断,尽量不在中断里处理数据,最好用一个大的队列来保存接收的数据,如果硬件支持就用硬件的,否则就自己搭一个软件FIFO;
- 串口发送,根据需求选择是否采用中断,如果采用中断,就像上面一样采用队列存储数据,依次发送;
如果直接发送,需要确保在发送过程中,不被打断/抢占,导致发送丢包,或者出错(如,前面发送010203040506,发送了010203,然后中断发送070809101112;实际发送就是010203-070809101112-040506)
FIFO
uart.h
//缓存最大值,最多一次性可接收的字节数
#define UART_QUEUE_MAX_SIZE 80
//数据帧起始或结束数据
#define UART_QUEUE_START_DATA 0XAA
#define UART_QUEUE_END_DATA 0X55
#define UART_QUEUE_START_LENGHT 0X10
//数据帧长度
#define UART_QUEUE_DATA_FRAME_LENGTH 6
typedef struct uart_cmd_queue_
{
unsigned int head; // 队列FIFO头
unsigned int tail; // 队列FIFO尾
unsigned char dat[HMI_QUEUE_MAX_SIZE]; // 数据缓存区
}UART_CMD_QUEUE_STRUCT;
uart.c
/* 队列FIFO */
UART_CMD_QUEUE_STRUCT m_que;
/**
* @brief 串口接收队列复位
* @param None
* @retval None
*/
void Uart_Queue_Reset(void)
{
m_que.head = 0;
m_que.tail = 0;
}
/**
* @brief 存入一个数据到串口接收队列(FIFO)中
* @param 存入的数据
* @retval None
*/
void Uart_Queue_Push(unsigned char l_dat)
{
unsigned int pos;
pos = (m_que.tail + 1) % UART_QUEUE_MAX_SIZE;
/*非满状态*/
if(pos != m_que.head)
{
m_que.dat[m_que.tail] = l_dat;
m_que.tail = pos;
}
}
/**
* @brief 从串口接收队列(FIFO)中取出一个字节数据
* @param 取出的数据
* @retval None
*/
void Uart_Queue_Pop(unsigned char *l_dat)
{
unsigned int i;
/*非空状态*/
if(m_que.head != m_que.tail)
{
i = m_que.head;
*l_dat = m_que.dat[i];
m_que.head = (m_que.head+1) % UART_QUEUE_MAX_SIZE;
}
}
/**
* @brief 获取串口接收队列(FIFO)的长度
* @param None
* @retval 接收队列(FIFO)的长度
*/
unsigned int Uart_Queue_Size(void)
{
return ((m_que.tail + UART_QUEUE_MAX_SIZE - m_que.head) % UART_QUEUE_MAX_SIZE);
}
/**
* @brief 找出完整一帧指令
* @param 完整一帧指令
* @param 缓存区大小
* @retval 完整指令长度
*/
unsigned int Uart_Queue_Find_Cmd(unsigned char *buf)
{
unsigned int cmd_size = 0;
unsigned char l_dat = 0;
/* 队列指针 */
static unsigned int m_cmd_pos = 0;
/*取一个数据*/
Hmi_Queue_Pop(&l_dat);
switch(m_cmd_pos)
{
case 0:
{
/*长度出错,跳过*/
if(l_dat != UART_QUEUE_START_DATA)
{
m_cmd_pos = 0;
continue;
}
buf[m_cmd_pos] = l_dat;
m_cmd_pos = 1;
break;
}
default:
{
if(m_cmd_pos < UART_QUEUE_DATA_FRAME_LENGTH)
{
buf[m_cmd_pos] = l_dat;
}
else
{
/*得到完整的帧尾*/
cmd_size = m_cmd_pos;
m_cmd_pos = 0;
if(l_dat == UART_QUEUE_END_DATA)
{
return cmd_size;
}
}
m_cmd_pos++;
break;
}
}
return 0; //没有形成完整的一帧
}