一、通信协议
Tx:数据发送引脚
Rx:数据接收引脚
波特率:每秒钟最多传输多少位
常用的波特率:9600、115200、921600
例子1:通过串口发送十进制数字27
例子2:发送字符串“Hello”
ASCII码:H-0x48(0100 1000) e-0x65(0110 0101) l-0x6c(0110 1010) o-0x6f(0110 1111)
二、USART模块的使用方法
三、初始化IO引脚
点击查看代码
#include "stm32f10x.h"int main(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启时钟USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate = 115200; //波特率115200USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //双向USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位USART_InitStruct.USART_StopBits = USART_StopBits_1; //无校验位USART_InitStruct.USART_Parity = USART_Parity_No; //1位停止位USART_Init(USART1, &USART_InitStruct); //初始化串口GPIO_InitTypeDef GPIO_InitStruct;// // PA9 tx 复用输出推挽
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
// GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
// GPIO_Init(GPIOA, &GPIO_InitStruct);
//
// // PA10 rx 输入浮空 输入上拉
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
// GPIO_Init(GPIOA, &GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能AFIO模块的时钟GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); //USART1_REMAP = 1// PB6 tx 输出推挽RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// PB7 rx 输入浮空 输入上拉RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStruct);while(1){}
}
四、发送数据
TxE:-发送数据寄存器空-当TDR空时,TxE=1;否则TxE=0
TC:-发送完成-当TDR空且移位寄存器空时,TC=1;否则TC=0
效果:向串口调试窗口发送1,2,3,4,5
点击查看代码
#include "stm32f10x.h"void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size);int main(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;// PB6RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate = 115200;USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_Parity = USART_Parity_No;USART_Init(USART1, &USART_InitStruct);USART_Cmd(USART1, ENABLE); // 闭合总开关uint8_t bytesToSend[] = {1,2,3,4,5};My_USART_SendBytes(USART1, bytesToSend, 5);while(1){}
}// @作用:使用串口一次性发送多个字节
// @参数:pData-要发送的数据 Size-字节的数量
void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size)
{for(uint32_t i = 0; i < Size; i++){// #1. 等待发送数据寄存器为空while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// #2. 将要发送的数据写入发数据寄存器当中USART_SendData(USART1, pData[i]);}// #3等待数据发送完成while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
五、格式化打印字符串
点击查看代码
#include "stm32f10x.h"
#include <stdio.h>
#include "delay.h"void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size);
void My_USART1_Init(void);int main(void)
{ Delay_Init();My_USART1_Init();// printf("Hello world. \r\n");while(1){uint32_t currentTick = GetTick();uint32_t miliSeconds = currentTick % 1000;currentTick = currentTick / 1000;uint32_t seconds = currentTick % 60;currentTick = currentTick / 60;uint32_t minutes = currentTick % 60;currentTick = currentTick / 60;uint32_t hour = currentTick;printf("%02u:%02u:%02u.%03u\r\n", hour, minutes, seconds, miliSeconds);Delay(100);}
}//
// @简介:通过串口发送多个字节
// @参数 USARTx:填写串口的名称
// @参数 pData:要发送的数据
// @参数 Size:要发送数据的数量,单位是字节
//
void My_USART_SendBytes(USART_TypeDef *USARTx, uint8_t *pData, uint16_t Size)
{for(uint32_t i = 0; i < Size; i++){// #1. 等待发送数据寄存器为空while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// #2. 写入发数据寄存器当中USART_SendData(USART1, pData[i]);}// 等待发送完成while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}//
// @简介:对USART1进行初始化
// PB6 - Tx, PB7 - Rx
// 115200, 8, 1, None, 双向
//
void My_USART1_Init(void)
{// #1. 初始化PB6和PB7GPIO_InitTypeDef GPIO_InitStruct;// // PA9 tx
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
// GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
// GPIO_Init(GPIOA, &GPIO_InitStruct);
//
// // PA10 rx
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
// GPIO_Init(GPIOA, &GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);// PB6RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStruct);// #2. 初始化USART1RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate = 115200;USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_Parity = USART_Parity_No;USART_Init(USART1, &USART_InitStruct);USART_Cmd(USART1, ENABLE); // 闭合总开关
}//重写fputc函数:通过串口发送出去
int fputc(int ch, FILE *f)
{// #1. 等待发送寄存器为空while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// #2. 写入发送寄存器当中USART_SendData(USART1, (uint8_t)ch);return ch;
}
六、接收数据
RxNE:-接收数据寄存器非空-当RDR非空时,RxNE=1;否则RxNE=0
PE:-奇偶校验错-如果接收到的数据有校验错误,则PE=1;否则PE=0
FE:-帧格式错误-接收到了无效的数据帧,则FE=1;否则FE=0
NE:-噪声错-接收的数据中检测到了噪声,则NE=1;否则NE=0
ORE:-过载错-由于过载造成了数据丢失,则ORE=1;否则ORE=0
效果:串口调试窗口发送1,板载LED亮,发送0,板载LED灭
main.c
点击查看代码
#include "stm32f10x.h"void My_USART1_Init(void);
void My_OnBoardLED_Init(void);int main(void)
{ My_USART1_Init();My_OnBoardLED_Init();while(1){// #1. 等待接收数据寄存器非空while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);// #2. 把数据从接收数据寄存器里读出来uint8_t byteRcvd = USART_ReceiveData(USART1);// #3. 对数据进行处理if(byteRcvd == '0'){GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 亮灯}else if(byteRcvd == '1'){GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 灭灯}}
}//
// @简介:对USART1进行初始化
// PB6 - Tx, PB7 - Rx
// 115200, 8, 1, None, 双向
//
void My_USART1_Init(void)
{// #1. 初始化PB6和PB7GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);// PB6RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);// PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStruct);// #2. 初始化USART1RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_BaudRate = 115200;USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_Parity = USART_Parity_No;USART_Init(USART1, &USART_InitStruct);USART_Cmd(USART1, ENABLE); // 闭合总开关
}void My_OnBoardLED_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}
七、封装常用功能
usart.h
点击查看代码
#ifndef _USART_H_
#define _USART_H_#include "stm32f10x.h"#define LINE_SEPERATOR_CR 0x00 // 回车 \r
#define LINE_SEPERATOR_LF 0x01 // 换行 \n
#define LINE_SEPERATOR_CRLF 0x02 // 回车+换行 \r\nvoid My_USART_SendByte(USART_TypeDef *USARTx, const uint8_t Data);
void My_USART_SendBytes(USART_TypeDef *USARTx, const uint8_t *pData, uint16_t Size);
void My_USART_SendChar(USART_TypeDef *USARTx, const char C);
void My_USART_SendString(USART_TypeDef *USARTx, const char *Str);
void My_USART_Printf(USART_TypeDef *USARTx, const char *Format, ...);uint8_t My_USART_ReceiveByte(USART_TypeDef *USARTx);
uint16_t My_USART_ReceiveBytes(USART_TypeDef *USARTx, uint8_t *pDataOut, uint16_t Size, int Timeout);int My_USART_ReceiveLine(USART_TypeDef *USARTx, char *pStrOut, uint16_t MaxLength, uint16_t LineSeperator, int Timeout);#endif
usart.c
点击查看代码
#include "usart.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "delay.h"static USART_TypeDef *usart_for_printf = 0; // 用于printf的USART名称//
// @简介:使用串口发送一个字节的数据
//
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 Data : 要发送的数据
//
void My_USART_SendByte(USART_TypeDef *USARTx, const uint8_t Data)
{My_USART_SendBytes(USARTx, &Data, 1);
}//
// @简介:使用串口发送多个字节的数据
//
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 pData : 要发送的数据(数组)
// @参数 Size :要发送数据的数量,单位是字节
//
void My_USART_SendBytes(USART_TypeDef *USARTx, const uint8_t *pData, uint16_t Size)
{if(Size == 0) return;for(uint16_t i=0; i < Size; i++){while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);USART_SendData(USARTx, pData[i]);}while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}//
// @简介:通过串口发送一个字符
//
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 C :要发送的字符
//
void My_USART_SendChar(USART_TypeDef *USARTx, const char C)
{My_USART_SendBytes(USARTx, (const uint8_t *)&C, 1);
}//
// @简介:通过串口发送字符串
//
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 Str :要发送的字符串
//
void My_USART_SendString(USART_TypeDef *USARTx, const char *Str)
{My_USART_SendBytes(USARTx, (const uint8_t *)Str, strlen(Str));
}//
// @简介:通过串口格式化打印字符串
//
// @参数 USARTx:串口名称,如USART1, USART2, USART3 ...
// @参数 Format:字符串的格式
// @参数 ... :可变参数
//
void My_USART_Printf(USART_TypeDef *USARTx, const char *Format, ...)
{usart_for_printf = USARTx;va_list args;va_start(args, Format);vprintf(Format, args);va_end(args);
}//
// @简介:通过串口读取一字节的数据
//
// @参数 USARTx :串口名称,如USART1, USART2, USART3 ...
//
// @返回值:读取到的字节
//
uint8_t My_USART_ReceiveByte(USART_TypeDef *USARTx)
{while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);return USART_ReceiveData(USARTx);
}//
// @简介:通过串口读取多个字节的数据
//
// @参数 USARTx :串口名称,如USART1, USART2, USART3 ...
// @参数 pDataOut:输出参数,读取到的数据将输出到此数组当中
// @参数 Size :需要读取的字节数量
// @参数 Timeout :超时时间,单位是毫秒,负数表示无限长。如果超时时间内没有读取完成则返回。
//
// @返回值:实际读取到的数据数量
//
uint16_t My_USART_ReceiveBytes(USART_TypeDef *USARTx, uint8_t *pDataOut, uint16_t Size, int Timeout)
{uint32_t expireTime;Delay_Init();if(Timeout >= 0){expireTime = GetTick() + Timeout; // 计算过期时间,过期时间 = 当前时间+Timeout}uint16_t i = 0;do{if(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == SET){pDataOut[i++] = USART_ReceiveData(USARTx);if(i==Size) break;}}while(Timeout < 0 || GetTick() < expireTime); // 判断是否超时return i;
}//
// @简介:通过串口读取一行字符串
//
// @参数 USARTx :串口名称,如USART1, USART2, USART3 ...
// @参数 pStrOut :输出参数,读取到的数据将输出到此数组当中
// @参数 MaxLength :字符串的最大长度
// @参数 LineSeperator:行分隔符 LINE_SEPERATOR_CR - 回车 \r
// LINE_SEPERATOR_LF - 换行 \n
// LINE_SEPERATOR_CRLF - 回车+换行 \r\n
// @参数 Timeout :超时时间,单位是毫秒,负数表示无限长。如果超时时间内没有读取完成则返回
//
// @返回值:0 - 成功读到一行字符串
// -1 - 超时(Timeout内未读到一行完整的字符串)
// -2 - 超过字符串的最大长度(字符串的最大长度用MaxLength参数设置)
//
int My_USART_ReceiveLine(USART_TypeDef *USARTx, char *pStrOut, uint16_t MaxLength, uint16_t LineSeperator, int Timeout)
{int ret = -1;uint32_t expireTime;Delay_Init();if(Timeout >= 0){expireTime = GetTick() + Timeout; // 计算过期时间,过期时间 = 当前时间+Timeout}uint16_t i = 0;do{if(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == SET){char c = (char)USART_ReceiveData(USARTx);pStrOut[i++] = c;if(LineSeperator == LINE_SEPERATOR_CR && c == '\r') // \r{pStrOut[i] = 0;ret = 0;break;}else if(LineSeperator == LINE_SEPERATOR_LF && c == '\n') // \n{pStrOut[i] = 0;ret = 0;break;}else if(pStrOut[i-2] == '\r' && c == '\n') // \r\n{pStrOut[i] = 0;ret = 0;break;}if(i == MaxLength) // 超过最大长度{ret = -2;break;}}}while(Timeout < 0 || GetTick() < expireTime); // 判断是否超时return ret;
}//
// @简介:此函数为对fputc的重写,以实现串口格式化打印功能
//
int fputc(int ch, FILE *f)
{// #1. 等待TXEwhile(USART_GetFlagStatus(usart_for_printf, USART_FLAG_TXE) == RESET);// #2. 将数据写入TDRUSART_SendData(usart_for_printf, (uint8_t)ch);return ch;
}