一,时基单元

计数器计数脉冲,自动重装寄存器ARR设置定时的周期。

RCR设置重复计数的次数。

计数的方式:上计数,下计数,中心对齐

1us也就是10-6s,也就是1Mhz。经过预分频器PSC到10Mhz。周期ARR设置为999

假设72M,PSC设置为71----1MHz

重复计数器RCR用来设置重复计数的次数,现在要求每发生一次就溢出,RCR=0。

updata标志位

二,自制延迟函数

之前的delay函数是提供系统滴答定时器SysTick来构建的。

每1ms一次中断,中断响应函数里面currentTick++  updata标志位!!!!

#include "stm32f10x.h"
volatile uint32_t currentTick =0;//记录当前时间,单位是ms
void App_Delay(uint32_t ms);
void App_TIM3_TimeBaseInit(void);//定时器3的时基单元
void TIM3_IRQHandler(void);//中断响应函数,每1ms,updata标志位触发中断,currenttick++
void My_OnBoardLED_Init(void);

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置中断优先级分租2
  My_OnBoardLED_Init();
	App_TIM3_TimeBaseInit();
	while(1)
	{
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
		App_Delay(500);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
		App_Delay(500);
	}
}


//延迟一段时间,ms:要延时的时间
void App_Delay(uint32_t ms)
{ 
	uint32_t expireTime = currentTick + ms;//当前时间+要延迟的时间
	while(currentTick < expireTime);//等待延迟的结束,每1ms都会跳转去执行中断。

}
void App_TIM3_TimeBaseInit(void)
{
   //  使能定时器3的时钟
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	  //  初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
    TIM_TimeBaseInitStruct.TIM_Period = 999;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//上计数
	  TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
	
	 //  使能定时器,闭合开关
    TIM_Cmd(TIM3, ENABLE);
	
	 // 使能Update中断
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);//Updata标志位
	 // 5. 配置NVIC内部中断
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);


}


void TIM3_IRQHandler(void)//中断响应函数
{
    if(TIM_GetFlagStatus(TIM3, TIM_FLAG_Update) == SET)//标志位有效了
    {
        TIM_ClearFlag(TIM3, TIM_FLAG_Update);
        currentTick++;
    }
}


void My_OnBoardLED_Init(void)
{
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
 GPIO_InitTypeDef GPIO_InitStruct={0};
 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_OD;
 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;
 GPIO_Init(GPIOC,&GPIO_InitStruct);
  // 初始状态设为关闭(根据你的开发板电路决定)
    GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}
	

三,输出比较

1,PWM原理

PWM--pulse width modulation 脉冲宽度调制

一种周期固定,占空比可以调节的信号。

当CNT的值小于CCR的值的时候,输出比较输出高电压;当CNT的值大于CCR的值的时候,会输出低电压。

2,八种工作模式

3,互补输出

为什么需要互补输出?

sw1和sw2打开关闭相反,也就是一对开关交替导通。

4,极性选择

根据正常输出/互补输出来选择极性。

正极性---都选择正极性,都走上面那条路,负极性,走下面带有非号的那条路

如果都选择正极性,上面正常输出,下面互补输出

四,呼吸灯实验

通过两个led,分别产生正常输出pwm和互补输出pwm。

sin范围-1-1,加一后0-2 乘0.5后0-1

占空比就等于0.5(sin(2Πt)+1)

通过调整pwm波的占空比来模拟正弦波--从而来实现呼吸灯的效果。

CH1--正常输出   CH1N互补输出    PA8和PB13

ARR决定了周期,CCR决定了占空比

#include "stm32f10x.h"
#include "delay.h"
#include "math.h"
void App_PWM_Init(void);

int main(void)
{
	App_PWM_Init();
	
	while(1)
	{
		float t=GetTick()*1.0e-3f;
		float duty=0.5*(sin(2*3.14*t)+1);
		uint16_t ccr = duty * 999;  // 计算CCR值,周期×占空比得到高电平的时间 (占空比 * ARR)---比较值
    TIM_SetCompare1(TIM1, ccr);  // 更新PWM占空比
	}
}

void App_PWM_Init(void){
	
	// 1. 初始化GPIO为复用推挽模式(用于定时器PWM输出)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;  // TIM1_CH1对应PA8
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
	  
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;  // TIM1_CH1对应PB13
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
	  
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
	   // 2. 初始化定时器时基单元 1ms,分辨率1us
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 71;  // 72分频 (72MHz/72=1MHz)
    TIM_TimeBaseInitStruct.TIM_Period = 999;  // 计数周期1000 (ARR=999)
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;  // 不重复计数
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
		
		//配置ARR寄存器预加载
		TIM_ARRPreloadConfig(TIM1,ENABLE);
		//闭合开关
		TIM_Cmd(TIM1,ENABLE);
		
		
		  // 4. 初始化输出比较通道1 (PWM模式)
    TIM_OCInitTypeDef TIM_OCInitStruct;
		
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;  // 八种模式选择-PWM模式1
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;  // 正极性
		TIM_OCInitStruct.TIM_OCNPolarity=TIM_OCPolarity_High;   //互补输出级也是正
		TIM_OCInitStruct.TIM_OutputNState=TIM_OutputNState_Enable;   //互补输出及使能
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;  // 输出使能
    TIM_OCInitStruct.TIM_Pulse = 0;  // CCR初始值
		
    TIM_OC1Init(TIM1, &TIM_OCInitStruct);
		
		//闭合MOE总开关
		TIM_CtrlPWMOutputs(TIM1,ENABLE);
		
		TIM_CCPreloadControl(TIM1,ENABLE);//开启预加载
		
		
	
}

五,输入捕获

  1,输入捕获的基本工作原理

  

   输入捕获了上升沿或者下降沿,向右触发ccx事件(cc1,cc2.....),会把CNT的值给到CCR,当我们读取CCR寄存器的数值的时候,其实就是输入信号变化的时间。

  2,输入滤波

  3,边沿检测

选择上升沿和下降沿

  4,信号选择

TRC是来自从模式控制器。直接和间接

从本通道获取信号称为直接,从另外的通道获取信号称为间接

这样交叉引用的好处,省了输入通道。

  5,分频

六,超声波测距实验

1,HCSR-04模块的使用方法

一眼发生一眼接收

发送超声波的时候Echo会立刻输出高电压,当接收完全部(8个周期)会输出低电压。

脉冲宽度----距离

利用定时器的输入捕获功能来测量高电平。

A8--TIM1_CH1---输入捕获

PA0--启动测量--不少与10us的电平,不需要特别精确,只需要普通IO引脚就可以。

2,思路梳理

刚刚把Echo接到了TIM1_CH1的输入端,上升沿对于时间保存到CCR1,下降沿的时间保存到了CCR2中了。

等待CC1和CC2都为1的时候,就可以了。

精度3mm,3mm/340m/s===8us。取1us完全满足要求

周期取越长越好,对于时基单元来说都是16位寄存器  0-65535。

ARR=65535,使得周期达到最大值。

3,初始化时基单元,初始化输入捕获

4,完整代码

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"

void My_USART_Init(void);
 void App_HCSR04_Init(void);

int main(void)
{
  My_USART_Init();
	App_HCSR04_Init();
	My_USART_SendString(USART1,"Hello world. \r\n");//串口没有问题
	while(1)
	{
		// 1. 清除捕获标志位
    TIM_ClearFlag(TIM1, TIM_FLAG_CC1);
    TIM_ClearFlag(TIM1, TIM_FLAG_CC2);

    // 2. 复位计数器
    TIM_SetCounter(TIM1, 0);
    TIM_GenerateEvent(TIM1, TIM_EventSource_Update);

    // 3. 使能定时器
    TIM_Cmd(TIM1, ENABLE);

    // 4. 发送10us触发脉冲
    GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
    DelayUs(10);  // 需要实现微秒级延时函数
    GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);

    // 5. 等待回波信号
    while(TIM_GetFlagStatus(TIM1, TIM_FLAG_CC1) == RESET);  // 等待上升沿
    while(TIM_GetFlagStatus(TIM1, TIM_FLAG_CC2) == RESET);  // 等待下降沿
    
    TIM_Cmd(TIM1, DISABLE);

    // 6. 计算距离(单位:米)
    uint16_t t1 = TIM_GetCapture1(TIM1);  // 上升沿时间
    uint16_t t2 = TIM_GetCapture2(TIM1);  // 下降沿时间
    float distance = (t2 - t1) * 1e-6f * 340.0f / 2.0f;  // 距离=(时间差×声速)/2
		
		My_USART_Printf(USART1,"distance = %.4f\r\n",distance);
		Delay(100);
	}
}


void My_USART_Init(void)//三部:时钟初始化,定义结构体变量,结构体变量赋值,初始化函数,开启串口函数
	{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
		//串口引脚的初始化
	GPIO_InitTypeDef GPIO_InitStruct = {0};//**变量+变量名**

	
	//TX PA9
  GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
  GPIO_Init(GPIOA,&GPIO_InitStruct);
	//RX PA10
  GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
  GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	USART_InitTypeDef USART_InitStruct={0};
	
	USART_InitStruct.USART_BaudRate = 115200;                     // 波特率115200
  USART_InitStruct.USART_WordLength = USART_WordLength_8b;      // 8位数据
  USART_InitStruct.USART_StopBits = USART_StopBits_1;           // 1位停止位
  USART_InitStruct.USART_Parity = USART_Parity_No;              // 无校验
  USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;  // 双向通
	
	USART_Init(USART1, &USART_InitStruct);  // 初始化USART1
	
	USART_Cmd(USART1, ENABLE);  // 启动USART1
	}
	
	
	
 void App_HCSR04_Init(void)
{
    // 初始化Trig引脚(PA0)为输出推挽模式
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 初始化Echo引脚(PA8)为输入下拉模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;  // 输入下拉
    GPIO_Init(GPIOA, &GPIO_InitStruct);

     //  定时器时基配置
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

   
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 71;       // 72MHz/72=1MHz (1us分辨率)
    TIM_TimeBaseInitStruct.TIM_Period = 65535;        // 最大计数周期
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
    
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_GenerateEvent(TIM1, TIM_EventSource_Update);

    
    // 设置输入滤波分频
    TIM_SetClockDivision(TIM1, TIM_CKD_DIV2);

    // 配置输入捕获通道1(上升沿捕获)
    TIM_ICInitTypeDef TIM_ICInitStruct;
    TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStruct.TIM_ICFilter = 0x08;             // 滤波系数
    TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;//直接
    TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInit(TIM1, &TIM_ICInitStruct);

    // 配置输入捕获通道2(下降沿捕获)
    TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
    TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
    TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;//间接
    TIM_ICInit(TIM1, &TIM_ICInitStruct);

    // 9. 使能捕获通道
    TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Enable);
    TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Enable);
}

七,从模式控制器

八,PWM参数测量原理

九,PWM参数测量实验

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐