嵌入式学习笔记——STM32的USART收发字符串及串口中断

这篇具有很好参考价值的文章主要介绍了嵌入式学习笔记——STM32的USART收发字符串及串口中断。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

上一篇中,介绍了串口收发相关的寄存器,通过代码实现了一个字节的收发,本文接着上面的内容,通过功能函数实现字符串的收发,然后引入中断解决收发过程中while()死等的问题。

字符串的收发

发送一个字符串

根据昨天的字符发送函数,只需要稍作修改即可实现发送函数了,一个字符串的结尾会有一个’\0’作为结束符,所以再发送过程中,只需要判断当前发送的字符是不是结束符即可,如果不是结束符就将该位发送至电脑的串口调试助手,如果是结束符,那就意味着一个字符串发送完毕了。具体代码如下:

/*******************************************
*函数名    :Usart1_Send_Str
*函数功能  :串口1发送一个字符串函数
*函数参数  :u8 *str
*函数返回值:无
*函数描述  :
*********************************************/
void Usart1_Send_Str(u8 *str)
{
	while(*str != '\0')
	{
		Usart1_Send_Byte(*str);
		str++;
	}
}

接收字符串

发送字符串相对容易,接收这边,就需要借用C语言中的数组来帮忙了,因为数据是一个字符一个字符的发送过来的,每一次只能接收一个字符,所以需要使用一个数组来存接收到的位,而且串口助手在发送字符串的时候是不会给单片机发送结束符,所以还需要编程者自己规定结束符,当然,后面引入空闲中断之后就不需要这样操作了。这里笔者使用的是‘#’作为结束标志。具体实现代码如下:


/*******************************************
*函数名    :Usart1_Receive_Str
*函数功能  :串口1接收一个字节函数
*函数参数  :void
*函数返回值:u8 str
*函数描述  :
*********************************************/
void Usart1_Receive_Str(void)
{
	static u8 i=0;
	//等待接收完成
	while(!(USART1->SR & (1<<5)));
	//将数据寄存器的数据读取到数组
		Str_Buff[i] = USART1->DR;
		i++;
	if(Str_Buff[i-1]=='#')//如果检测到结束标志‘#’
	{
		Str_Buff[i-1]= '\0';//手动给字符串添加‘\0’结束符
		i=0;
		Usart1_Receive_Str_Flag=1;//接收完成的标志位置一
		Usart1_Send_Str(Str_Buff);//将接受的数组再发回串口助手
	}
}

需求

使用串口调试助手发送11打开1号小灯,10关闭一号小灯,21打开二号小灯,20关闭二号小灯。效果如下:
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
主函数代码:
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断

利用串口实现printf

在C语言的学习中,使用频率最高的输出函数就是printf了,这个函数在单片机上也同样适用,只是要改一下输出的方向,所以也叫重定向。
在C中printf函数-----输出函数
输出方向:PC机------>屏幕
在单片机中printf-------输出函数
输出方向: 单片机---->PC机
关于具体的修改其实KEIL已经帮我们做好了,需要我们修改的只有一个,就是将“stdio.h”内的fputc,也就是字符输出函数,修改到和我们的字符串输出函数关联即可。具体代码如下:

 //printf的重定向函数
//fputc-----专门发送字符的函数h
int fputc(int c, FILE * stream)
{
	Usart1_Send_Byte(c);
	return c;
}

将此代码放到USART1.c中即可,不需要调用也不需要声明。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
然后就是勾选KEIL的库,如下图所示,依次选择魔法棒、Target、然后将3所在位置的复选框锁定。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
然后再在USART1.h中添加stdio.h。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
最后在主函数中调用printf即可,printf主要是方便我们的后期调试,实用语法与C一致。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
实际输出效果:
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
到这里,已经实现了字符串的收发,但是存在两个问题,
1是上位机发送数据到板子上必须要设置结束符,类似笔者此处的‘#’;
2是此代码在接收时会阻塞在等待接受完毕的while,这会导致其他的模块工作不正常,在while(1)内,一定要尽力避免死等的出现。
很明显,现在这个代码还不太令人满意,那么要怎么修改呢,在修改代码之前,需要先去了解一个新的东西——中断。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断

中断

首先,需要知道中断是个什么东西,它有什么作用,具体怎么使用,下面一一来进行介绍。

中断是什么

中断嘛,按照名字的第一反应是终止一件事,打断某些东西的感觉,实际上也差不多是这个意思,它终止和打断的就是前面编写的main函数里面运行的东西。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
也就是说在程序正常运行过程中,出现了不正常的事件(异常),CPU会优先去处理这个异常,处理完之后再回到正常的程序中。这个异常事件就是中断。
中断的目的:由外设或者CPU创造一个异常事件(紧急事件)
紧急事件发生的时间和地点:未知
紧急事件是实时响应的,紧急事件不能执行太久(里面不能有延时 循环 阻塞程序)
下面我们来看一张图:
代码正常运行的时候是如下图所示的蓝色箭头方向,首先会依次向下执行初始化代码,然后进入while循环,始终在任务1与任务2之间循环运行。

------->任务2
如果我们在初始化中,初始化了中断,就会有一个对应的中断服务函数,当中断的条件满足了,程序就会暂时终止main函数里面的内容,去把中断服务函数里面的内容执行完毕了再回来运行main里面的内容。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
同时,从上图可以看出,当中断被初始化后,任何时刻都可能满足中断的条件,也就是在任何位置都可能会跳出main里面的内容去执行中断服务函数里面的内容。因此说它产生的时间和地点是未知的。
此时代码的运行顺序就不固定了可能是从任务1跳出去执行任务3任务4后再回来执行任务2

也有可能是直接就先去执行任务3,任务4后再回来执行任务1和任务2,等等还有多种可能,也就是只要初始化中断后,任何时刻,只要中断信号满足了,CPU就需要优先解决中断事件。
这正是中断的意义所在,类似上面的串口接收,没有中断的话,需要一直死等,这会严重影响其他函数的运行,引入中断后,就可以通过中断来判断是否接收完成了。那么具体是怎么配置的呢。
通过上面的流程图,大致分析一下,首先,肯定需要在初始化中初始化中断,然后是需要一个中断服务函数来存放异常事件,此外还需要判断对应的中断事件有没有产生。

串口的接收中断以及空闲中断

本文主要是接着串口先体验一下中断的作用,关于STM32中断系统的详细介绍放到下一篇中。
在配置串口的接收和发送的时候,串口相关的有很多寄存器是直接跳过了的,这里面就有一部分是和中断相关的,
首先,来看控制寄存器1(USART1-CR[0])的第四到第八位都是中断相关的使能位,也就是说,只要在代码中配置了相关的位,就可以在其描述的时刻产生对应的中断信号,这里面常用的就是第4位的空闲中断和第5位的接收完成中断;第4位的中断信号是在接收完成后一段时间内再也没有数据接收了就会被触发,所以叫做空闲中断的使能;而第5是接收中断,也就是每次接收完一个字符后就会触发一次中断;编程者可以使用这两个中断实现非阻塞的串口数据接收。当接收中断产生的时候就读取接受的字符到数组,当产生空闲中断的时候就在数组末尾添加结束标志。这样就省去了结束符,也解决了接收死等的问题。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
串口的接收中断:
触发条件是:串口的DR寄存器接收到了值,接收到触发信号后,会将信号传递给NVIC控制器,NVIC控制器会将CPU调到中断服务函数中使用。
串口的空闲中断:
触发条件:在串口接收数据过程中,如果有一段事件串口停止工作了会产生触发信号。
在串口工作结束的时候产生一次中断请求
接收到字符串的时候没有结束条件,可以使用空闲中断作为判断字符串结束的条件
只要使用串口的模块,大部分都需要接收数据,但是如果使用基础的串口函数都会造成阻塞

实现代码

首先来分析一下实现流程,伪代码:

串口1的初始化函数
{
   //打开时钟
   //GPIO控制器配置
   //USART1的控制器配置
    加上串口的接收中断使能
   //NVIC控制器的配置
    优先级分组   ------   主函数的初始化之上
    优先级合成
    优先级分配
    使能中断源
}

在nvic.c中写中断服务函数即可
Void 串口1的中断服务函数名(void)
{
   If(USART1->SR & (1<<5))  //开启了CR1中的接收中断使能
   {
     //清除标志位 //读取DR寄存器  同一个步骤
	}
}

首先,肯定是需要实现串口的基础收发功能的,这个步骤在之前已经实现了,然后再其基础上开启串口的接收中断使能和空闲中断使能,也就是使能对应寄存器的第4位和第5位。
操作代码如下:

		USART1->CR1  |= (1<<5);   //使能接收中断
		USART1->CR1  |= (1<<4);   //使能空闲中断

需要将这两行代码添加到如下图所示的位置。
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
其实到这里有关串口中断的配置已经完成了,但是为了方便后面的对中断的管理,还需要对中断进行分组和设置优先级,原因是在整个系统中,中断不可能只有一个,为了避免中断之间的冲突,就需要对中断的优先级进行管理,也正是为了方便管理,stm32的所有中断都有固定的命名,具体的管理和分组留到下一篇中在做介绍,在本文只需要知道这一段代码的作用就是设置中断分组、优先级,还有就是打开对应的中断使能即可。
代码如下:

//NVIC控制器配置
		u32 pri=NVIC_EncodePriority(7-2,0,0);//设置抢占优先级为0,响应优先级为0
		NVIC_SetPriority(USART1_IRQn,pri);   //将对应的优先级设置映射到对应的中断
		NVIC_EnableIRQ(USART1_IRQn);         //使能中断

此段代码的位置如下:
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
除此之外,还需要添加一句在所有的初始化之前的的中断分组函数

NVIC_SetPriorityGrouping(7-2);//设置中断的优先组别(111-110-101-100)

stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
至此,有关串口中断的初始化部分就搞定了,然后就是中断服务函数,中断服务函数的名称也是固定的,必须使用.s文件内的命名才可以,否则会找不到的,而且中断服务函数在使用的时候不需要调用、声明,也没有返回值、形参。只需要按照.s的函数名命名即可。
具体代码如下:
还是需要在对应文件夹下新建Nvic.c和Nvic.h。并将Nvic.c添加到工程,在主函数的main.h添加Nvic.h的头文件。

#include "Nvic.h"
UsartStruct Usart1_Receive;//声明结构体
/*******************************
函数名:USART1_IRQHandler
函数功能:串口一的中断服务函数函数
函数形参:无
函数返回值:void
备注:
********************************/
void USART1_IRQHandler(void)
{
	if(USART1->SR & (1<<5))//判断接收中断
	{
		Usart1_Receive.Rebuf[Usart1_Receive.ReBytes] = USART1->DR;
		Usart1_Receive.ReBytes++;
 	}
	if(USART1->SR & (1<<4))//判断空闲中断
	{
		USART1->SR;
		USART1->DR;//清除标志位
		Usart1_Receive.Rebuf[Usart1_Receive.ReBytes]='\0';
		Usart1_Receive.ReBytes=0;
		Usart1_Receive.UsartFlag=1;
	}
}

Nvic.h

#ifndef _NVIC_H__
#define _NVIC_H__
#include "stm32f4xx.h"
#define	RBUF_MAX				50
typedef struct
{
	void (*Usart_Recelve_Process)(void);//串口数据处理函数
	u8 	Rebuf[RBUF_MAX];        //串口接收数据数组
	u16 ReBytes;                //串口接收数据长度 
	u8 	UsartFlag;				//串口接收到数据标志位
	
}UsartStruct;
extern UsartStruct Usart1_Receive;//外部声明
#endif

实现效果

为了方便看见效果,这里写了一个小需求,使用串口调试助手发送“open”,打开LED1和LED2,并通过printf返回小灯状态,发送“close”,关闭LED1和LED2。
注意需要使用到“String.h”里面的字符串处理函数,要将这个头文件添加到main.h
具体的main.c内容如下:

#include "main.h"

int main(void)
{
/*------------------变量定义区--------------------------*/

/*------------------初始化外设区------------------------*/
	NVIC_SetPriorityGrouping(7-2);//设置中断的优先组别(111-110-101-100)
	Led_Init();
	Key_Init();
	Usart1_Init(115200);
	printf("系统初始化完毕\r\n");
/*------------------单次运行区--------------------------*/	
	
	while(1)//防止程序跑飞
	{
/*------------------主循环区--------------------------*/	
		if(Usart1_Receive.UsartFlag==1)
		{
			Usart1_Receive.UsartFlag=0;
			if(strcmp((const char*)Usart1_Receive.Rebuf,"open")==0 )
			{
				LED_1_ON;
				LED_2(1);
				printf("小灯已打开\r\n");
			}
			if(strcmp((const char*)Usart1_Receive.Rebuf,"close")==0)
			{
				LED_1_OFF;
				LED_2(0);
				printf("小灯已关闭\r\n");
			}
		}
	}
}

最终运行效果:
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断
stm32串口接收字符串并处理,嵌入式 —M4,stm32,单片机,学习,嵌入式,中断

总结

本文主要是进一步完善串口的收发功能函数,并引出串口中断的相关使用办法,由于基础的串口接收时会有阻塞作用,会导致while(1)内的其他功能无法正常运行,就像流水灯、按键扫描这些都会收到影响,所以就引入了串口中断,当对应的中断信号到了,说明已经接收到了数据,此时CPU跳出去先把数据接收,再回来继续运行while(1)的内容,这样就巧妙地回避了一直等待接受的过程。这一篇中代码有些多,写的也有些混乱,大家有建议可以指出,以错误之处也欢迎提出。

M4系列目录

1.嵌入式学习笔记——概述
2.嵌入式学习笔记——基于Cortex-M的单片机介绍
3.嵌入式学习笔记——STM32单片机开发前的准备
4.嵌入式学习笔记——STM32硬件基础知识
5.嵌入式学习笔记——认识STM32的 GPIO口
6.嵌入式学习笔记——使用寄存器编程操作GPIO
7.嵌入式学习笔记——寄存器实现控制LED小灯
8.嵌入式学习笔记——使用寄存器编程实现按键输入功能
9.嵌入式学习笔记——STM32的USART通信概述
10.嵌入式学习笔记——STM32的USART相关寄存器介绍及其配置
11.嵌入式学习笔记——STM32的USART收发字符串及串口中断
12.嵌入式学习笔记——STM32的中断控制体系
13.嵌入式学习笔记——STM32寄存器编程实现外部中断
14.嵌入式学习笔记——STM32的时钟树
15.嵌入式学习笔记——SysTick(系统滴答)
16.嵌入式学习笔记——M4的基本定时器
17.嵌入式学习笔记——通用定时器
18.嵌入式学习笔记——PWM与输入捕获(上)
19.嵌入式学习笔记——PWM与输入捕获(下)
20.嵌入式学习笔记——ADC模数转换器
21.嵌入式学习笔记——DMA
22.嵌入式学习笔记——SPI通信
23.嵌入式学习笔记——SPI通信的应用
24嵌入式学习笔记——IIC通信文章来源地址https://www.toymoban.com/news/detail-778408.html

到了这里,关于嵌入式学习笔记——STM32的USART收发字符串及串口中断的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式学习笔记——STM32的时钟树

    在之前的所有代码编程的过程中,似乎每次都绕不开一个叫做时钟使能的东西,当时我们是在数据手册上直接看其挂接在那条时钟线上的,那么STM32内部的时钟到底是怎么一个构型呢,本文来对此做一个介绍。 老规矩,一个新的名词出现,首先需要搞清楚它是个啥,下图中对

    2024年02月02日
    浏览(48)
  • 嵌入式学习笔记——STM32硬件基础知识

    上一篇中我们重点是讲了一下怎么搭建开发环境以及怎么下载烧录的过程,这都是解决的电脑端的开发环境问题,还没有到实际的开发板上,我们的单片机是都是焊接在开发板上的,PCB上有着它所需的工作电路。并不是直接给供电电压就可以让其工作的,本文主要是简介一下

    2024年01月22日
    浏览(55)
  • 【嵌入式知识08】STM32的USART串口通信,给上位机连续发送Hello Windows!

    本文主要介绍串口协议和RS-232、485标准,以及RS232、485电平与TTL电平的区别,了解\\\"USB/TTL转232\\\"模块的工作原理;并完成一个STM32的USART串口通讯程序。   串口通信(Serial Communication)的概念非常简单,串口按位(bit)发送和接收字节的通信方式。尽管比按字节(byte)的并行通信

    2024年02月13日
    浏览(40)
  • 【STM32】STM32学习笔记-USART串口收发HEX和文本数据包(29)

    串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式, 电子工程师在调试设备时也经常使用该通讯方式输出调试信息。 在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和

    2024年01月19日
    浏览(39)
  • 蓝桥杯嵌入式CT117E-M4学习笔记02-STM32G431RBT6芯片学习

    首先学习了解一下蓝桥杯嵌入式CT117E-M4开发板的主控芯片STM32G431RBT6,本文仅为个人学习成果总结,如有错误,恳请指正。 上图为STM32CubeMX选型界面,如图可以看出STM32G431RBT6具有以下特点和硬件集成。 采用Cortex-M4 32位RISC核心架构,工作频率最高可达170Mhz。 128kBytes的FLASH,32

    2023年04月09日
    浏览(48)
  • STM32的时钟系统(嵌入式学习)

    时钟是指用于计量和同步时间的装置或系统。时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作,外设部件在时钟的驱动下完成各种工作,例如:串口数据的发送、AD转换、定时器计数等。因此时钟对于计算机系统是至关重要的,通常时钟系

    2024年02月16日
    浏览(41)
  • STM32串口通信详解(嵌入式学习)

    时钟信号在电子领域中是指用于同步和定时电路操作的周期性信号。它在数字系统和通信系统中起着至关重要的作用,用于协调各个组件之间的数据传输和操作。 时钟信号有以下几个重要的方面: 频率:时钟信号的频率是指单位时间内信号周期的数量。它通常以赫兹(Hz)为

    2024年02月09日
    浏览(58)
  • STM32的中断系统详解(嵌入式学习)

    中断是处理器中的一种机制,用于响应和处理突发事件或紧急事件。当发生中断时,当前正在执行的程序会被暂时中止,处理器会跳转到中断处理程序(也称为中断服务例程),对中断事件进行处理。处理完中断后,处理器再返回到被中断的程序继续执行。 中断可以分为内部

    2024年02月12日
    浏览(61)
  • STM32开发环境搭建&工程创建(嵌入式学习)

    简介 STM32CubeMX是STMicroelectronics公司提供的一款集成开发环境(IDE)工具,用于快速配置和初始化STM32微控制器系列的软件工程。它提供了图形化界面和交互式工具,使开发者能够轻松地生成STM32微控制器的初始化代码和配置文件。 STM32CubeMX具有以下主要功能和特点: 微控制器

    2024年02月11日
    浏览(54)
  • 嵌入式学习stm32基础知识(期末复习)

    1. 计算机的体系架构 冯诺依曼架构 ​ 在完整的计算机系统中,包含五个部分,储存器,运算器,控制器输入设备和输出设备。 改进的冯诺依曼架构 改进型架构的各模块的高速数据交换中心利用储存器这个大容量,极大的提高了效率。 哈佛架构 ​ 哈佛结构数据空间和地址

    2024年02月07日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包