STM32学习——USART收发数据

发布者:Huanle最新更新时间:2022-01-25 来源: eefocus关键字:STM32  USART  收发数据 手机看文章 扫描二维码
随时随地手机看文章

简介

1.串口通讯的双方若采用不同的电平标准,则需要利用电平转换芯片进行转换。

2.调试程序时可以把一些调试信息“打印”在电脑端的串口调试助手上。

3.硬件原理以后有空再研究,应该跟微机里学的挺类似的。。。


配置一个串口的步骤

1.使能USART时钟,以及RX和TX引脚的GPIO时钟

2.初始化GPIO,配置相关的引脚功能

3.配置USART的工作参数

4.配置中断控制器NVIC,使能串口中断

5.使能USART的接收中断

6.使能USART

7.编写中断服务函数


代码设计

通用代码参考的是野火的代码,自己写的太不规范了。。。


(1)串口初始化


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

}


(2)NVIC中断控制器的初始化


static void NVIC_Configuration(void)

{

  NVIC_InitTypeDef NVIC_InitStructure;

  

  /* 嵌套向量中断控制器组选择 */

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

}


(3)发送一个字符以及发送字符串

对于发送字符,这里封装了库函数并增加了等待发送完成的查询(若无该步,字符串无法发送成功)


/*===================发送一个字符=========================*/

void Usart_SendByte(USART_TypeDef* USARTx, uint8_t Data)

{

USART_SendData(USARTx,Data);

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

}


/*===================发送一个字符串======================*/

void Usart_SendString(USART_TypeDef* USARTx,char *str)

{

uint32_t i=0;

for(;*(str+i)!='';i++)

{

Usart_SendByte(USARTx,*(str+i));

}

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

}


(4)printf的重定向

为了使用c库中的printf,需要对fputc进行重定向


//注意添加头文件

#include


//……


///重定向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);

}


(5)发送和接受数据

定义一个缓冲区,利用中断方式接受数据,利用FIFO模式发送数据,分别贴出中断服务函数以及主函数


//中断服务函数


extern uint8_t Uart_data[64];

extern uint32_t Rx_Num;


void DEBUG_USART_IRQHandler(void)

{

uint8_t temp;

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

{

temp= USART_ReceiveData(DEBUG_USARTx);

Uart_data[Rx_Num]=temp;

Rx_Num++;

Rx_Num&=0x3f;

}

}


//主函数

uint8_t Uart_data[64];

uint32_t Rx_Num=0;

uint32_t Tx_Num=0;


int main(void)

{

DEBUG_USART_Config();

printf("hello,worldr");

while(1)

{

if(Tx_Num!=Rx_Num)

{

USART_SendData(DEBUG_USARTx,Uart_data[Tx_Num]);

Tx_Num++;

Tx_Num&=0x3f;

}

}

}


上述方法定义了三个外部变量,需要注意用法。


(6)设置指令集(控制LED)

利用上位机发送数据时,只能发送单个字符,发送一串字符时仅能接收到第一个。


/*====================字符串匹配=======================*/

uint8_t Cmp(uint8_t *str,uint8_t num)

{

uint8_t i=0;

uint8_t str1[]="111111111";

uint8_t str2[]="000000000";

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

{

if(*(str1+i)!=*(str+i))

break;

}

if(i==(num-1))

return 1;

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

{

if(*(str2+i)!=*(str+i))

break;

}

if(i==(num-1))

return 2;

return 0;

}


//中断服务函数

// 串口中断服务函数

void DEBUG_USART_IRQHandler(void)

{

  uint8_t ucTemp;

u8 i;

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

{

ucTemp = USART_ReceiveData(DEBUG_USARTx);

    //USART_SendData(DEBUG_USARTx,ucTemp);  

Uart_Data[Rx_Num]=ucTemp;

Rx_Num++;

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

{

printf("%c",Uart_Data[i]);

}

if(Rx_Num==9)

{

// for(i=0;i<10;i++)

// {

// printf("%c",Uart_Data[i]);

// }

Rx_Num=0;

}

}  

}


//主函数

int main(void)

{

  USART_Config();

LED_Config();


  while(1)

{

switch(Cmp(Uart_Data,10))

{

case 1: LED1_ON;

LED2_OFF;

break;

case 2: LED1_OFF;

LED2_ON;

break;

case 0:

LED2_OFF;

LED1_OFF;

break;

}

}

}


填坑

在使用正点原子例程进行接受数据时,出现的问题

功能概述:MCU之间通过USART2进行数据传输,然后主控芯片又通过USART1向电脑端的串口助手发送数据。

存在问题:在高速数据传输的过程中第一位丢失


原因:printf的问题,重定向时存在问题,导致数据高速传输时,使用printf会导致之后的数据丢失(主控向电脑端传输时)

解决方法:原先用printf发送数据的地方改用USART_SendData发送,在用while等待


while(1)

{

/*====================MCU之间串口通信的数据显示=========================*/

//功能说明

//MCU之间通过USART2进行通信,主控芯片将数据存入缓冲区,并通过串口1发送到电脑端

//数组有效数据长度为9

if(USART2_RX_STA&0x8000) //如果发送完成

{

len=(USART2_RX_STA&0x3fff);

USART_SendData(USART1,len);//发长度

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);

for(i=0;i //将数据通过串口1发送

{

USART_SendData(USART1,USART2_RX_BUF[i]);

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);

}

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);

USART_SendData(USART1,'r');//插入换行

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);

USART_SendData(USART1,'n');//插入换行

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);

USART2_RX_STA=0;

}

/*==========================================================================*/

}


注意:一定要取一中间变量len,在for循环发送数据时USART2_RX_STA可能会进入中断从而改变数据长度

正点原子串口数据接收的思路:

1.利用缓冲区

2.设置状态变量USART2_RX_STA,低13位表示数据长度,14位表示接收到0x0d,15位表示接收到0x0a(接收完成)(通信协议,发送的数据最后需要加’rn’才能接受)


void USART2_IRQHandler(void)                //串口2中断服务程序

{

u8 Res;

if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

{

USART_ClearITPendingBit(USART2, USART_IT_RXNE);

Res =USART_ReceiveData(USART2);//(USART1->DR); //读取接收到的数据

if((USART2_RX_STA&0x8000)==0)//接收未完成

{

if(USART2_RX_STA&0x4000)//接收到了0x0d

{

if(Res!=0x0a)USART2_RX_STA=0;//接收错误,重新开始

else USART2_RX_STA|=0x8000; //接收完成了 

}

else //还没收到0X0D

{

if(Res==0x0d)USART2_RX_STA|=0x4000;

else

{

USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;

USART2_RX_STA++;

if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收   

}  

}

}

  } 

}

关键字:STM32  USART  收发数据 引用地址:STM32学习——USART收发数据

上一篇:STM32学习——中断方式下的发送数据
下一篇:STM32学习——EXTI外部中断

推荐阅读最新更新时间:2024-11-06 10:16

STM32 不断进入串口中断问题 解决方法
STM32 有时候会不断进入中断,解决方法如下 1.串口初始化配置时,需要打开ORE 溢出中断,如下红色代码所示 void Usart_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 开启串口时钟 GPIO_PinA
[单片机]
USART接收数据,以回车结束
单片机:PIC18F45K80 实现功能:接收发来的以回车换行(0x0D,0X0A)结尾的串口数据。 #define RX_SIZE 100 //接收缓存区大小 #define FINISH 1 bit RX_flag; //接收完成标志 unsigned char RX ; //接收缓存 unsigned char RX_C=0; //接收计数 void USART_RX() //在中断中加入此函数 { unsigned char data; if(RC1IE&&RC1IF) //判断是否是usart1中断,且RC1IF=1 { data=RCREG1; //接收1字节数据
[单片机]
stm32定时器1的2路互补PWM
void TIM1_PWM_Ini(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; //开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能定时器3时钟 RCC_APB2PeriphClockC
[单片机]
基于STM32单片机的大扭矩永磁同步电机驱动系统
0 引言 大扭矩永磁同步电机直接驱动由于去掉了复杂的机械传动机构,从而消除了机械结构带来的效率低、维护频繁、噪声与转动惯量大等不利因素,具有效率高、振动与噪声小、精度高、响应快、使用维修方便等一系列突出优点 。近年来,随着电力电子技术、永磁材料、电机设计与制造技术、传感技术、控制理论等的发展,大扭矩永磁同步电机在数控机床、矿山机械、港口机械等高性能系统中得到了越来越广泛的应用 。 交流电机控制系统广泛采用单片机、DSP、FPGA为控制系统核心。STM32 是一种基于ARM 公司Cortex-M3 内核的新型32 位闪存微控制器,采用了高性能、高代码密度的Thumb-2 指令集和紧耦合嵌套向量中断控制器,拥有丰富的外围接口,具有
[单片机]
基于<font color='red'>STM32</font>单片机的大扭矩永磁同步电机驱动系统
stm32使用HX711读电子秤的值
使用HX711变送器模块+5kg的传感器。 #define HX711_DATA PEin(0) #define HX711_SCK PEout(1) void HX711_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.G
[单片机]
<font color='red'>stm32</font>使用HX711读电子秤的值
STM32输入捕获,实现红外解码,支持长按
初始化代码: static void RCC_Configuration( void ); static void GPIO_Configuration( void ); static void NVIC_Configuration( void ); void InputCaptureInit( void ) { TIM_ICInitTypeDef TIM_ICInitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; RCC_Configuration(); NVIC_Configuration(); GPIO_Conf
[单片机]
应用笔记|直接修改寄存器来输出内部时钟的方法
1. 在特殊情况下使能 MCO 功能的方法 在对某些不容易复现的问题进行代码调时,需要观察内部时钟的情况,但往往代码之前并没有使能 MCO 功能,在这种情况下就可以使用寄存器直接配置来输出内部时钟到 GPIO 脚位上进行观察和测试。 下面的例子就是在调试 STM32G474 很难复现的一个问题,调试暂停时,通过 PC 端调试工具直接更改寄存器配置来使能 MCO 功能输出 SYSCLK 到 GPIO 口的方法。 2.具体实现 MCO 输出内部时钟到 GPIO 脚位,可以不通过运行用户代码,直接在 PC 端调试工具中配置寄存器来实现。在程序暂停时,往往已经走过了时钟配置,在这种情况下首先需要使能并配置RCC_CFGR 寄存器MCO
[单片机]
应用笔记|直接修改寄存器来输出内部时钟的方法
在Mac OSX中开发STM32程序
在Mac OSX下编写STM32程序: 1、下载stm32的gcc软件包,参考下面URL: 2、安装moxa NPORT 5110在虚拟Windows中; 3、安装ST Flash Loader 软件; 需要注意的: 1、Mac OSX 需要10.5; 2、NPORT装完要关机重启;
[单片机]
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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