想要分享一下自己学习stm32的过程,这篇分享一下串口通信的相关内容,是自己的一些理解,可能不是很专业全面,请大家谅解。
主要是用stm32通过串口一发送信息到电脑,在串口调试助手上显示出来,包括发送一个字节,发送两个字节,发送8位数据数组,发送字符串,重定向c库函数printf到串口几个内容来说;
#ifndef __BSP_USART_H
#define __BSP_USART_H
#include "stm32f10x.h"
#include <stdio.h>
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
#define DEBUG_USART_GPIO_CLK RCC_APB2Periph_GPIOA
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler
void USART_Config(void);
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data);
void Usart_SendHalfWord(USART_TypeDef* pUSARTx,uint16_t data);
void Usart_Array(USART_TypeDef* pUSARTx,uint8_t *array,uint8_t num);
void Usart_SendStr(USART_TypeDef* pUSARTx,uint8_t *str);
#endif
#include "bsp_usart.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
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(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
这里给大家直接展示了项目中变量的宏定义和串口和NVIC的初始化部分,中断在串口通信中的作用是电脑上位机发送信息给单片机时,单片机接收到了就产生中断,并且在中断服务函数里面把电脑发给单片机的内容立马返回去,现在只是stm32通过串口一发送信息到电脑,所以并未用到。
初始化完成之后就可以编写相应发送字节函数,下面奉上发送一个字节数据函数
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data)
{
USART_SendData(pUSARTx, data);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE)==RESET);
}
发送一个字节数据函数调用了USART_SendData函数,将串口和通信的数据通过形参传入,用与发送数据,使用USART_GetFlagStatus是获取相应的标志位,其中USART_FLAG_TXE是USART_SR状态寄存器中的位7所控制,通过判断发送数据寄存器是否为空来确定数据是否发送完成,通过此位是0还是1来确定,见下图
转到程序中,USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE)==RESET说明数据还没有被转移到移位寄存器未发送完成,当为1时表示发送完成跳出循环。
/*发送两个字节数据*/
void Usart_SendHalfWord(USART_TypeDef* pUSARTx,uint16_t data)
{
uint8_t temp_h,temp_l;
temp_h = (data&0xff00) >> 8;
temp_l = data&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);
}
发送两个字节数据函数形参data为16位,可以将其分为高8位和低8位分别发送,通过(data&0xff00) >> 8获取高8位(0xff00转化二进制为1111 1111 0000 0000,data8位数据每位与1相与最终还是data之后再右移,将0000 0000移除,获得temp_h)data&0xff获取低8位(与上述同样原理,但不需要右移),分别发送,最终实发送两个字节数据。
/*发送8位数据数组*/
void Usart_Array(USART_TypeDef* pUSARTx,uint8_t *array,uint8_t num)
{
uint8_t i;
for(i = 0;i < num;i++)
{
Usart_SendByte(pUSARTx,array[i]);
}
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET);
}
这个代码是发送8位数组的,感觉这里值得注意的是检测发送是否完成用得是USART_FLAG_TC,其实在这里使用USART_FLAG_TXE经过尝试也是没有问题的,但在发送一字节和两字节数据时使用USART_FLAG_TC会导致数据传输不完整,这个问题未解决。
/*发送字符串*/
void Usart_SendStr(USART_TypeDef* pUSARTx,uint8_t *str)
{
uint8_t i=0;
do
{
Usart_SendByte(pUSARTx,*(str+i));
i++;
}
while(*(str+i)!='\0');
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET);
}
上面是发送字符串的函数,和发送8位数据数组大同小异,不再过多赘述。
最后再说一下使用printf同样可以将信息发送至串口调试助手,不过需要在串口的.h文件中声明#include <stdio.h>头文件,同时在串口的.c文件中加入fputc函数重定向c库函数printf到串口
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch,FILE *f)
{
/*发送一个字节数据到串口*/
USART_SendData(DEBUG_USARTx,(uint8_t) ch);
/*等待发送完毕*/
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE)==RESET);
return (ch);
}
到此,先分享到这里,最后奉上我的main.c和bsp_usart.c和bsp_usart.h文件
main.c文件
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_usart.h"
int main(void)
{
uint8_t a[10]={1,2,3,4,5,6,7,8,9,10};
//LED_GPIO_Config();
USART_Config();
//Usart_SendByte(DEBUG_USARTx,0X64);
//Usart_SendHalfWord(DEBUG_USARTx,0xff56);
//Usart_Array(DEBUG_USARTx,a,10);
Usart_SendStr(DEBUG_USARTx, "哈哈哈哈 \r\n");
printf("哈哈哈哈哈 \r\n");
while (1)
{
}
}
bsp_usart.c文件
#include "bsp_usart.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief USART GPIO 配置,工作参数配置
* @param 无
* @retval 无
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
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(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
/*发送一个字节数据*/
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data)
{
USART_SendData(pUSARTx, data);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE)==RESET);
}
/*发送两个字节数据*/
void Usart_SendHalfWord(USART_TypeDef* pUSARTx,uint16_t data)
{
uint8_t temp_h,temp_l;
temp_h = (data&0xff00) >> 8;
temp_l = data&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);
}
/*发送8位数据数组*/
void Usart_Array(USART_TypeDef* pUSARTx,uint8_t *array,uint8_t num)
{
uint8_t i;
for(i = 0;i < num;i++)
{
Usart_SendByte(pUSARTx,array[i]);
}
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET);
}
/*发送字符串*/
void Usart_SendStr(USART_TypeDef* pUSARTx,uint8_t *str)
{
uint8_t i=0;
do
{
Usart_SendByte(pUSARTx,*(str+i));
i++;
}
while(*(str+i)!='\0');
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET);
}
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch,FILE *f)
{
/*发送一个字节数据到串口*/
USART_SendData(DEBUG_USARTx,(uint8_t) ch);
/*等待发送完毕*/
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE)==RESET);
return (ch);
}
//重定向c库函数scanf到串口,重定向后可使用scanf,getchar函数
int fgetc(FILE *f)
{
/*等待串口输入数据*/
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE)==RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
bsp_usart.h文件
#ifndef __BSP_USART_H
#define __BSP_USART_H
#include "stm32f10x.h"
#include <stdio.h>
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
#define DEBUG_USART_GPIO_CLK RCC_APB2Periph_GPIOA
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler
void USART_Config(void);
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data);
void Usart_SendHalfWord(USART_TypeDef* pUSARTx,uint16_t data);
void Usart_Array(USART_TypeDef* pUSARTx,uint8_t *array,uint8_t num);
void Usart_SendStr(USART_TypeDef* pUSARTx,uint8_t *str);
#endif
谢谢大家观看,学习较浅,有错误之处请大家谅解。