历史上的今天

今天是:2024年10月21日(星期一)

正在发生

2021年10月21日 | stm32专题十一:USART(四)USART编程

发布者:心灵捕手 来源: eefocus关键字:stm32  USART  编程 手机看文章 扫描二维码
随时随地手机看文章

stm32的串口USART编程要点


先初始化串口所用到的GPIO;

初始化串口,配置pUSART_InitTypeDef结构体;

配置中断NVIC(接收中断,中断优先级);

使能串口;

编写发送和接收函数;

编写中断服务函数;

接下在看具体的代码实现过程:


USART初始化配置函数,不难但是过程挺多的,容易遗漏,代码如下:


// 串口1 USART1

#define DEBUG_USARTx    USART1

#define DEBUG_USART_CLK    RCC_APB2Periph_USART1

#define DEBUG_USART_APBxClkCmd    RCC_APB2PeriphClockCmd

#define DEBUG_USART_BAUDRATE    115200

 

// USART GPIO引脚宏定义

#define DEBUG_UASRT_GPIO_CLK RCC_APB2Periph_GPIOA

#define DEBUG_UASRT_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd

 

#define DEBUG_UASRT_TX_GPIO_PORT GPIOA

#define DEBUG_UASRT_TX_GPIO_PIN GPIO_Pin_9

#define DEBUG_UASRT_RX_GPIO_PORT GPIOA

#define DEBUG_UASRT_RX_GPIO_PIN GPIO_Pin_10

 

#define DEBUG_UASRT_IRQn USART1_IRQn

#define DEBUG_UASRT_IRQHandler USART1_IRQHandler

 

void NVIC_Config(void)

{

NVIC_InitTypeDef NVIC_InitStruct;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

NVIC_InitStruct.NVIC_IRQChannel = DEBUG_UASRT_IRQn;

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStruct);

}

 

void USART_config(void)

{

  GPIO_InitTypeDef GPIO_InitStruct;

USART_InitTypeDef USART_InitStruct;

// 开启串口的GPIO时钟

DEBUG_UASRT_GPIO_APBxClkCmd(DEBUG_UASRT_GPIO_CLK, ENABLE);

 

  // USART的TX配置为复用推挽输出

GPIO_InitStruct.GPIO_Pin = DEBUG_UASRT_TX_GPIO_PIN;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(DEBUG_UASRT_TX_GPIO_PORT, &GPIO_InitStruct);

// USART的RX配置为浮空输入(由中文参考手册查询)

GPIO_InitStruct.GPIO_Pin = DEBUG_UASRT_RX_GPIO_PIN;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(DEBUG_UASRT_RX_GPIO_PORT, &GPIO_InitStruct);

// 开启串口时钟

DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

// 配置串口参数(波特率、8位数据、1位停止位、无校验、发送接收模式、无硬件流控)

USART_InitStruct.USART_BaudRate = DEBUG_USART_BAUDRATE;

USART_InitStruct.USART_WordLength = USART_WordLength_8b;

USART_InitStruct.USART_StopBits = USART_StopBits_1;

USART_InitStruct.USART_Parity = USART_Parity_No;

USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_Init(DEBUG_USARTx, &USART_InitStruct);

// 设置NVIC

NVIC_Config();

// 使能串口接收中断

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);

// 当发送数据时,发送数据寄存器非空,TXE标志位首先为0

// 然后程序便会等待,直至数据从DR转移到移位寄存器,此时TXE = 1,TC = 0

// 当数据全部从移位寄存器发出后,TC = 1

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

}


这里值得注意的是,串口调试助手有一个特点,不管接收到什么数据,显示的都是字符。当我测试发送数据100时,显示的是字符d,如下图所示,这是为什么?关键还是在于ASCII码。


USART_SendByte(DEBUG_USARTx, 100);

这是串口助手打印出来的信息(默认不√HEX显示,这样就是输出的字符,对应ASCII),和我们预期的打印输出100完全不同,接下来分析原因。

如下是ASCII表。可以看到,串口调试助手,将接收到的数据(100)转换成字符d(ASCII值)并显示,所以,我们如果是发送数据0X64,串口助手同样会打印字符d。同理,如果是电脑的串口助手给单片机发数据,比如发1,单片机在解析时,要认为这是字符' 1 ',而不是数值1,这一点要非常注意。

如果我们勾选了hex显示,那么串口助手就会显示接收到的数据(100)对应的十六进制数(64),如下图:

既然串口调试助手默认显示字符,那我们就可以直接打印字符(使用单引号' A '),如下,字符正确显示。


USART_SendByte(DEBUG_USARTx, 'A');

发送16位数据函数


/* 发送两个字节的数据 */

void USART_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)

{

uint8_t temp_h, temp_l; // 16位数据的高8位和低8位

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位数组的函数


等待最后一个8位数据从移位寄存器中发送完毕,然后TC从0变为1(如果是0则一直等待)

之前单个字节其实也应该再加一个TC判断,之所以没加,是为了提高效率

单字节读数据到DR,转移到移位寄存器,然后马上再读数据到DR(不用等到移位完毕再读取数据)

/* 发送8位数据的数组 */

void USART_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)

{

uint8_t i;

// 直接发送num次8位数据

for (i = 0; i < num; i++)

{

USART_SendByte(pUSARTx, array[i]);

}

// 等待最后一个8位数据从移位寄存器中发送完毕,然后TC从0变为1(如果是0则一直等待)

// 之前单个字节其实也应该再加一个TC判断,之所以没加,是为了提高效率

// 单字节读数据到DR,转移到移位寄存器,然后马上再读数据到DR(不用等到移位完毕再读取数据)

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);

}

为了使用printf,我们要重定义fputc和fgetc函数,将文件流向串口,具体如下:


// fputc,是函数。函数功能: 将字符ch写到文件指针fp所指向的文件的当前写指针的位置。

// 函数格式:int fputc (int c, FILE *fp)。fp为文件指针,它的值是执行fopen()打开文件时获得的。

/* 重定向c库函数printf到串口 */

int fputc(int ch, FILE *f)

{

/* 把ch发送到串口 */

USART_SendData(DEBUG_USARTx, (uint8_t)ch);

/* 等待发送完毕 */

while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);

 

return ch;

}

 

 

/* 重定向fgetc库函数到scanf串口 */

int fgetc(FILE *f)

{

/* 等待串口输入数据 */

while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

return (int)USART_ReceiveData(DEBUG_USARTx);

}

附(bsp_usart.c和bsp_usart.h)


/************************************* USART.C *******************************/

 

 

#include "bsp_usart.h"

#include

 

void NVIC_Config(void)

{

NVIC_InitTypeDef NVIC_InitStruct;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

NVIC_InitStruct.NVIC_IRQChannel = DEBUG_UASRT_IRQn;

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStruct);

}

 

void USART_config(void)

{

  GPIO_InitTypeDef GPIO_InitStruct;

USART_InitTypeDef USART_InitStruct;

// 开启串口的GPIO时钟

DEBUG_UASRT_GPIO_APBxClkCmd(DEBUG_UASRT_GPIO_CLK, ENABLE);

 

  // USART的TX配置为复用推挽输出

GPIO_InitStruct.GPIO_Pin = DEBUG_UASRT_TX_GPIO_PIN;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(DEBUG_UASRT_TX_GPIO_PORT, &GPIO_InitStruct);

// USART的RX配置为浮空输入(由中文参考手册查询)

GPIO_InitStruct.GPIO_Pin = DEBUG_UASRT_RX_GPIO_PIN;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(DEBUG_UASRT_RX_GPIO_PORT, &GPIO_InitStruct);

// 开启串口时钟

DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

// 配置串口参数(波特率、8位数据、1位停止位、无校验、发送接收模式、无硬件流控)

USART_InitStruct.USART_BaudRate = DEBUG_USART_BAUDRATE;

USART_InitStruct.USART_WordLength = USART_WordLength_8b;

USART_InitStruct.USART_StopBits = USART_StopBits_1;

USART_InitStruct.USART_Parity = USART_Parity_No;

USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_Init(DEBUG_USARTx, &USART_InitStruct);

// 设置NVIC

NVIC_Config();

// 使能串口接收中断

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);

// 当发送数据时,发送数据寄存器非空,TXE标志位首先为0

// 然后程序便会等待,直至数据从DR转移到移位寄存器,此时TXE = 1,TC = 0

// 当数据全部从移位寄存器发出后,TC = 1

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

}

 

/* 发送两个字节的数据 */

void USART_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)

{

uint8_t temp_h, temp_l; // 16位数据的高8位和低8位

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_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)

{

uint8_t i;

// 直接发送num次8位数据

for (i = 0; i < num; i++)

{

USART_SendByte(pUSARTx, array[i]);

}

// 等待最后一个8位数据从移位寄存器中发送完毕,然后TC从0变为1(如果是0则一直等待)

// 之前单个字节其实也应该再加一个TC判断,之所以没加,是为了提高效率

// 单字节读数据到DR,转移到移位寄存器,然后马上再读数据到DR(不用等到移位完毕再读取数据)

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);

}

 

/* 发送字符串 */

void USART_SendString(USART_TypeDef* pUSARTx, uint8_t *str)

{

uint8_t i = 0;

do 

{

USART_SendByte(pUSARTx, *(str + i));

i++;

} while (*(str + i) != '');

// 等待最后一个8位数据从移位寄存器中发送完毕,然后TC从0变为1(如果是0则一直等待)

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);

}

// fputc,是函数。函数功能: 将字符ch写到文件指针fp所指向的文件的当前写指针的位置。

// 函数格式:int fputc (int c, FILE *fp)。fp为文件指针,它的值是执行fopen()打开文件时获得的。

/* 重定向c库函数printf到串口 */

int fputc(int ch, FILE *f)

{

/* 把ch发送到串口 */

USART_SendData(DEBUG_USARTx, (uint8_t)ch);

/* 等待发送完毕 */

while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);

 

return ch;

}

 

/* 重定向fgetc库函数到scanf串口 */

int fgetc(FILE *f)

{

/* 等待串口输入数据 */

while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

return (int)USART_ReceiveData(DEBUG_USARTx);

}

/********************************** USART.H ***********************************/

 

#ifndef __BSP_USART_H

#define __BSP_USART_H

 

#include "stm32f10x.h"

 

// 串口1 USART1

#define DEBUG_USARTx    USART1

#define DEBUG_USART_BAUDRATE    115200

#define DEBUG_USART_CLK    RCC_APB2Periph_USART1

#define DEBUG_USART_APBxClkCmd    RCC_APB2PeriphClockCmd

 

// USART GPIO引脚宏定义

#define DEBUG_UASRT_GPIO_CLK RCC_APB2Periph_GPIOA

#define DEBUG_UASRT_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd

 

#define DEBUG_UASRT_TX_GPIO_PORT GPIOA

#define DEBUG_UASRT_TX_GPIO_PIN GPIO_Pin_9

#define DEBUG_UASRT_RX_GPIO_PORT GPIOA

#define DEBUG_UASRT_RX_GPIO_PIN GPIO_Pin_10

 

#define DEBUG_UASRT_IRQn USART1_IRQn

#define DEBUG_UASRT_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_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num);

void USART_SendString(USART_TypeDef* pUSARTx, uint8_t *str);

 

#endif /* __BSP_USART_H */

 

串口数据回显(接收到数据,然后通过串口原样发送)


void DEBUG_UASRT_IRQHandler(void)

{

uint8_t ucTemp;

  if (USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)

{

ucTemp = USART_ReceiveData(DEBUG_USARTx);

USART_SendData(DEBUG_USARTx, ucTemp);

}

}

关键字:stm32  USART  编程 引用地址:stm32专题十一:USART(四)USART编程

上一篇:stm32专题十一:USART(三)初始化结构体和标准库函数分析
下一篇:stm32专题十一:USART(五)USART发送命令解析

推荐阅读

AI芯片是科技行业最惹人注目的赛道。寒武纪是这个赛道上最惹人注目的成长期公司。贸易战关口,这里的一草一木更是彻底暴露于聚光灯下。 在今年初完成数亿美元B轮融资后,寒武纪投后估值已达25亿美元,成为中国人工智能芯片领域的头部独角兽。作为“中国第一家作出最原始创新(从学术理念到商业产品)的芯片设计公司和全球第一家推出量产商业AI芯片的公司...
#include “stm32f10x.h”void RCC_Configuration(void);//2void GPIO_Configuration(void);//GPIOvoid Delay(u32 count){u32 i=0;for(;i<count;i++);}int main(void){ RCC_Configuration();//3LED_Init();while(1){GPIO_SetBits(GPIOA,GPIO_Pin_0);//第一灯亮Delay(800000); //延时GPIO_ResetBits(GPIOA,GPIO_Pin_0); //第一灯灭Delay(800000);...
据台媒联合新闻网报道,相关消息指出,三星最快会在2022年将AMD Radeon GPU设计整合在旗下Exynos处理器上,以借此强化其处理器显示运算性能。 AMD在去年6月份宣布与三星建立合作,预计将把Radeon图像芯片技术导入智能手机应用,而三星在后续也说明此项合作至少长达两年。 报道指出,高通处理器所搭载的Adreno GPU通常在手机显示运算性能有明显突出表现...
Diodes Incorporated 推出高效率 D 类立体声音频放大器节省电池电量,同时提供绝佳音质【2021 年 10 月 21 日美国德州普拉诺讯】Diodes 公司) 推出带有整合式同步升压转换器的 PAM8965 D 类立体声音频功率放大器。Diodes公司锁定支持人工智能的扬声器系统及携带式乐器,推出这款高效率装置,而提高输出功率、延长电池寿命及小巧结构都是这些...

史海拾趣

问答坊 | AI 解惑

usb-blaster出现的怪问题,高手帮忙看一下,谢谢啦!

自己所用usb-blaster以前效果很好,有一次接到板子上后,下载线的芯片发热(时间有点久),最后拔下来后,再去试时,在设备管理器中可以看见发现了下载线,但是下载时以致出现以下错误,大家帮忙看看吧!!…

查看全部问答∨

XP下如何对IO进行操作,请指教思路

我的工控机装的是XP系统,需要一个看门狗,所以需要我的程序对IO进行操作,但是XP下是不允许应用程序直接对IO进行操作的!那应该怎么做?请高手指教思路,是要自己写IO的驱动吗,一点思路都没有,请高手指教思路。谢谢…

查看全部问答∨

如何运用Quartus软件设计交通灯控制器

内容: 设计一个十字路口的红、绿、黄三色信号交通灯控制电路 设计的要求与数据:1. 用红、绿、黄三色发光二极管作信号灯。主干道为东西向,有红、绿、黄三个灯;支干道为南北向,也有红、绿、黄三个灯。红灯亮禁止通行;绿灯亮允许通行;黄灯亮则 ...…

查看全部问答∨

屏幕右下角的时钟,跟双击又下角设置时钟界面的时钟不一致?

屏幕右下角的时钟,跟双击又下角设置时钟界面的时钟不一致? 右下角的时钟好像是比设置界面的时钟慢十几秒?这是什么原因啊? 用的外部的rtc芯片…

查看全部问答∨

【求助】 keilc编程 已知2个字符、字符串发送函数。

//向串口发送一个字符 void send_char_com(unsigned char ch) { SBUF=ch; while(TI == 0); TI=0; } //向串口发送一个字符串,长度不限。 //应用:send_string_com("d9887321$"); void send_string_com(unsigned char *str) { whil ...…

查看全部问答∨

请问信息存储段可以用来存程序吗?

1、比如用2kflash的2013,但是程序有2100个字节(信息段不用),请问可以把256 byte的信息段当程序段使用吗? 2、430系列的芯片可以做成掩膜(就是一个黑疙瘩的那种)的吗?,比如把上面的程序做在一个掩膜芯片里面。如果可以的话应该怎么做?不知 ...…

查看全部问答∨

各位Proteus高手请看看

在Proteus元件库中HT48R50A芯片和SN74HC57A芯片找不到!!请高手帮帮忙!! 最好发可以仿真的元件来!!谢谢!!! …

查看全部问答∨

据说MSP430的单片机能够熔断保险丝?请高手指导一下,如何操作呢?

保险丝是集成在单片机里边吗??   如何熔断啊,需要搭载怎样的电路呢?   还是需要编程时实现?   求高手指教,谢谢了…

查看全部问答∨

MSP-EXP430FR5739 USB Experimenter’s Board US14.5

剛剛從TI DEAL 中看到這則優惠,轉過來讓大家看看。http://tideals.com/有需要的可以直接買喔~…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved