超声波是振动频率高于20kHz的机械波。它具有频率高、波长短、绕射现象小、方向性小、能够成为射线而定向传播等特点。
工作原理
- 采用(IO触发测距、给至少10μs的高电平信号)
- 模块自动发送8个40kHZ的方波、自动检测是否有信号返回
- 有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340m/s)/2)
产品参数
产品名称 HC-SR04超声波模块 工作电压 DC5V 工作电流 15mA 工作频率 40kHz 射程范围 2cm~4m(峰值) 测量角度 ≤15° 输入触发信号 10μs的TTL脉冲 输出回响信号 输出TTL电平信号,与射程成比例 模块尺寸 45*20*12mm
1、定时器模块
- Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //启用TIM3时钟
TIM_InternalClockConfig(TIM3); //设置TIM3使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体,配置定时器
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置1分频(不分频)
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //设置最大计数值,达到最大值触发更新事件,因为从0开始计数,所以计数10次是10-1,每10微秒触发一次
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //设置时钟预分频,72-1就是每 时钟频率(72Mhz)/72=1000000 个时钟周期计数器加1,每1微秒+1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器(高级定时器才有,所以设置0)
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //初始化TIM3定时器
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除更新中断标志位
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //开启更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体,配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //指定中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置响应优先级
NVIC_Init(&NVIC_InitStructure); // https://blog.zeruns.tech
TIM_Cmd(TIM3, ENABLE); //开启定时器
}
- Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
2、HC-SR04模块
- HCSR04.c
#include "stm32f10x.h"
#include "Delay.h"
#define Echo GPIO_Pin_0 //HC-SR04模块的Echo脚接GPIOB0
#define Trig GPIO_Pin_1 //HC-SR04模块的Trig脚接GPIOB1
uint64_t time=0; //声明变量,用来计时
uint64_t time_end=0; //声明变量,存储回波信号时间
void HC_SR04_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //启用GPIOB的外设时钟
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置GPIO口为推挽输出
GPIO_InitStructure.GPIO_Pin = Trig; //设置GPIO口5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO口速度50Mhz
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置GPIO口为下拉输入模式
GPIO_InitStructure.GPIO_Pin = Echo; //设置GPIO口6
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB
GPIO_WriteBit(GPIOB,GPIO_Pin_5,0); //输出低电平
Delay_us(15); //延时15微秒
}
int16_t sonar_mm(void) //测距并返回单位为毫米的距离结果
{
uint32_t Distance,Distance_mm = 0;
GPIO_WriteBit(GPIOB,Trig,1); //输出高电平
Delay_us(15); //延时15微秒
GPIO_WriteBit(GPIOB,Trig,0); //输出低电平
while(GPIO_ReadInputDataBit(GPIOB,Echo)==0); //等待低电平结束
time=0; //计时清零
while(GPIO_ReadInputDataBit(GPIOB,Echo)==1); //等待高电平结束
time_end=time; //记录结束时的时间
if(time_end/100<38) //判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
{
Distance=(time_end*346)/2; //计算距离,25°C空气中的音速为346m/s
Distance_mm=Distance/100; //因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
}
return Distance_mm; //返回测距结果
}
float sonar(void) //测距并返回单位为米的距离结果
{
uint32_t Distance,Distance_mm = 0;
float Distance_m=0;
GPIO_WriteBit(GPIOB,Trig,1); //输出高电平
Delay_us(15);
GPIO_WriteBit(GPIOB,Trig,0); //输出低电平
while(GPIO_ReadInputDataBit(GPIOB,Echo)==0);
time=0;
while(GPIO_ReadInputDataBit(GPIOB,Echo)==1);
time_end=time;
if(time_end/100<38)
{
Distance=(time_end*346)/2;
Distance_mm=Distance/100;
Distance_m=Distance_mm/1000;
}
return Distance_m;
}
void TIM3_IRQHandler(void) //更新中断函数,用来计时,每10微秒变量time加1
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
time++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除更新中断标志位
}
}
- HCSR04.h
#ifndef __HCSR04_H
#define __HCSR04_H
void HC_SR04_Init(void);
int16_t sonar_mm(void);
float sonar(void);
#endif
3、OLED模块
- OLED.c
#include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
OLED_W_SCL(0);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
- OLED.h
#ifndef __OLED_H
#define __OLED_H
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
#endif
4、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "HCSR04.h"
uint64_t numlen(uint64_t num)//计算数字的长度
{
uint64_t len = 1; // 初始长度为1
for(; num > 9; ++len) // 判断num是否大于9,否则长度+1
num /= 10; // 使用除法进行运算,直到num小于1
return len; // 返回长度的值
}
int main(void)
{
OLED_Init(); //初始化OLED屏
Timer_Init(); //初始化定时器
HC_SR04_Init(); //初始化超声波测距模块
OLED_ShowString(1, 1, "Distance:"); //OLED屏输出字符串
while (1)
{
int Distance_mm=sonar_mm(); //获取距离测量结果,单位毫米(mm)
int Distance_m=Distance_mm/1000; //转换为米(m)为单位,将整数部分放入Distance_m
int Distance_m_p=Distance_mm%1000; //转换为米(m)为单位,将小数部分放入Distance_m_p
OLED_ShowString(2,1," "); //将OLDE屏第2行清屏
OLED_ShowNum(2, 1,Distance_m,numlen(Distance_m)); //显示测量结果的整数部分
OLED_ShowChar(2, 1+numlen(Distance_m), '.'); //显示小数点
if(Distance_m_p<100) //判断是否小于100毫米
{
OLED_ShowChar(2, 1+numlen(Distance_m)+1,'0'); //因为单位是米,所以小于10cm时要加0
OLED_ShowNum(2, 1+numlen(Distance_m)+2,Distance_m_p,numlen(Distance_m_p)); //显示测量结果的小数部分
OLED_ShowChar(2, 1+numlen(Distance_m)+2+numlen(Distance_m_p), 'm'); //显示单位
}
else
{
OLED_ShowNum(2, 1+numlen(Distance_m)+1,Distance_m_p,numlen(Distance_m_p)); //显示测量结果的小数部分
OLED_ShowChar(2, 1+numlen(Distance_m)+1+numlen(Distance_m_p), 'm'); //显示单位
}
OLED_ShowString(3,1," "); //将OLDE屏第3行清屏
OLED_ShowNum(3, 1,Distance_mm,numlen(Distance_mm)); //显示单位为毫米的距离结果
OLED_ShowString(3, 1 + numlen(Distance_mm), "mm");
Delay_ms(300); //延时300毫秒
}
}
5、接线
- HC-SR04
HC-SR04 | STM32 |
---|---|
VCC | 5V供电 |
Trig | PB1 |
Echo | PB0 |
GND | GND |
- OLED
利用HC-SR04超声波测距模块和stm32F103 完成一个超声波测距方案
超声波是振动频率高于20kHz的机械波。它具有频率高、波长短、绕射现象小、方向性小、能够成为射线而定向传播等特点。
工作原理
- 采用(IO触发测距、给至少10μs的高电平信号)
- 模块自动发送8个40kHZ的方波、自动检测是否有信号返回
- 有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340m/s)/2)
产品参数
产品名称 HC-SR04超声波模块 工作电压 DC5V 工作电流 15mA 工作频率 40kHz 射程范围 2cm~4m(峰值) 测量角度 ≤15° 输入触发信号 10μs的TTL脉冲 输出回响信号 输出TTL电平信号,与射程成比例 模块尺寸 45*20*12mm
1、定时器模块
- Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //启用TIM3时钟
TIM_InternalClockConfig(TIM3); //设置TIM3使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体,配置定时器
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置1分频(不分频)
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //设置最大计数值,达到最大值触发更新事件,因为从0开始计数,所以计数10次是10-1,每10微秒触发一次
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //设置时钟预分频,72-1就是每 时钟频率(72Mhz)/72=1000000 个时钟周期计数器加1,每1微秒+1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器(高级定时器才有,所以设置0)
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //初始化TIM3定时器
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除更新中断标志位
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //开启更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体,配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //指定中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置响应优先级
NVIC_Init(&NVIC_InitStructure); // https://blog.zeruns.tech
TIM_Cmd(TIM3, ENABLE); //开启定时器
}
- Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
2、HC-SR04模块
- HCSR04.c
#include "stm32f10x.h"
#include "Delay.h"
#define Echo GPIO_Pin_0 //HC-SR04模块的Echo脚接GPIOB0
#define Trig GPIO_Pin_1 //HC-SR04模块的Trig脚接GPIOB1
uint64_t time=0; //声明变量,用来计时
uint64_t time_end=0; //声明变量,存储回波信号时间
void HC_SR04_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //启用GPIOB的外设时钟
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置GPIO口为推挽输出
GPIO_InitStructure.GPIO_Pin = Trig; //设置GPIO口5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO口速度50Mhz
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置GPIO口为下拉输入模式
GPIO_InitStructure.GPIO_Pin = Echo; //设置GPIO口6
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB
GPIO_WriteBit(GPIOB,GPIO_Pin_5,0); //输出低电平
Delay_us(15); //延时15微秒
}
int16_t sonar_mm(void) //测距并返回单位为毫米的距离结果
{
uint32_t Distance,Distance_mm = 0;
GPIO_WriteBit(GPIOB,Trig,1); //输出高电平
Delay_us(15); //延时15微秒
GPIO_WriteBit(GPIOB,Trig,0); //输出低电平
while(GPIO_ReadInputDataBit(GPIOB,Echo)==0); //等待低电平结束
time=0; //计时清零
while(GPIO_ReadInputDataBit(GPIOB,Echo)==1); //等待高电平结束
time_end=time; //记录结束时的时间
if(time_end/100<38) //判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
{
Distance=(time_end*346)/2; //计算距离,25°C空气中的音速为346m/s
Distance_mm=Distance/100; //因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
}
return Distance_mm; //返回测距结果
}
float sonar(void) //测距并返回单位为米的距离结果
{
uint32_t Distance,Distance_mm = 0;
float Distance_m=0;
GPIO_WriteBit(GPIOB,Trig,1); //输出高电平
Delay_us(15);
GPIO_WriteBit(GPIOB,Trig,0); //输出低电平
while(GPIO_ReadInputDataBit(GPIOB,Echo)==0);
time=0;
while(GPIO_ReadInputDataBit(GPIOB,Echo)==1);
time_end=time;
if(time_end/100<38)
{
Distance=(time_end*346)/2;
Distance_mm=Distance/100;
Distance_m=Distance_mm/1000;
}
return Distance_m;
}
void TIM3_IRQHandler(void) //更新中断函数,用来计时,每10微秒变量time加1
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
time++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除更新中断标志位
}
}
- HCSR04.h
#ifndef __HCSR04_H
#define __HCSR04_H
void HC_SR04_Init(void);
int16_t sonar_mm(void);
float sonar(void);
#endif
3、OLED模块
- OLED.c
#include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
OLED_W_SCL(0);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
- OLED.h
#ifndef __OLED_H
#define __OLED_H
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
#endif
4、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "HCSR04.h"
uint64_t numlen(uint64_t num)//计算数字的长度
{
uint64_t len = 1; // 初始长度为1
for(; num > 9; ++len) // 判断num是否大于9,否则长度+1
num /= 10; // 使用除法进行运算,直到num小于1
return len; // 返回长度的值
}
int main(void)
{
OLED_Init(); //初始化OLED屏
Timer_Init(); //初始化定时器
HC_SR04_Init(); //初始化超声波测距模块
OLED_ShowString(1, 1, "Distance:"); //OLED屏输出字符串
while (1)
{
int Distance_mm=sonar_mm(); //获取距离测量结果,单位毫米(mm)
int Distance_m=Distance_mm/1000; //转换为米(m)为单位,将整数部分放入Distance_m
int Distance_m_p=Distance_mm%1000; //转换为米(m)为单位,将小数部分放入Distance_m_p
OLED_ShowString(2,1," "); //将OLDE屏第2行清屏
OLED_ShowNum(2, 1,Distance_m,numlen(Distance_m)); //显示测量结果的整数部分
OLED_ShowChar(2, 1+numlen(Distance_m), '.'); //显示小数点
if(Distance_m_p<100) //判断是否小于100毫米
{
OLED_ShowChar(2, 1+numlen(Distance_m)+1,'0'); //因为单位是米,所以小于10cm时要加0
OLED_ShowNum(2, 1+numlen(Distance_m)+2,Distance_m_p,numlen(Distance_m_p)); //显示测量结果的小数部分
OLED_ShowChar(2, 1+numlen(Distance_m)+2+numlen(Distance_m_p), 'm'); //显示单位
}
else
{
OLED_ShowNum(2, 1+numlen(Distance_m)+1,Distance_m_p,numlen(Distance_m_p)); //显示测量结果的小数部分
OLED_ShowChar(2, 1+numlen(Distance_m)+1+numlen(Distance_m_p), 'm'); //显示单位
}
OLED_ShowString(3,1," "); //将OLDE屏第3行清屏
OLED_ShowNum(3, 1,Distance_mm,numlen(Distance_mm)); //显示单位为毫米的距离结果
OLED_ShowString(3, 1 + numlen(Distance_mm), "mm");
Delay_ms(300); //延时300毫秒
}
}
5、接线
- HC-SR04
HC-SR04 | STM32 |
---|---|
VCC | 5V供电 |
Trig | PB1 |
Echo | PB0 |
GND | GND |
- OLED
6、编译下载
利用HC-SR04超声波测距模块和stm32F103 完成一个超声波测距方案