串口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

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import