US-015超声波模块
SG90舵机云台
接线:舵机超声波:
A1–P2.7
B8–Trig
B9–Echo
所有的代码都是直接从工程里面复制的,实测是没有问题的。
参考文章: stm32 智能避障小车(二)之sg90
我这里再简单总结一下:
舵机:
橙色信号号线,红色5V,棕色GND;
舵机控制需要一个周期为20ms的方波,方波的高电平部分在0.5ms~2.5ms中,会转动一定的角度。
t=0.5ms——舵机转动0°
t=1.0ms——舵机转动45°
t=1.5ms——舵机转动90°
t=2.0ms——舵机转动135°
t=2.5ms——舵机转动180°
设置在转动90°时为舵机的正前方,这样就可以左右转动了。
代码思路:利用定时器输出一个占空比可调的PWM,且PWM周期为20ms
A1接舵机的橙色线
#include "SG90.H"
void SG90_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
//开启定时器2,GPIOA,AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//初始化A1
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//初始化定时器2
TIM_TimeBaseInitStruct.TIM_Period=199;
TIM_TimeBaseInitStruct.TIM_Prescaler=7199;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);//PWM周期为:(7200*200)/72000000=0.02=20ms
//定时器2通道二初始化
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//当计时器值小于比较器设定值时输出有效低电平;大于等于时输出有效高电平
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_Low;//初试极性为Low
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC2Init(TIM2,&TIM_OCInitStruct);
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_Cmd(TIM2,ENABLE);
}
1.开启时钟,定时器2:
RCC_APB1PeriphClockCmd();
开启IO口时钟、AFIO:
RCC_APB2PeriphClockCmd();//GPIOA
2.初始化A1:
GPIO_Init();//复用推挽输出 AF_PP
3.定时器2初始化:
TIM_TimeBaseInit();//周期199,预分频系数7199,计数器模式 up,时钟分割 0
4.初始化定时器2的通道2:并使能定时器:
TIM_OC2Init();//模式:PWM1,极性:low,输出状态:ENABLE
TIM_OC2PreloadConfig();
TIM_Cmd();
#ifndef __SG90_H
#define __SG90_H
#include "stm32f10x.h"
#include "delay.h"
#define SG90_Right_90 TIM_SetCompare2(TIM2,195)
#define SG90_Right_45 TIM_SetCompare2(TIM2,190)//右转45
#define SG90_Front TIM_SetCompare2(TIM2,185)//方向摆正,(15/200)*20ms=1.5ms
#define SG90_Left_45 TIM_SetCompare2(TIM2,180)
#define SG90_Left_90 TIM_SetCompare2(TIM2,175)//左转45
void SG90_PWM_Init(void);
#endif
参考资料:STM32的超声波测距程序
超声波模块工作原理:
采用IO口TRIG触发测距,需要给最少10us的高电平信呈;
模块自动发送8个40KHZ的方波,自动检测是否有信号返回;
有信号返回,通过IO口ECHO输出高电平,高电平持续时间就是超声波从发射到返回的时间;
测试距离=高电平持续时间*声速/2
我是用定时器计数来获取时间的。小伙伴最好已经熟练掌握STM32的定时器中断实验。代码的原理一模一样,看不懂代码的可以看看原子哥的视频讲解。我这里不再详细讲解了。
#include "CJ.H"
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
//配置中断优先级
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel=TIM4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//响应优先级
NVIC_Init(&NVIC_InitStruct);
}
void CH_SR04_Init(u16 reload,u16 psr)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
//开启定时器4,GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
//PB8 TRIG触发信号
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct); //PB8接TRIG
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
//PB9 ECHO回响信号
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//定时器4初始化 设置分频系数
TIM_TimeBaseInitStruct.TIM_Period=reload;
TIM_TimeBaseInitStruct.TIM_Prescaler=psr;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_4; //通道4
TIM_ICInitStruct.TIM_ICFilter=0x00; //不滤波
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising; //捕获模式,这里设置为上升沿捕获
TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInit(TIM4,&TIM_ICInitStruct);
TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC4,ENABLE);
NVIC_Config();
}
#ifndef __CJ_H
#define __CJ_H
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#define uint unsigned int
#define TRIG_Send PBout(8)
#define ECHO_Receive PBout(9)
void CH_SR04_Init(u16 reload,u16 psr);//超声波模块配置
void NVIC_Config(void);//中断配置
#endif
#include "stm32f10x.h"
#include "moter.h"
#include "delay.h"
#include "SG90.H"
#include "cj.h"
#include "usart.h"
#include "sys.h"
#include "stdio.h"
#define CLK_TIME 1.0/8000000.0
u8 TIM4CH4_CAPTURE_STA=0; //输入捕获状态
u16 TIM4CH4_CAPTURE_VAL; //输入捕获值
u8 cap_flag=0;
float length_res[5]; //用来存放测距结果
u32 time_cnt;
double long_chang;
float sensor(void)
{
PBout(8)=1; //拉高信号,作为触发信号
delay_us(20); //高电平信号超过10us
PBout(8)=0;
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==RESET);
TIM_Cmd(TIM4,ENABLE);//回响信号到来,开启定时器计数
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)==SET);//回响信号消失
TIM_Cmd(TIM4,DISABLE);//关闭定时器
if(cap_flag)
{
time_cnt=(TIM4CH4_CAPTURE_STA&0x3f)*65536;
time_cnt+=TIM4CH4_CAPTURE_VAL;
//printf("%d\r\n",time_cnt);
long_chang=time_cnt*CLK_TIME*340/2.0*100.0;
//printf("%f cm\r\n",long_chang);
cap_flag=0;
TIM4CH4_CAPTURE_STA=0;
TIM4CH4_CAPTURE_VAL=0;
delay_ms(10);
}
return long_chang;
}
int main(void)
{
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
// uart_init(115200);
delay_init(); //延时初始化
TIM3_PWM_Init(); //电机pwm TIM3
SG90_PWM_Init(); //舵机初始化
CH_SR04_Init(0xffff,8); //超声波定时器初始化
TIM_Cmd(TIM4,ENABLE);//开启超声波测距定时器
while(1)
{
SG90_Front;
delay_ms(700);
length_res[0]=sensor();
delay_ms(10);
//printf("fornt:%f\r\n",length_res[0]);
if(length_res[0]>40.0000) //如果前方距离大于40cm 前进
{
Forward();
}
else if(length_res[0]<40.0000) //如果前方距离小于40厘米 停车测左右距离
{
STOP();
SG90_Left_45; //舵机左转45度测距
delay_ms(700);
length_res[1] =sensor(); //把测量结果放进数组
delay_ms(10);
//printf("left:%fcm left45 \r\n",length_res[1]);
SG90_Right_45; //舵机右转45度测距
delay_ms(700);
length_res[4] =sensor(); //把测量结果放进数组
delay_ms(10);
//printf("right:%fcm right 45\r\n",length_res[4]);
SG90_Front; //舵机摆正
//delay_ms(700);
if(length_res[1]<40.00&&length_res[4]<40.00&&length_res[1]>length_res[4])
{
SG90_Front; //舵机摆正
delay_ms(700);
length_res[0] =sensor(); //重复测前方的距离同时
delay_ms(10);
Backward();
delay_ms(700);
Turn_left();
// delay_ms(500);
}
if(length_res[1]<40.00&&length_res[4]<40.00&&length_res[1]<length_res[4])
{
SG90_Front; //舵机摆正
delay_ms(700);
length_res[0] =sensor(); //重复测前方的距离同时
delay_ms(10);
Backward();
delay_ms(700);
Turn_right();
// delay_ms(500);
}
if(length_res[1]>length_res[4]) //如果左边的距离大于右边的距离
{
SG90_Front; //舵机摆正
delay_ms(700);
length_res[0] =sensor(); //重复测前方的距离同时左转
delay_ms(10);
Turn_left();
if(length_res[0]>35.0000)
Forward();
}
if(length_res[1]<length_res[4]) //如果右边的距离大于左边的距离
{
SG90_Front;
delay_ms(700);
length_res[0] =sensor(); //重复测前方的距离同时右转
delay_ms(10);
Turn_right();
if(length_res[0]>35.0000)
Forward();
}
}
}
}
void TIM4_IRQHandler(void)//中断溢出
{
if((TIM4CH4_CAPTURE_STA&0x80)==0)
{
if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{ //判断定时器是否溢出
if(TIM4CH4_CAPTURE_STA&0x40)//判断之前是否已经接收到上升沿
{
if((TIM4CH4_CAPTURE_STA&0X3F)==0X3F) //如果计数已经满了 直接停止
{
//cap_flag=1;
TIM4CH4_CAPTURE_STA|=0x80;
TIM4CH4_CAPTURE_VAL=0XFFFF;
}
else TIM4CH4_CAPTURE_STA++; //如果计数没满,继续计数
}
}
}
if(TIM_GetITStatus(TIM4, TIM_IT_CC4) != RESET)
{ //表示捕获到来上升沿
if(TIM4CH4_CAPTURE_STA&0x40)//表明之前已经接收到了一个边沿(这一次捕获边沿表示捕获结束)
{
cap_flag=1;
TIM4CH4_CAPTURE_STA|=0x80;
TIM4CH4_CAPTURE_VAL=TIM_GetCapture4(TIM4); //读取捕获的值
TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Rising); //重新设置为上升沿捕获
}
else
{ //表示首次进入捕获边沿
cap_flag=0;
TIM4CH4_CAPTURE_STA=0;
TIM4CH4_CAPTURE_VAL=0;
TIM4CH4_CAPTURE_STA|=0X40; //标记为捕获了一次上升沿
TIM_SetCounter(TIM4,0);
TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Falling);
}
}
TIM_ClearITPendingBit(TIM4, TIM_IT_CC4|TIM_IT_Update); //清除中断标志位
}
将以上程序写入工程里,下载到板子上,实验现象为:
舵机摆正,不断测量前方的距离;
当前方距离大于40cm时,小车前进;
当前方距离小于40cm时,停车;舵机左右摆,测两边的距离,同时判断情况。
---------当左右两边都小于40cm,但左边距离大于右边时,后退,然后左转。
--------- 当左右两边都小于40cm,但右边距离大于左边时,后退,然后右转。
---------当左边距离大于右边时,左转。
---------当左边距离小于右边时,右转。
任意时刻,当前方距离大于40cm时,小车前进。
STM32小车-超声波避障-超声波测距.zip-嵌入式文档类资源-CSDN文库
下一节写蓝牙控制程序