文章目录
前言
使用b站江科大课程进行学习
一、定义概念 + 缩写
1. USART
Universal Synchronous/Asynchronous Receiver/Transmitter
通用--------同步------------异步-----------------收发器
2. 波特率(Baud Rate)
单位时间内传送的码元符号的个数: 百度 - 波特率
3. USART的最基本结构
数据流:接收端 -> 寄存器
二、性质
1. 串口的时序
2. stm32f103c8t6的USART资源
USART 1 在 APB2 总线上
USART 2、USART 3 在 APB1 总线上
3. 引脚的复用
三、使用步骤(函数代码)
1. 观察H文件内的函数,寻找自己要用的函数
像用一个软件一样,刚开始用的时候大部分功能(函数)我们都用不上,就需要到H文件(用于声明函数名和参数,如果参数是结构体(像Python里用字典传递参数)意味着参数很多)去寻找自己需要的功能
// stm32f10x_usart.h
// init
void USART_DeInit(USART_TypeDef* USARTx);
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
// 同步时钟输出
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
// DMA
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);
// 设置地址,唤醒,LIN
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);
// 重要:sendData 发送数据,receiveData 接收数据
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
// 智能卡,IRDA等函数
void USART_SendBreak(USART_TypeDef* USARTx);
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);
// 标志位相关函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
2. 串口初始化
- 开启时钟
void Serial_Init(void)
{
}
在函数内配置以下内容:
- 开启时钟
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
- 通过配置GPIO结构体来初始化GPIO
// GPIO初始化,配置结构体
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 将PA9引脚初始化为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
- 通过配置USART1结构体来初始化USART1
// USART初始化,配置结构体
USART_InitTypeDef USART_InitStructure; // 定义结构体变量
USART_InitStructure.USART_BaudRate = 9600; // 波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制 -> 不需要
USART_InitStructure.USART_Mode = USART_Mode_Tx; // 模式,选择为发送模式
USART_InitStructure.USART_Parity = USART_Parity_No; //奇偶校验,不需要
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位,选择1位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长,选择8位
USART_Init(USART1, &USART_InitStructure); //将结构体变量交给USART_Init,配置USART1
- 使能USART1
/*USART使能*/
USART_Cmd(USART1, ENABLE); //使能USART1,串口开始运行
3. 发送一个字节
void Serial_SendByte(uint8_t Byte)
{
// 将字节数据写入数据寄存器,写入后USART自动生成时序波形
USART_SendData(USART1, Byte);
// 等待标志位变为 RESET 标志发送完成
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
4. 对SendByte函数进行封装(成发送数组)
- 封装为发送数组,实现方法是调用 Length 次 Serial_SendByte()
/**
* 函 数:串口发送一个数组
* 参 数:Array 要发送数组的首地址
* 参 数:Length 要发送数组的长度
* 返 回 值:无
*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++) //遍历数组
{
Serial_SendByte(Array[i]); //依次调用Serial_SendByte发送每个字节数据
}
}
- 封装为发送字符串,原理与发送数组相似,结束标志 String[i] == ‘\0’
/**
* 函 数:串口发送一个字符串
* 参 数:String 要发送字符串的首地址
* 返 回 值:无
*/
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
{
Serial_SendByte(String[i]); //依次调用Serial_SendByte发送每个字节数据
}
}
- 传送 int 数字,将 int 数字的每位依次SendByte
/**
* 函 数:串口发送数字
* 参 数: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发送每位数字
}
}
- 给sendbyte用,函数结果是得到数的乘方,作用是提取sendbyte输入的某一位
/**
* 函 数:次方函数(内部使用)
* 返 回 值:返回值等于X的Y次方
*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1; //设置结果初值为1
while (Y --) //执行Y次
{
Result *= X; //将X累乘到结果
}
return Result;
}
5. printf函数
- 首先创建一个重定向的函数,将printf的底层重定向到 SendByte
在原始情况下,电脑上的 printf 底层就是通过fputc(file output char)一个字一个字打印在 cmd 上的,这里重定向了printf的底层函数
/**
* 函 数:使用printf需要重定向的底层函数
* 参 数:保持原始格式即可,无需变动
* 返 回 值:保持原始格式即可,无需变动
*/
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch); //将printf的底层重定向到自己的发送字节函数
return ch;
}
- 另一种 printf 方法
/*方法2:使用sprintf打印到字符数组,再用串口发送字符数组,此方法打印到字符数组,之后想怎么处理都可以,可在多处使用*/
char String[100]; // 定义字符数组
sprintf(String, "\r\nNum3=%d", 333);// 使用sprintf,把格式化字符串打印到字符数组
Serial_SendString(String); // 串口发送字符数组(字符串)
之前没有学过sprintf,sprintf函数的格式:
int sprintf( char *buffer, const char *format [, argument,…] );
// 原文链接:https://blog.csdn.net/yishizuofei/article/details/78195255
除了前两个参数固定外,可选参数可以是任意个。buffer是字符数组名;format是格式化字符串(像:"%3d%6.2f%#x%o"
,%与#合用时,自动在十六进制数前面加上0x)。只要在printf中可以使用的格式化字符串,在sprintf都可以使用。其中的格式化字符串是此函数的精华。
- 第三种printf方法,对可变参数
sprintf
进行封装
void Serial_Printf(char *format, ...) // ... 代表可变参数
{
char String[100]; // 定义字符数组
va_list arg; // 定义可变参数列表数据类型的变量arg
va_start(arg, format); // 从format开始,接收参数列表到arg变量
vsprintf(String, format, arg); // 使用vsprintf打印格式化字符串和参数列表到字符数组中,不能用sprintf
va_end(arg); // 结束变量arg,释放参数表
Serial_SendString(String); // 串口发送字符数组(字符串)
}
6. 汉字编码
汉字编码需要同时改变 Keil 编译器和串口助手 / TX 信号的接受方的编码,比如都是utf-8
需要给编译器输入--no-multibyte-chars
总结
以上就是江科大32串口的发送内容,不包括接收