1.重复概念
串口(USART)是一种通用的同步/异步收发器,可以实现串行通信的功能。串行通信是指数据按位顺序依次传输,相比并行通信,占用的引脚资源少,但速度较慢。串口通信需要有一定的通信协议,即双方约定的数据传输的规则和约定。通常,串口通信的协议包括以下几个要素:
- 波特率:表示每秒钟传输的码元(信号单元)的个数,单位是波特(Baud)。波特率越高,数据传输的速率越快,但也越容易出错。波特率是由串口的波特率寄存器(USART_BRR)控制的,通常可以通过计算或查表的方式得到波特率寄存器的值。
- 数据位:表示每个码元包含的二进制位的个数,一般有5、6、7、8、9等几种选择。数据位越多,可以传输的信息量越大,但也占用的时间越长。
- 奇偶校验位:表示是否对数据位进行奇偶校验,用于检测数据传输是否正确。一般有无校验、奇校验、偶校验三种选择。无校验表示不进行校验,奇校验表示数据位的1的个数为奇数时,校验位为0,否则为1,偶校验则相反。
- 停止位:表示数据传输结束的标志位,可以是1位、1.5位或2位。停止位的位数越多,数据传输的稳定性越高,但速度也越慢。
2.类型解释
串口通信的数据类型主要有两种:字符型和数值型。字符型数据是指按照ASCII码或其他编码方式传输的字符,如字母、数字、符号等。数值型数据是指按照二进制或十六进制等方式传输的数值,如整数、浮点数等。不同的数据类型在串口通信中有不同的处理方式,下面分别介绍。
字符型数据的收发
字符型数据的收发比较简单,只需要调用库函数即可。STM32提供了以下几个库函数:
- void USART_SendData(USART_TypeDef* USARTx, uint16_t Data):用于发送单个字符,第一个参数是串口号,第二个参数是要发送的字符。该函数会将字符写入发送数据寄存器(USART_DR),然后等待发送移位寄存器为空,再将数据逐位发送出去。
- uint16_t USART_ReceiveData(USART_TypeDef* USARTx):用于接收单个字符,参数是串口号。该函数会从接收数据寄存器(USART_DR)读取字符,然后返回给调用者。
- int fputc(int ch, FILE *f):用于重定义标准输出函数,使得可以使用printf等函数向串口发送字符。该函数需要调用USART_SendData函数来实现。
- int fgetc(FILE *f):用于重定义标准输入函数,使得可以使用scanf等函数从串口接收字
- 符。该函数需要调用USART_ReceiveData函数来实现。
3.字符型数据的收发的示例代码
以下是一个使用字符型数据的收发的示例代码,实现了STM32通过串口1和上位机(PC)对话的功能,即将接收到的字符原样返回给上位机。
/* 发送单个字符 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
USART_SendData(pUSARTx, data); // 调用库函数发送数据
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == 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)
{}
}
/* 串口接收中断服务函数 */
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // 判断是否有接收中断
{
ucTemp = USART_ReceiveData(USART1); // 调用库函数接收数据
USART_SendData(USART1, ucTemp); // 调用库函数发送数据
}
4.数值型数据的收发的示例代码
数值型数据的收发比较复杂,因为需要考虑数据的格式和大小端问题。数据的格式是指数据是按照二进制、十进制、十六进制等方式表示的,不同的格式在串口通信中有不同的表示方法。数据的大小端是指数据在内存中的存储顺序,大端模式是指高位字节存放在低地址,低位字节存放在高地址,小端模式则相反。STM32是小端模式的处理器,而串口通信一般是大端模式的,所以在数据收发时需要进行大小端的转换。
数值型数据的收发一般有以下几种方法:
- 使用sprintf和sscanf函数:这种方法可以将数值转换成字符串,然后使用字符型数据的收发函数进行传输。这种方法的优点是简单易用,缺点是占用内存和时间较多,而且不能传输浮点数。
- 使用memcpy和union结构体:这种方法可以将数值拆分成字节,然后逐个字节进行传输。这种方法的优点是占用内存和时间较少,而且可以传输浮点数。
- 使用位运算和移位操作:这种方法可以将数值按位进行操作,然后按照一定的顺序进行传输。这种方法的优点是效率最高,而且可以灵活地控制数据的格式和大小端,缺点是编程较复杂,而且需要注意数据的溢出和符号问题。
/* 发送浮点数 */
void Usart_SendFloat(USART_TypeDef* pUSARTx, float data)
{
uint8_t i;
union
{
float f;
uint8_t c[4];
}u; // 定义一个联合体,用于浮点数和字节数组的转换
u.f = data; // 将浮点数赋值给联合体
for(i = 0; i < 4; i++)
{
Usart_SendByte(pUSARTx, u.c[3-i]); // 逆序发送字节,实现大小端的转换
}
}
/* 接收浮点数 */
float Usart_ReceiveFloat(USART_TypeDef* pUSARTx)
{
uint8_t i;
union
{
float f;
uint8_t c[4];
}u; // 定义一个联合体,用于浮点数和字节数组的转换
for(i = 0; i < 4; i++)
{
u.c[3-i] = USART_ReceiveData(pUSARTx); // 逆序接收字节,实现大小端的转换
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_RXNE) == RESET); // 等待接收完成
}
return u.f; // 返回浮点数
}
串口发送数字
* 参 数:Number 要发送的数字,范围:0~4294967295
* 参 数:Length 要发送数字的长度,范围:0~10
* 返 回 值:无
*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++) //根据数字长度遍历数字的每一位
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0'); //依次调用Serial_SendByte发送每位数字
}
}
5.结语
其实说到底无非就是发送和接收这两个函数,那种类型的发送按照自己的需求进行封装,这样当然最好,当然上面的程序使用或者用来参考也是可以的。