赞
踩
这个垃圾桶的功能效果和51的那个一样,但是实现的方式不一样,都可以由距离,震动,按键触发垃圾桶开盖子。
一、硬件介绍
名称 | 图片 | 功能 |
超声波模块 | 避障模式时测量距离。这个模块具体的简绍可以看http://t.csdnimg.cn/uFgqP | |
舵机 | 与超声波模块配合使用,使超声波向前左右扭头测距。可以用PWM波形来控制它的转动角度。 | |
振动传感器 | 单片机供电5V, GND接单片机 ,电源指示灯亮。发生震动时,信号指示灯会亮,同时AO口输出低电平。无震动时,信号指示灯不亮,同时AO口输出高电平。 | |
蜂鸣器 | 单片机供电3.3V, GND接单片机. 。单片机引脚·发出低电平时蜂鸣器报警,高电平时,蜂鸣器不叫。 |
二、代码部分
代码分了两部分,一部分是手写的(手写的我用淡蓝色的标记了),一部分是在stm32cube上设置后,软件生成的。
#include "main.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define OPEN 1
#define CLOSE 0
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
char flag = CLOSE;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//使用TIM2来做us级延时函数
void TIM2_Delay_us(uint16_t n_us)
{
/* 使能定时器2计数,HAL库没有us函数,因此要自己写,我们把PSC的只改成71,就可以实现1us计数一次,原因在下面我会说 */
__HAL_TIM_ENABLE(&htim2);
__HAL_TIM_SetCounter(&htim2, 0);
while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
/* 关闭定时器2计数 */
__HAL_TIM_DISABLE(&htim2);
}
double get_distance()
{
int cnt=0;//数值代表时间
//1. Trig ,给Trig端口至少10us的高电平,,让超声波模块开始工作
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);//拉高
TIM2_Delay_us(15);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);//拉低
//2. echo由低电平跳转到高电平,表示开始发送波
//波发出去的那一下,开始启动定时器
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_RESET);//等待输入电平拉高
HAL_TIM_Base_Start(&htim2);
__HAL_TIM_SetCounter(&htim2,0);//每一次开始测距时,把定时器二的CNT清零
//3. 由高电平跳转回低电平,表示波回来了
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET);//等待输入电平变低
//波回来的那一下,我们开始停止定时器
HAL_TIM_Base_Stop(&htim2);
//4. 计算出中间经过多少时间
cnt = __HAL_TIM_GetCounter(&htim2);
//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
return (cnt*0.034); //单位换算后面我写的有
}
void openStatusLight()
{
//点亮LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
}
void closeStatusLight()
{
//熄灭LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}
void initSG90_0()
{
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //__HAL_TIM_SetCompare,在运行时设置TIM捕获比较寄存器值,这里CCR=5,将舵机置为0度
}
void openDusbin()
{
if(flag == CLOSE)
{
flag = OPEN;
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 15); //将舵机置为90度
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);
}
HAL_Delay(2000);
}
void closeDusbin()
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
flag = CLOSE;
HAL_Delay(150);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0 || GPIO_Pin == GPIO_PIN_5)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET || //按键
HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_RESET) // 震动传感器
{
openStatusLight();
openDusbin();
}
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
float distance;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
initSG90_0();
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//超声波测距
distance = get_distance();
if(distance < 10){
//点亮LED1
openStatusLight();
//开盖
openDusbin();
}else{
//熄灭LED1
closeStatusLight();
//关盖
closeDusbin();
}
}
/* USER CODE END 3 */
}
三、stm32cube上的设置
SYS里,debug选择Serial Wire为st-link下载器用。RCC里,选择High Speed Clock(HSE),配置为Crystal/Ceramic Resonator,然后在Clock Configuration里按下图配置
GPIO的配置如下
PA0对应单片机按键1 PB4对应蜂鸣器 PB5接振动传感器 PB6接Trig PB7接Echo PB8对应单片机LED1 PB9接舵机
PA0和PB5要作为外部触发中断使用,当按键按下,来一个低电平给单片机PA0,触发中断,垃圾桶打开;当发生震动,传感器来一个低电平给单片机PB5,触发中断,垃圾桶打开。
超声波配置
测距需要时间,还需速度,声波的速度是340m/s,换算单位就是0.034cm/us。时间怎么计算呢?
这时候就需要用定时器了,将定时器2如下图设置
时钟源来自系统的72MHz,分频器PSC设置为71,自动重装使能。
关于时基单元的PSC,CNT和ARR,我说说我的理解
对于预分频器PSC,它就是一个改变计数时间寄存器,在你不分频(即PSC=0)的时候以72MHz/(0+1)的频率计数;当你的PSC=71时,你要以72MHz/(71+1)的频率计数,也就是每隔1us计数一次,CNT加一。自动装载寄存器ARR是你设置的计数上限,达到这个值之后就要将CNT置零。
所以所谓的定时中断,不过是你通过改变PSC来改变计一个数的时间,然后输入ARR的值再规定记到多少个数字停止。就比如你要定时1s中断,你就可以改变PSC的值,使CNT每隔20毫秒加一,将ARR设置为49,打开中断通道,这样过20ms*(49+1)就实现1s可以计时了。ARR之所以要加以是因为0到49有50个数。
回到定时器2,我们将它的PSC=71,这样就可以实现1us记一个数了,定时器2的CNT每加一就代表时间增加1us。至于ARR,我们不用管,在有限距离内,ARR不会爆表。(为什么不会爆表,可以看一下这里http://t.csdnimg.cn/wbIWW).
接着说舵机,图上的舵机的驱动频率是50Hz,周期也是就20ms。舵机想要改变角度,就要控制一个钟期内高电平的占用时间,也就是我们说的占空比。
一个周期内,高电平占用时间对应的旋转角度
回到定时器4,配置如下图
对于时基单元的配置我不说了,无论你想咋配置,只要能配制出20ms就行。关于PWM Generation下面的东西,大家可以出一下手册,了解一下,中文手册在255页。
我要说的是角度的事,按照我上面写的ARR=199,也就是说,ARR从0到199,计数200次后就要自动变成0。
在PWM模式1:在向上计数时,一旦 CNT < CCRx 时输出为有效电平,否则为无效电平; 在向 下计数时,一旦 CNT > CCRx 时输出为无效电平即低电平,否则为有效电平。说白了就是CCR是个门槛,是高低电平的分水岭。
这里的,你可以发现CCR越接近ARR,一个周期里,高电平持续时间越长。所以高电平的持续时间与CCR的值成正比,如果ARR=CCR,则整个周期都是高电平。要想舵机达到0度,则要有持续时间0.5ms的高电平,则ARR从0到199共200份,CCR要占到5份,其余的角度以此类推。
四、其它
这就要说一个很傻逼的事了,是振动传感器的,本来在51上面,他的灵敏度是可以的,但是我也没想到到了32上,原来的灵敏度就不行了。等我把程序写好,硬件安好后,舵机就像神经了一样,不受控制过两秒自己动,过两秒自己动,刚开始我以为是我代码有问题,导致死机了,后来我一点一点排查,我发现把振动传感器控制开盖去掉就好了,我才意识到是震动传感器出了问题。但说实话,我也没想到是灵敏度的问题,知道我看见震动模块的灯一直闪,我才有意无意的调了一下灵敏度,没想到竟然阴差阳错好了。折磨了我快3个小时,如果你做的话,引以为戒吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。