串口printf输出、systick、time定时器、外部中断的关系以及超声波实现身高测量

本文主要是介绍串口printf输出、systick、time定时器、外部中断的关系以及超声波实现身高测量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

printf重定向 

上次提到printf重定向问题,其实在usart.c代码中就有:

//重定义fputc函数   printf 是一个宏
int fputc(int ch, FILE *f)
{ 	//其实调printf就是调用串口3发送数据USART_SendData(USART3,ch);while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);   //等待发送完成   return ch;
}

 

这段代码是在重新定义标准库函数 `fputc`。在这段代码中,`fputc` 函数被重写,以便将字符发送到串口3(USART3)(因为这里使用到的是串口3)

1. `int fputc(int ch, FILE *f)`: 这是 `fputc` 函数的新定义,它接受一个字符 `ch` 和一个文件指针 `f` 作为参数,并返回一个整数。

2. `USART_SendData(USART3,ch);`: 这行代码调用了一个函数 `USART_SendData()`,它用于将数据发送到 USART3 串口。`ch` 是要发送的字符。

3. `while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);`: 这是一个循环,它等待 USART3 发送缓冲区变为空。这是因为在向串口发送数据时,需要等待之前发送的数据完全被传输完成,才能发送下一个字符。

4. `return ch;`: 返回发送的字符 `ch`。

总之,这段代码的作用是重新定义 `fputc` 函数,使其将字符发送到 USART3 串口。这样,在使用 `printf` 输出时,实际上将字符发送到串口,而不是标准输出设备(比如屏幕)。

这就是printf重定向。这段代码实现了 printf 的重定向。在嵌入式系统中,常常需要重定向 printf,以便将输出发送到串口,而不是标准输出设备(比如终端或显示屏)。通过重定义 `fputc` 函数,可以实现这样的重定向,让 printf 输出的内容通过串口发送出去

SysTick定时器、Time定时器和外部中断(EXTI)的关系和区别

当谈到STM32微控制器中的SysTick定时器、Time定时器和外部中断(EXTI)时,我们可以将它们比作不同类型的闹钟或计时器,每个都有自己的特点和用途。

1. SysTick定时器:

可以想象成一个特殊的时钟,它专门用于帮助微控制器跟踪时间。这个定时器内置在STM32芯片中,它可以按照你的要求定期触发中断,就像闹钟一样。你可以设置它,让它每隔一段时间(比如1毫秒)就“叮咚”一声,提醒微控制器去执行某些任务,比如更新LCD显示或者检查传感器数据。这个定时器不需要你手动去计数时间,它会自动进行计时,因此它非常适合用于需要精确计时的应用,比如实时操作系统(RTOS)的任务调度。

因此,systick定时器用来写delay()函数。

2. Time定时器:

可以把它想象成一个普通的计时器,你可以自己按下按钮来启动它,然后它会开始计时。这个计时器不同于SysTick定时器,它需要你手动设置它的开始和结束时间。Time定时器通常用于测量两个事件之间的时间间隔,比如测量超声波的飞行时间,或者生成PWM信号来控制电机的转速。它的功能更加灵活,可以根据你的需要来启动、停止和重置计时。

3. 外部中断(EXTI):

可以把它想象成一个触发器,当某个事件发生时(比如按下按钮或者传感器检测到物体),它就会像按下闹钟的按钮一样,立即通知微控制器。外部中断可以让微控制器在需要的时候立即停下手中的任务,去处理紧急事件,比如按键输入或者检测到紧急情况。它的响应速度非常快,因为它可以立即中断微控制器当前的工作,去执行中断服务程序,而不需要等待某个特定的时间间隔。

总的来说,SysTick定时器用于精确计时和周期性任务的调度,Time定时器用于手动计时和测量时间间隔,而外部中断用于实时响应外部事件。

NVIC是什么?

 

NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)是一个关键的系统组件,用于管理中断。

它是 ARM Cortex-M 处理器内核的一部分,负责管理各种中断并进行优先级的调度

1. 中断管理:NVIC 负责管理 STM32 微控制器上所有可能的中断源,包括外部中断、内部中断和异常。它可以为每个中断源分配优先级,并控制中断的使能和禁用。

2. 中断优先级:NVIC 允许用户为每个中断源分配优先级,以确保在多个中断同时发生时,系统可以按照一定的优先级顺序进行响应。优先级越高的中断将优先被处理。

3. 中断响应:当一个中断发生时,NVIC 负责检查中断源的优先级,并确定是否需要立即处理该中断。如果有多个中断同时发生,NVIC 会根据优先级来确定哪个中断先被处理。

4. 中断向量表:NVIC 维护了一个中断向量表,其中存储了每个中断源对应的中断服务程序的地址。当一个中断发生时,NVIC 会根据中断号从中断向量表中找到对应的中断服务程序的地址,并跳转到该地址执行相应的处理程序。

它管理着系统中所有的中断源,并负责对中断进行优先级的管理和调度,以确保系统能够及时、有效地响应外部事件。

 什么时候会用到NVIC?

time定时器、exti中断、串口都有用到。

当需要使用外部中断、定时器中断、串口中断等各种类型的中断时,NVIC 就会被用到。通过 NVIC,可以为每个中断源分配优先级、使能或禁用中断,以及配置中断处理程序等

超声波实现身高测量 

hcsr04.c

#include "hcsr04.h"/*
引脚说明:
TRIG -- PA2(输出)
ECHO -- PA3(输入)
*/void Hcsr04_Init(void)
{GPIO_InitTypeDef  		GPIO_InitStruct;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//打开A组时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//1、能定时器时钟。RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_2; 		//引脚9 10GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_OUT;					//输出GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz;					//速度GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;					//推挽GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;						//上拉GPIO_Init(GPIOA, &GPIO_InitStruct);	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_3;	//引脚3GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IN; //输入GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP; //上拉//2、 初始化IO口为输入。GPIO_Init(GPIOA, &GPIO_InitStruct);		TIM_TimeBaseInitStruct.TIM_Prescaler	= 84-1;  				//84分频    84MHZ/84 = 1MHZ  1us数一个数TIM_TimeBaseInitStruct.TIM_Period		= 50000-1;				//计数50000TIM_TimeBaseInitStruct.TIM_CounterMode	= TIM_CounterMode_Up; 	//向上计数TIM_TimeBaseInitStruct.TIM_ClockDivision= TIM_CKD_DIV1;			//分频因子//2、初始化定时器,配置ARR,PSC。TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);	//5、不使能定时器。TIM_Cmd(TIM4, DISABLE);}u16 Get_Hcsr04_Value(void)
{u16 count, value;//PA2给低电平GPIO_ResetBits(GPIOA, GPIO_Pin_2);  delay_us(5); //A2给高电平GPIO_SetBits(GPIOA, GPIO_Pin_2);  delay_us(15); //PA2给低电平GPIO_ResetBits(GPIOA, GPIO_Pin_2);  TIM4->CNT=0;  	//清空计数器while( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) == 0 );  //等待PA3高电平到来//开定时器;TIM_Cmd(TIM4, ENABLE);while( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) == 1 );  //等待PA3低电平到来//获取CNT的值;count = TIM4->CNT;//关闭定时器TIM_Cmd(TIM4, DISABLE);//距离 = 定时的CNT/58;value = count/58;return value;}

在这,使用time函数的作用

正如上面说的,就是一个普通计时器。

TIM4 定时器被用于测量超声波传感器 HCSR04 接收到回波信号的时间。通过测量回波信号的时间,可以计算出超声波从传感器发射出去并返回的距离

具体来说,定时器 TIM4 被配置为向上计数模式,使用定时器的计数器寄存器(TIM4->CNT)来记录计数的时间。

当超声波发射信号后,开始计时,直到接收到回波信号时停止计时。

通过测量计数器的计数值,可以得知超声波的飞行时间,从而计算出距离。

在代码中的具体步骤是:
1. 给超声波模块的 TRIG 引脚(PA2)一个短暂的低电平信号,然后立即给一个高电平信号,以触发超声波的发射。
2. 开始计时,直到接收到超声波的回波信号,此时 ECHO 引脚(PA3)会变为高电平。
3. 记录计数器的值(TIM4->CNT),这个值与超声波的飞行时间成正比。
4. 停止计时器,以便下一次测量。

通过将测量到的计数值除以一个常数(58),可以得到超声波传播的距离(单位为厘米)。这个常数是超声波在空气中每走过一个微秒所移动的距离,是一个经验值。

因此,TIM4 定时器在这段代码中的作用是用于测量超声波的飞行时间,从而计算出超声波传感器到目标物体的距离。

hcsr0.h

#ifndef __HCSR04_H
#define __HCSR04_H
#include "stm32f4xx.h"
#include "delay.h"void Hcsr04_Init(void);
u16 Get_Hcsr04_Value(void);#endif

 main.c

#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "delay.h"
#include "pwm.h"
#include "usart.h"
#include "string.h"
#include "hcsr04.h"#define LED0_ON 	GPIO_ResetBits(GPIOF,GPIO_Pin_9)      	//开灯
#define LED0_OFF 	GPIO_SetBits(GPIOF,GPIO_Pin_9)      	//关灯u8 Usart_Data;
u8 rx_flag = 0;  		//表示串口接收标志 rx_flag = 1表示接收完成 rx_flag = 0未完成
u8 buffer[64] = {0};	//接收存储数据数组
u8 rx_buffer[64] = {0};	//接收存储数据数组
u8 rx_i,rx_count=0;void USART1_IRQHandler(void)
{//若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){	/* DR读取接受到的数据*/buffer[rx_count++] = USART_ReceiveData(USART1);	 //先赋值再加if(buffer[rx_count-1] == ':')  //判断是否接收到结束标志{for(rx_i=0; rx_i<rx_count-1; rx_i++){rx_buffer[rx_i] = buffer[rx_i]; //将数据存储在rx_buffer数组中}rx_flag 	= 1; 	//rx_flag = 1表示接收字符串完成rx_count = 0;memset(buffer, 0, sizeof(buffer));}//判断为真后,为下次中断做准备,则需要对中断的标志清零USART_ClearITPendingBit(USART1,USART_IT_RXNE);	   }	}//这是一个主函数
int main(void)
{u16 value = 0;//NVIC分组 抢占优先级两位:0~3  响应优先级两位:0~3 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);Delay_Init();Led_Init();Usart1_Init();Hcsr04_Init();//GPIO_ToggleBits(GPIOE,GPIO_Pin_14);while(1){value = Get_Hcsr04_Value();printf("身高为:%d\n",value);delay_s(1);}return 0;
}

 README

这里用到串口(在上篇usart.c和usart.h),通过实验,超声波测量的距离会打印到串口上。

这篇关于串口printf输出、systick、time定时器、外部中断的关系以及超声波实现身高测量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/840252

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal