一、数据通信基础
1.串行/并行
并行通信可以进行8,16,32位的通信
串行通信 | 传输速率低,抗干扰能力强,通信距离长,成本低,IO资源少 |
并行通信 | 传输速率高,抗干扰能力弱,通信距离断,成本高,IO资源多 |
2.单工/半双工/全双工
注意:有两个数据线不一定是全双工,一定是要分别有输入输出通道才叫全双工
3.同步/异步通信
4.波特率
比特率 | 每秒钟传送的比特数,即二进制的位数,单位bit/s |
波特率 | 每秒钟传送的码元,单位Baud,例如有9v,6v,3v,0v,我们分别让他们对应1,2,3,4,那么就是四个码元,简单来说,就是信息经过编码后的个数 |
二者关系 | 比特率=波特率∗log2M,M表示每个码元承载的信息量,若是以上面为例子,那么M就是4 |
二进制系统中 | 波特率数值上等于比特率 |
5.常见串行通信
6.串口::串行通信接口,指按位发送和接收的接口,如RS—232/422/485等
7.RS232异步通信协议
二、USART/UART
1.USART和UART的关系
USART:通用同步异步收发器 | UART:通用异步收发器 | 两者都可以与外部设备进行全双工通信 |
2.框图分析
3.简化之后的框图
4.波特率计算公式
5.HAL库配置步骤
#ifndef __USART_H
#define __USART_H
#include "./SYSTEM/sys/sys.h"
extern UART_HandleTypeDef huart1;
extern uint8_t g_usart1_rx_flag;//串口接收到数据标志 0表示未接受到
extern uint8_t g_rx_buffer[1];//HAL库使用的串口接收缓冲区
void USART_Init(uint32_t BaudRate);
#define RXBUFFERSIZE 1
#define USART_REC_LEN 200
extern uint16_t g_usart_rx_sta;
/* 接收缓冲, 最大USART_REC_LEN个字节. */
extern uint8_t g_usart_rx_buf[USART_REC_LEN];
#endif
UART_HandleTypeDef huart1;
uint8_t g_usart1_rx_flag=0;//串口接收到数据标志 0表示未接受到
uint8_t g_rx_buffer[1];//HAL库使用的串口接收缓冲区
uint16_t g_usart_rx_sta=0;
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART1->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
void USART_Init(uint32_t BaudRate)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = BaudRate;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;//校验位
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;//硬件流
//huart1.Init.OverSampling = //过采样,F1系列没有这个东西
HAL_UART_Init(&huart1);
HAL_UART_Receive_IT(&huart1, g_rx_buffer, 1);//开启接收中断
}
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Init;
if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
//TX
GPIO_Init.Pin = GPIO_PIN_9;
GPIO_Init.Mode = GPIO_MODE_AF_PP;//输出,并且根据原理图可知并没有外部上拉,所以开漏输出不能输出高电平,所以这里选择推挽输出
//GPIO_Init.Pull = //输出没有上下拉
GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
//RX
GPIO_Init.Pin = GPIO_PIN_10;
GPIO_Init.Mode = GPIO_MODE_AF_INPUT;//输入
GPIO_Init.Pull = GPIO_PULLUP;//根据时序图,空闲时TX为高电平,又因为RX是连接到TX,所以这里用上拉
GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Init);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(USART1_IRQn,0,0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
//中断服务函数
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);//这个函数会清除中断标志位
}
//回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if((g_usart_rx_sta & 0x8000) == 0)//检测第15位,即换行0x0a 还没接收完
{
if(g_usart_rx_sta & 0x4000) //检测第14位,即回车0x0d 接收到了
{
if(g_rx_buffer[0] != 0x0a)//不是换行
{
g_usart_rx_sta = 0; //数据错误,重新接收
}
else
{
g_usart_rx_sta |= 0x8000;//接收完成 即第15位被置1
}
}
else //还没收到回车
{
if(g_rx_buffer[0] == 0x0d)//如果是回车
g_usart_rx_sta |= 0x4000;//就给第14位置1
else//不是回车,就继续接收数据
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];
g_usart_rx_sta++;//0x3fff -> 0011 1111 1111 1111 即0~13位为数据位
if(g_usart_rx_sta>(USART_REC_LEN - 1))
{
g_usart_rx_sta = 0;//接收数据错误,重新开始接收
}
}
}
}
HAL_UART_Receive_IT(&huart1,g_rx_buffer, RXBUFFERSIZE);//每进一次中断,标志位就会被清除,所以回调函数这里就要再开一次接收中断
}
}
回调函数接收数据的思路:
定义一个16位的状态位 g_usart_rx_sta
位0~13是接收到的数据
位14 接收到0x0d即回车
位15 接收到0x0a即换行,就置1,表示数据接收完成了
uint32_t time;
int main(void)
{
uint16_t len;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
USART_Init(115200);
while(1)
{
if(g_usart_rx_sta & 0x8000)//接收完成了
{
len=g_usart_rx_sta & 0x3FFF;//取出数据长度
printf("您发送的消息是:\r\n");
HAL_UART_Transmit(&huart1,g_usart_rx_buf,len,1000);//发送接收到的数据到串口助手
while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET);//发送寄存器为空时结束
printf("\r\n");
g_usart_rx_sta=0;//状态位给0,准备下一次接收
}
else
{
time++;
if(time%500==0)
{
printf("请输入数据\r\n");
}
if(time%100==0)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
delay_ms(10);
}
}
}