经过上周的环境搭建完成后点亮了一个灯,这次准备进行部分外设的学习。因为之前买了一个超声波模块一直没有玩过,正好借这次机会来玩一下。虽然这个模块使用起来相对比较简单,但是在不同的平台上对于控制的方式还是有所区分的。
我们先了解一下超声波模块测距离的工作原理:超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播,途中碰到障碍物就立即返回来,超声波接收器收到反射波就立即停止计时。超声波在空气中的传播速度为340m/s,根据计时器记录的时间t,就可以计算出发射点距障碍物的距离(s),即:s=340t/2 。更高端的雷达激光测距等也都是基于这个原理实现的测距。
了解了原理后,我们需要熟悉超声波模块的工作原理和使用方法。我使用的超声波模块型号为HY-SRF05,模块图如下
模块工作原理为:
(1)采用IO触发测距,给至少10us的高电平信号;
(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波从发射到返回的时间,测试距离=(高电平时间*声速(340M/S))/2;
使用方法为:一个控制口发一个10US以上的高电平,就可以在接收口等待高电平输出,一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离。
基于上面的情况了解,我准备通过外部中断配合定时器进行这次实验的实现。
1、选择一个适合的例程去修改达到我们的目的,基于此我选择了外部中断的例程。这样对于我们前期的工作会减少很多,不建议一步一步来,很多时候我们会使用工具也是一种智慧。
2、例程里使用的是PA0的外部中断来控制LED灯的亮灭,现在我们要修改外部中断的配置如下,这里主要修改了外部中断的触发模式。由原来的上升沿改为了上升和下降都出发中断,这样就可以在中断服务函数中得到高电平的时间。
//外部中断0初始化
void exint_line0_config(void)
{
exint_init_type exint_init_struct;
crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOA, GPIO_PINS_SOURCE0);
exint_default_para_init(&exint_init_struct);
exint_init_struct.line_enable = TRUE;
exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
exint_init_struct.line_select = EXINT_LINE_0;
exint_init_struct.line_polarity = EXINT_TRIGGER_BOTH_EDGE;
exint_init(&exint_init_struct);
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(EXINT0_IRQn, 1, 0);
}
//定时器初始化,在这里配置为1s计数10000次,我们计算时间即可计算计数的个数
void tmr1_config(void)
{
crm_clocks_freq_type crm_clocks_freq_struct = {0};
/* get system clock */
crm_clocks_freq_get(&crm_clocks_freq_struct);
crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);
/* (systemclock / (system_core_clock/10000)) / 10000 = 1Hz(1s) */
tmr_base_init(TMR1, 10000-1, system_core_clock/10000-1);
tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);
tmr_clock_source_div_set(TMR1, TMR_CLOCK_DIV1);
tmr_interrupt_enable(TMR1, TMR_OVF_INT, TRUE);
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 1, 1);
}
//初始化超声波模块
void HY_SRF05_init(void)
{
gpio_init_type gpio_init_struct;
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_1;
gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
gpio_init(GPIOA, &gpio_init_struct);
}
3、编写中断服务函数
//定时器服务函数,理论上超声波模块不会引起定时器溢出中断,为了我们的调试方便这里我们加上溢出的LOG
void TMR1_OVF_TMR10_IRQHandler(void)
{
//中断溢出标志
if(tmr_flag_get(TMR1,TMR_OVF_FLAG) != RESET)
{
at32_led_toggle(LED2);
printf("timer1 over flow!\r\n");
tmr_flag_clear(TMR1,TMR_OVF_FLAG);
}
}
//外部中断服务函数,我们在中断之后获取电平,高电平就开始计数,低电平打印结果
void EXINT0_IRQHandler(void)
{
if(exint_flag_get(EXINT_LINE_0) != RESET)
{
if(gpio_input_data_bit_read(GPIOA, GPIO_PINS_0) == 1)
{
tmr_counter_value_set(TMR1, 0);//开始计数
tmr_counter_enable(TMR1, TRUE);
}
else
{
tmr_counter_enable(TMR1, FALSE); //停止计数
printf("timer1: %d\r\n",tmr_counter_value_get(TMR1)); //获取计数值
printf("%f m",(float)tmr_counter_value_get(TMR1)/10000 * 340 / 2);
}
exint_flag_clear(EXINT_LINE_0);
}
}
4、验证测量计时是否准确,我们可以先在循环里设置PA1以30ms的间隔进行翻转一下,然后PA1通过跳线帽连到PA0来测试计准确
while(1)
{
gpio_bits_set(GPIOA, GPIO_PINS_1);
delay_ms(20);
gpio_bits_reset(GPIOA, GPIO_PINS_1);
结果非常准确的打印了计数的值,验证了通过外部中断来计算时间的可行性。
5、接线验证,注意我这个的超声波模块为5V供电,我们接上电源、trig接到PA1、echo接到PA0。结果很成功,测试数据比较稳定。
本帖最后由 LYU4662 于 2022-8-8 16:46 编辑
引用: soso 发表于 2022-8-8 17:35 文章结构挺好,继续加油~
感谢
超声波模块上用的驱动芯片是什么?
引用: 秦天qintian0303 发表于 2022-8-9 08:58 超声波模块上用的驱动芯片是什么?
超声波主控和超声波控制芯片都被生产商磨掉了丝印,还有一个LM324运放在。
想知道这款芯片的蓝牙性能如何。期待测评~
引用: qzc飘曳 发表于 2022-8-9 11:03 这种模块最远能测多远啊,这个开发板就是引脚太少,已经很强悍了
测距范围2-450cm 精度3mm 超声波模块主要用在小车上,起到遇到障碍物会停止的的作用
引用: LYU4662 发表于 2022-8-9 09:27 超声波主控和超声波控制芯片都被生产商磨掉了丝印,还有一个LM324运放在。
这就不地道了,这种方案也不少