目录
摘要
本文是基于STM32F103VET6芯片,HC-SR04超声波传感器,0.96寸OLED(4pin)为主要模块进行开发的个人学习项目。主要供学习交流使用,侵删。
这是我的第一篇博客,很开心能在这里遇见大家,如果文中有什么错误,欢迎指正!如果有帮到您,希望也能请您点赞关注支持一下~有问题可以在评论区中回复,看到都会回。
基础知识
认识STM32F106VET6(100 PIN)
STM32F103VET6是一款32位高密度高性能线路微控制器单元,电源电压为2V至3.6V。它提供三个12位ADC、四个16位通用定时器和两个PWM定时器,以及两个I2C、三个SPI和两个I2S、一个SDIO、五个USART、一个USB和一个CAN。在72 MHz频率下运行的高性能ARM集成®Cortex®-332位RISC内核、高速板载存储器以及连接到两条APB总线的各种扩展I/O和外围设备。在此系统中,其提供的I2C引脚能够给OLED使用,从而能够使超声波测距的距离显示在OLED屏幕上。
认识HC-SR04(4 PIN)
HC-SR04超声波距离传感器的核心是两个超声波传感器。一种用于将电信号转换为40kHz超声波脉冲的发射器。接收器监测发射的脉冲。该传感器体积小,提供了从2厘米到400厘米(约1英寸到13英尺)的出色非接触式检测,精度为3毫米。由于它的工作电压为5伏,测量角度为15°。在本系统中,只需要将其连接在STM32F103VET6的两个定时器管脚上,即可实现超声波的发送与接收,从而计算出距离。
认识0.96寸OLED(4 PIN)
OLED,即有机发光二极管,又称为有机电激光显示。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。分辨率:128*64,以显示汉字、ASCII、图案,对比度高。在本系统中,其只需连接在主控芯片的I2C引脚上并通过代码即可实现对其屏幕的控制。OLED自身不带显存,但其电路中的SSD1306可以提供,其大小为128*64bit大小,SSD1306将这些显存分为了8页。每页包含了128个字节,总共8页,这样刚好是128*64的点阵大小。
原理图介绍
这一部分主要就是根据元件的datasheet与单片机进行连线。有疑问可以单独私信或在评论区中回复。
PCB介绍
程序代码
主函数部分
#include "stm32f10x.h"
#include "OLED_I2C.h"
#include "delay.h"
#include "hc.h"
int main(void)
{
unsigned char i;
u8 string[10]={0};
float length;
DelayInit();
Hcsr04Init();
I2C_Configuration();
OLED_Init();
OLED_Fill(0xFF);//全屏点亮
delay_ms(100);
OLED_Fill(0x00);//全屏灭
delay_ms(100);
for(i=0;i<5;i++)
{
OLED_ShowCN(22+i*16,0,i); //测试显示中文
}
OLED_ShowStr(104,1,(unsigned char*)":",1); //测试6*8字符
OLED_ShowStr(56,6,(unsigned char*)"Cm",2); //测试8*16字符
while(1)
{
length=Hcsr04GetLength();
sprintf(string,"%.2f",length);
OLED_ShowStr(42,3,string,2);
}
}
超声波测距
#include "hc.h"
#include "delay.h"
#include "sys.h"
//超声波硬件接口定义
#define HCSR04_PORT GPIOC
#define HCSR04_CLK RCC_APB2Periph_GPIOC
#define HCSR04_TRIG GPIO_Pin_7
#define HCSR04_ECHO GPIO_Pin_6
#define TRIG_Send PCout(7)
#define ECHO_Reci PCin(6)
u16 msHcCount = 0;
//定时器4设置
void hcsr04_NVIC()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//IO口初始化 及其他初始化
void Hcsr04Init()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period = (1000-1);
TIM_TimeBaseStructure.TIM_Prescaler =(72-1);
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
hcsr04_NVIC();
TIM_Cmd(TIM4,DISABLE);
}
//打开定时器4
static void OpenTimerForHc()
{
TIM_SetCounter(TIM4,0);
msHcCount = 0;
TIM_Cmd(TIM4, ENABLE);
}
//关闭定时器4
static void CloseTimerForHc()
{
TIM_Cmd(TIM4, DISABLE);
}
//定时器4终中断
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update );
msHcCount++;
}
}
//获取定时器4计数器值
u32 GetEchoTimer(void)
{
u32 t = 0;
t = msHcCount*1000;
t += TIM_GetCounter(TIM4);
TIM4->CNT = 0;
delay_ms(50);
return t;
}
//通过定时器4计数器值推算距离
float Hcsr04GetLength(void )
{
u32 t = 0;
int i = 0;
float lengthTemp = 0;
float sum = 0;
while(i!=5)
{
TRIG_Send = 1;
delay_us(20);
TRIG_Send = 0;
while(ECHO_Reci == 0);
OpenTimerForHc();
i = i + 1;
while(ECHO_Reci == 1);
CloseTimerForHc();
t = GetEchoTimer();
lengthTemp = ((float)t/58.0);//cm
sum = lengthTemp + sum ;
}
lengthTemp = sum/5.0;
return lengthTemp;
}
OLED显示
*此段代码中参考了野火的资料
#include "OLED_I2C.h"
#include "delay.h"
#include "codetab.h"
u8 OLED_GRAM[144][8];
/**
* @brief I2C_Configuration,初始化硬件IIC引脚
* @param 无
* @retval 无
*/
void I2C_Configuration(void)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*STM32F103C8T6芯片的硬件I2C: PB6 -- SCL; PB7 -- SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//I2C必须开漏输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C1);//使用I2C1
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x30;//主机的I2C地址,随便写的
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000;//400K
I2C_Cmd(I2C1, ENABLE);
I2C_Init(I2C1, &I2C_InitStructure);
}
/**
* @brief I2C_WriteByte,向OLED寄存器地址写一个byte的数据
* @param addr:寄存器地址
* data:要写入的数据
* @retval 无
*/
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
I2C_GenerateSTART(I2C1, ENABLE);//开启I2C1
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));/*EV5,主模式*/
I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默认0x78
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, addr);//寄存器地址
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1, data);//发送数据
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
}
/**
* @brief WriteCmd,向OLED写入命令
* @param I2C_Command:命令代码
* @retval 无
*/
void WriteCmd(unsigned char I2C_Command)//写命令
{
I2C_WriteByte(0x00, I2C_Command);
}
/**
* @brief WriteDat,向OLED写入数据
* @param I2C_Data:数据
* @retval 无
*/
void WriteDat(unsigned char I2C_Data)//写数据
{
I2C_WriteByte(0x40, I2C_Data);
}
/**
* @brief OLED_Init,初始化OLED
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
delay_ms(100); //这里的延时很重要
WriteCmd(0xAE); //display off
WriteCmd(0x20); //Set Memory Addressing Mode
WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8); //Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //亮度调节 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel
}
/**
* @brief OLED_SetPos,设置光标
* @param x,光标x位置
* y,光标y位置
* @retval 无
*/
void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
/**
* @brief OLED_Fill,填充整个屏幕
* @param fill_Data:要填充的数据
* @retval 无
*/
void OLED_Fill(unsigned char fill_Data)//全屏填充
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
for(n=0;n<128;n++)
{
WriteDat(fill_Data);
}
}
}
/**
* @brief OLED_CLS,清屏
* @param 无
* @retval 无
*/
void OLED_CLS(void)//清屏
{
OLED_Fill(0x00);
}
/**
* @brief OLED_ON,将OLED从休眠中唤醒
* @param 无
* @retval 无
*/
void OLED_ON(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X14); //开启电荷泵
WriteCmd(0XAF); //OLED唤醒
}
/**
* @brief OLED_OFF,让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
* @param 无
* @retval 无
*/
void OLED_OFF(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X10); //关闭电荷泵
WriteCmd(0XAE); //OLED休眠
}
//m^n
u32 oled_pow(u8 m,u8 n)
{
u32 result=1;
while(n--)
{
result*=m;
}
return result;
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
/**
* @brief OLED_ShowStr,显示codetab.h中的ASCII字符,有6*8和8*16可选择
* @param x,y : 起始点坐标(x:0~127, y:0~7);
* ch[] :- 要显示的字符串;
* TextSize : 字符大小(1:6*8 ; 2:8*16)
* @retval 无
*/
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
}break;
}
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
/**
* @brief OLED_ShowCN,显示codetab.h中的汉字,16*16点阵
* @param x,y: 起始点坐标(x:0~127, y:0~7);
* N:汉字在codetab.h中的索引
* @retval 无
*/
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
}
实物图
成果展示
串口通信(使用XCOM)
实物视频
超声波测距