本文主要是介绍串口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定时器、外部中断的关系以及超声波实现身高测量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!