1、通讯基础
串行通讯与并行通讯
-
串行通信
设备之间通过少量数据信号线(一般是 8 根以下),地线以及控制信号线,按数据位形式一位一位地传输数据。
同一时刻只能传输一个数据位的数据
-
并行通讯
使用 8、16、32 及 64 根或更多的数据线进行传输的通讯方式
可同时传输多个数据位的数据
全双工、半双工及单工通讯
-
全双工
在同一时刻,两个设备之间可以同时收发数据
-
半双工
两个设备之间可以收发数据,但不能在同一时刻进行
-
单工
在任何时刻都只能进行一个方向的通讯,即一个固定为发送设备,另一个固定为接收设备
同步通讯、异步通讯
-
同步通讯
收发设备双方使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据
通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样
-
异步通讯
不使用时钟信号进行数据同步,直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据
某些通讯中需要双方约定数据的传输速率,以便更好地同步
通讯速率
-
比特率(Bitrate)
每秒传输的二进制位数,单位
bis/s
-
波特率(Baudrate)
每秒钟传输的码元
通讯中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元
常见波特率
4800,9600,115200
2、USART串口通讯
协议层
-
起始和停止信号
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示, 只要双方约定一致即可。
-
有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长
-
数据校验
在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验 (odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。
奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为: 01101001,此时总共有 4 个“1”,为达到奇校验效果,校验位为“1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位。
偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据 帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。
0 校验是不管有效数据中的内容是什么,校验位总为“0”,1 校验是校验位总为“1”。
U(S)ART
-
UART:通用异步收发器
-
USART:通用同步异步收发器
-
串口通信一般以帧格式传输数据,每帧包含起始信号、数据信息、停止信息,可选校验信息
-
中断控制
3、USART初始化结构体
-
USART初始化结构体
typedef struct { uint32_t USART_BaudRate; //波特率 uint16_t USART_WordLength; //数据帧字长 uint16_t USART_StopBits; //停止位 uint16_t USART_Parity; //校验位 uint16_t USART_Mode; //USART模式 uint16_t USART_HardwareFlowControl; //硬件流控制 } USART_InitTypeDef;
-
USART_BaudRate
一般设置2400,9600,19200,115200
-
USART_WordLength
USART_WordLength_8b,USART_WordLength_9b
-
USART_StopBits
USART_StopBits_1,USART_StopBits_0_5,USART_StopBits_2,USART_StopBits_1_5
-
USART_Parity
USART_Parity_No,USART_Parity_Even,USART_Parity_Odd
-
USART_Mode
USART_Mode_Rx,USART_Mode_Tx
,可通过逻辑|
设定为收发模式 -
USART_HardwareFlowControl
USART_HardwareFlowControl_None,USART_HardwareFlowControl_RTS,USART_HardwareFlowControl_CTS,USART_HardwareFlowControl_RTS_CTS
-
-
USART时钟初始化结构体
使用同步模式时需要配置SCLK引脚输出脉冲的属性
typedef struct { uint16_t USART_Clock; //同步模式下SCLK引脚上时钟输出使能控制 uint16_t USART_CPOL; //时钟极性 uint16_t USART_CPHA; //时钟相位 uint16_t USART_LastBit; //最尾位时钟脉冲 } USART_ClockInitTypeDef;
-
USART_Clock
USART_Clock_Disable,USART_Clock_Enable
-
USART_CPOL
USART_CPOL_Low,USART_CPOL_High
-
USART_CPHA
USART_CPHA_1Edge,USART_CPHA_2Edge
-
USART_LastBit
USART_LastBit_Disable,USART_LastBit_Enable
-
4、USART1串口配置
-
GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
-
在所有中断前都要设置中断分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
-
串口时钟使能,GPIO时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
-
串口复位
一般系统刚开始配置外设时会先执行复位
USART_DeInit(USART1);
-
GPIO端口模式设置
//USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9 //USART1_RX GPIOA.10初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
-
串口参数初始化
USART_InitStructure.USART_BaudRate = bound;//串口波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1
-
初始化NVIC
//Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
-
开启串口中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
-
使能串口
USART_Cmd(USART1, ENABLE); //使能串口1
5、发送与接收
发送数据
-
发送单个字节
可发送字符或者数字
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) { /* 发送一个字节数据到USART */ USART_SendData(pUSARTx,ch); /* 等待发送数据寄存器为空 */ while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); }
Usart_SendByte(USART1, ch);
-
发送8位的数组
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num) { uint8_t i; for(i=0; i<num; i++) { /* 发送一个字节数据到USART */ Usart_SendByte(pUSARTx,array[i]); } /* 等待发送完成 */ while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); }
-
发送字符串
void Usart_SendString( USART_TypeDef * pUSARTx, char *str) { unsigned int k=0; do { Usart_SendByte( pUSARTx, *(str + k) ); k++; } while(*(str + k)!='\0'); /* 等待发送完成 */ while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET) {} }
-
发送一个16位数
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch) { uint8_t temp_h, temp_l; /* 取出高八位 */ temp_h = (ch&0XFF00)>>8; /* 取出低八位 */ temp_l = ch&0XFF; /* 发送高八位 */ USART_SendData(pUSARTx,temp_h); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); /* 发送低八位 */ USART_SendData(pUSARTx,temp_l); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); }
-
使用
printf/putchar
首先要包含头文件
#include "stdio.h"
重定向
int fputc(int ch, FILE *f) { /* 发送一个字节数据到串口 */ USART_SendData(DEBUG_USARTx, (uint8_t) ch); /* 等待发送完毕 */ while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return (ch); }
接受数据
-
使用
scanf/getchar
重定向
int fgetc(FILE *f) { /* 等待串口输入数据 */ while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET); return (int)USART_ReceiveData(DEBUG_USARTx); }
-
中断接收
编写中断函数
// 串口中断服务函数 void USART1_IRQHandler(void) { uint8_t ucTemp; if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) { ucTemp = USART_ReceiveData(USART1); // 对数据进行处理 } }