这次我们将实践定时器捕获和定时器比较输出PWM,完成超声波测距控制舵机控制
程序思路
超声波每100ms进行检测 (通过定时器7每一毫秒中断),如果检测的距离小于5cm ,我们就将舵机,进行比较寄存器加特定值,控制舵机转动。(定时器4中断服务函数)
首先我们将用到定时器TIM4,TIM7,TIM14 (由于IO口的复用),将TIM7做为定时中断(超声波多少秒进行检测),TIM4用与输入捕获用于捕获超声波反馈回来的脉冲,TIM14用于输出PWM控制舵机
主函数
int main(void){NVIC_SetPriorityGrouping(5);usart1_init(115200);SR04_init();tim14_ch1_pwm();timer7_interrupt_ms_init(1);BEEP_ON;delay_ms(300);BEEP_OFF;while(1){}return 0;}
初始化函数
超声波初始化
void SR04_init(void){tim4_ch1_input_init();RCC->AHB1ENR |= (1<<1);GPIOB->MODER &= ~(3<<16);GPIOB->MODER |= (1<<16);GPIOB->ODR &= ~(1<<8);}
超声波开始函数
void SR04_star(void){GPIOB->ODR |= (1<<8);timer6_delay_us(10);GPIOB->ODR &= ~(1<<8);}
定时器4通道捕获(初始化定时器输入捕获)
void tim4_ch1_input_init(void){/*IO口控制器配置*///端口时钟使能RCC->AHB1ENR |= (1<<1);//端口模式配置GPIOB->MODER &= ~(3<<12);GPIOB->MODER |= (2<<12);//上下拉配置GPIOB->PUPDR &= ~(3<<12);//复用功能配置GPIOB->AFR[0] &= ~(0xf<<24);GPIOB->AFR[0] |= (2<<24);/*定时器配置*///定时器时钟使能RCC->APB1ENR |= (1<<2);//CR1TIM4->CR1 &= ~(3<<8);TIM4->CR1 |= (1<<7);TIM4->CR1 &= ~(3<<5);TIM4->CR1 &= ~(1<<4);TIM4->CR1 &= ~(1<<3);TIM4->CR1 &= ~(1<<2);TIM4->CR1 &= ~(1<<1);//SMCRTIM4->SMCR &= ~(7<<0);//DIERTIM4->DIER |= (1<<1);TIM4->DIER |= (1<<0);//CCMRxTIM4->CCMR1 &= ~(3<<0);TIM4->CCMR1 |= (1<<0);TIM4->CCMR1 &= ~(0xf<<4);TIM4->CCMR1 |= (0xf<<4);//CCERTIM4->CCER |= (1<<0);TIM4->CCER &= ~(1<<1);TIM4->CCER &= ~(1<<3);//PSCTIM4->PSC = 84-1;//ARRTIM4->ARR = 65535;//EGRTIM4->EGR |= (1<<0);/*NVIC控制器配置*///优先级分组---------主函数//计算优先级编码值u32 pri = NVIC_EncodePriority (5,1,0);//确定具体中断源NVIC_SetPriority(TIM4_IRQn,pri);//使能NVIC响应通道NVIC_EnableIRQ(TIM4_IRQn);//使能计数器TIM4->CR1 |= (1<<0);}
初始化定时器14输出比较(可以作为舵机初始化)
void tim14_ch1_pwm(void){/*IO口控制器*///端口时钟使能RCC->AHB1ENR |= (1<<0);//端口模式配置-----复用输出GPIOA->MODER &= ~(3<<14);GPIOA->MODER |= (2<<14);//输出类型GPIOA->OTYPER &= ~(1<<7);//输出速度GPIOA->OSPEEDR &= ~(3<<14);//上下拉配置GPIOA->PUPDR &= ~(3<<14);//复用功能寄存器GPIOA->AFR[0] &= ~(0xf<<28);GPIOA->AFR[0] |= (9<<28);/*定时器和通道配置*///定时器时钟使能RCC->APB1ENR |= (1<<8);//CR1TIM14->CR1 |= (1<<7); //重载影子寄存器TIM14->CR1 &= ~(1<<1); //产生更新事件的条件//CCMRxTIM14->CCMR1 &= ~(3<<0); //将通道定位输出模式TIM14->CCMR1 |= (1<<3); //比较寄存器影子寄存器TIM14->CCMR1 &= ~(7<<4);TIM14->CCMR1 |= (6<<4); //PWM1//CCERTIM14->CCER |= (1<<0); //开启通道TIM14->CCER &= ~(1<<1); //高电平有效//PSCTIM14->PSC = 84-1;//ARRTIM14->ARR = 20000-1;//CCRxTIM14->CCR1 = 500;//EGRTIM14->EGR |= (1<<0); //人为产生更新事件//计数器是能TIM14->CR1 |= (1<<0);}
舵机初始化函数(直接调用定时器14的初始化,主函数没有调用是因为直接调用了tim14_ch1_pwm()初始化函数)
void sg90_init(void){tim14_ch1_pwm();}
我们再初始化定时器7毫秒级定时中断(每几毫秒触发一次中断)
void timer7_interrupt_ms_init(u16 ms){/*定时器控制器配置*///定时器时钟使能RCC->APB1ENR |= (1<<5);//CR1 (3号位要写0,连续计数; 2号位写0 可以产生中断)TIM7->CR1 |= (1<<7);TIM7->CR1 &= ~(1<<3); //循环计数TIM7->CR1 &= ~(1<<2); //可以产生中断的条件TIM7->CR1 &= ~(1<<1); //可以产生更新事件的条件//DIER中断使能TIM7->DIER |= (1<<0);//分频寄存器TIM7->PSC = 8400-1;//重载值寄存器TIM7->ARR = ms * 10 -1;//人为产生更新事件TIM7->EGR |= (1<<0);//清除中断标志位TIM7->SR &= ~(1<<0);/*NVIC控制器配置*///优先级分组---------主函数//计算优先级编码值u32 pri = NVIC_EncodePriority (5,1,3);//确定具体中断源NVIC_SetPriority(TIM7_IRQn,pri);//使能NVIC响应通道NVIC_EnableIRQ(TIM7_IRQn);//计数器使能TIM7->CR1 |= (1<<0);}
中断函数
定时器4中断服务函数(计数器更新中断信号和捕获寄存器捕获中断信号)
void TIM4_IRQHandler(void){static u8 tim_n = 0;static u16 test_1;u16 test_2;u32 test;float cm_val;static u8 servo_state = 0; // 新增:舵机状态标志(0:0度,1:180度)//判断更新中断标志Wieif(TIM4->SR & (1<<0)){//清除标志位TIM4->SR &= ~(1<<0);//紧急事件tim_n++;}//判断捕获中断标志位if(TIM4->SR & (1<<1)){//清除标志位TIM4->SR &= ~(1<<1);//紧急事件//如果是边沿1触发捕获 上升沿if(!(TIM4->CCER & (1<<1))){tim_n = 0; //开始记录有效溢出次数//记录捕获寄存器的第一次值test_1 = TIM4->CCR1;//切换边沿2TIM4->CCER |= (1<<1);}//如果是边沿2触发捕获 下降沿else if(TIM4->CCER & (1<<1)){//记录捕获寄存器的第二次值test_2 = TIM4->CCR1;//计算脉宽test = tim_n*65535-test_1+test_2;cm_val = test/1000.0 * 34 / 2;if(cm_val <= 5 ){if(servo_state == 0){TIM14->CCR1 = 2500; // 180度位置servo_state = 1;}else{TIM14->CCR1 = 500; // 0度位置servo_state = 0;}}printf("检测距离:%0.1fcm\r\n",cm_val);//再次切换边沿1TIM4->CCER &= ~(1<<1);}}}
定时器7中断函数
u32 timer7_cont[10];void TIM7_IRQHandler(void){//判断是TIM7的定时完成中断信号触发if(TIM7->SR & (1<<0)){//清除标志WieTIM7->SR &= ~(1<<0);//紧急事件timer7_cont[1]++;if(timer7_cont[1]>=100){timer7_cont[3] = 0;SR04_star();}}}
