这次我们将实践定时器捕获和定时器比较输出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);
//CR1
TIM4->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);
//SMCR
TIM4->SMCR &= ~(7<<0);
//DIER
TIM4->DIER |= (1<<1);
TIM4->DIER |= (1<<0);
//CCMRx
TIM4->CCMR1 &= ~(3<<0);
TIM4->CCMR1 |= (1<<0);
TIM4->CCMR1 &= ~(0xf<<4);
TIM4->CCMR1 |= (0xf<<4);
//CCER
TIM4->CCER |= (1<<0);
TIM4->CCER &= ~(1<<1);
TIM4->CCER &= ~(1<<3);
//PSC
TIM4->PSC = 84-1;
//ARR
TIM4->ARR = 65535;
//EGR
TIM4->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);
//CR1
TIM14->CR1 |= (1<<7); //重载影子寄存器
TIM14->CR1 &= ~(1<<1); //产生更新事件的条件
//CCMRx
TIM14->CCMR1 &= ~(3<<0); //将通道定位输出模式
TIM14->CCMR1 |= (1<<3); //比较寄存器影子寄存器
TIM14->CCMR1 &= ~(7<<4);
TIM14->CCMR1 |= (6<<4); //PWM1
//CCER
TIM14->CCER |= (1<<0); //开启通道
TIM14->CCER &= ~(1<<1); //高电平有效
//PSC
TIM14->PSC = 84-1;
//ARR
TIM14->ARR = 20000-1;
//CCRx
TIM14->CCR1 = 500;
//EGR
TIM14->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度)
//判断更新中断标志Wie
if(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;
//切换边沿2
TIM4->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);
//再次切换边沿1
TIM4->CCER &= ~(1<<1);
}
}
}
定时器7中断函数
u32 timer7_cont[10];
void TIM7_IRQHandler(void)
{
//判断是TIM7的定时完成中断信号触发
if(TIM7->SR & (1<<0))
{
//清除标志Wie
TIM7->SR &= ~(1<<0);
//紧急事件
timer7_cont[1]++;
if(timer7_cont[1]>=100)
{
timer7_cont[3] = 0;
SR04_star();
}
}
}