本次实验主要内容是把DHT温湿度模块和超声波模块显示在OLED屏、通过蓝牙模块把DHT温湿度模块和超声波模块数据显示在蓝牙APP上。下面是主要的代码。
#ifndef USER_DHT11_H
#define USER_DHT11_H
#include "debug.h"
//IO方向设置
#define DHT11_IO_IN() {GPIOB->CFGHR&=0XFFFF0FFF;GPIOB->CFGHR|=8<<12;}
#define DHT11_IO_OUT() {GPIOB->CFGHR&=0XFFFF0FFF;GPIOB->CFGHR|=3<<12;}
////IO操作函数
#define DHT11_DQ_IN GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3) //数据端口 PB3
u8 DHT11_Init(void);//初始化DHT11
u16 DHT11_Read_Data(u16 *temp,u16 *humi);//读取温湿度
u8 DHT11_Read_Byte(void);//读出一个字节
u8 DHT11_Read_Bit(void);//读出一个位
u8 DHT11_Check(void);//检测是否存在DHT11
void DHT11_Rst(void);//复位DHT11
#endif
#include "dht11.h"
#include "debug.h"
//复位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //设置为输出模式
GPIO_ResetBits(GPIOB,GPIO_Pin_3); //拉低DQ
Delay_Ms(20); //拉低至少18ms
GPIO_SetBits(GPIOB,GPIO_Pin_3); //DQ=1
Delay_Us(30); //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)
{
u8 retry=0;
DHT11_IO_IN();//设置为输入模式
while (DHT11_DQ_IN && retry<100)//DHT11会拉低40~80us
{
retry++;
Delay_Us(1);
};
if(retry>=100)
{
return 1;}
else {retry=0;}
while (!DHT11_DQ_IN && retry<100)//DHT11拉低后会再次拉高40~80us
{
retry++;
Delay_Us(1);
};
if(retry>=100)
{
return 1;}
return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN && retry<100)//等待变为低电平
{
retry++;
Delay_Us(1);
}
retry=0;
while(!DHT11_DQ_IN && retry<100)//等待变高电平
{
retry++;
Delay_Us(1);
}
Delay_Us(40);//等待40us
if(DHT11_DQ_IN)
return 1;
else
return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)
{
u8 i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u16 DHT11_Read_Data(u16 *temp,u16 *humi)
{
u8 buf[5];
u8 i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}else
return 1;
return 0;
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
u8 DHT11_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //PB3端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化IO口
GPIO_SetBits(GPIOB,GPIO_Pin_3); //PG11 输出高
DHT11_Rst(); //复位DHT11
return DHT11_Check();//等待DHT11的回应
}
#ifndef USER_GPIO_H_
#define USER_GPIO_H_
#define Trig_H GPIO_SetBits(GPIOA,GPIO_Pin_0)
#define Trig_L GPIO_ResetBits(GPIOA,GPIO_Pin_0)
#define Echo_H GPIO_SetBits(GPIOA,GPIO_Pin_1)
#define Echo_L GPIO_ResetBits(GPIOA,GPIO_Pin_1)
void ultrasonic_GPIO_Init(void);
void Start_Trig(void);
#endif /* USER_GPIO_H_ */
#include "gpio.h"
#include "debug.h"
void ultrasonic_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
}
void Start_Trig(void)
{
Trig_H;
Delay_Us(50);
Trig_L;
}
#ifndef USER_TIMER_H_
#define USER_TIMER_H_
#include "ch32v30x_conf.h"
void Input_Capture_Init( u16 arr, u16 psc );
void ENABLE_TIM(void);
void DISABLE_TIM(void);
u32 GetCount(void);
float Ultrasoniclength(void);
#endif /* USER_TIMER_H_ */
#include "timer.h"
#include "gpio.h"
#include "debug.h"
void TIM2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
u16 count = 0;//计数
/*******************************************************************************
* Function Name : Input_Capture_Init
* Input : arr: the period value.
* psc: the prescaler value.
* Return : None
*******************************************************************************/
void Input_Capture_Init( u16 arr, u16 psc )
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能IM2时钟
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE);
ultrasonic_GPIO_Init(); //配置Trig和Echo两个引脚
TIM_DeInit(TIM2);
//定时器周期,实际就是设定自动重载寄存器 ARR 的值, ARR 为要装载到实际自动重载寄存器(即影子寄存器) 的值, 可设置范围为 0 至 65535。
TIM_TimeBaseInitStructure.TIM_Period = arr;
//定时器预分频器设置,时钟源经该预分频器才是定时器计数时钟CK_CNT,它设定 PSC 寄存器的值。
//计算公式为: 计数器时钟频率 (fCK_CNT) 等于fCK_PSC / (PSC[15:0] + 1),可实现 1 至 65536 分频。
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
//时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择 1、 2、 4 分频。
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式,向上计数模式
//TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //设置重复计数器的值,0。重复计数器,只有 8 位,只存在于高级定时器。
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure); //初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM1捕获比较中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //设置响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能通道
NVIC_Init(&NVIC_InitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE ); //使能TIM2更新中断
TIM_Cmd( TIM2, DISABLE ); //定时器使能
}
void ENABLE_TIM(void)
{
//while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==RESET)
{
TIM_SetCounter(TIM2,0);
count = 0;
TIM_Cmd(TIM2,ENABLE);//回响信号到来,开启定时器计数
}
}
void DISABLE_TIM(void)
{
//while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==SET)
{
TIM_Cmd(TIM2,DISABLE);//回响信号到来,开启定时器计数
}
}
u32 GetCount(void)
{
u32 t = 0;
t = count*1000;
t += TIM_GetCounter(TIM2);
TIM_SetCounter(TIM2,0);
Delay_Ms(10);
return t;
}
//一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
//为了消除余震的影响,取五次数据的平均值进行加权滤波。
float Ultrasoniclength(void )
{
u32 t = 0;
int i = 0;
float length = 0 , sum = 0;
while(i!=5)
{
Start_Trig();
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==RESET); //此处一直等,等到为1,进行下一步
ENABLE_TIM();//回响信号到来,开启定时器计数
i = i + 1;
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==SET); //此处一直等,等到为0,进行下一步,这两段while之间的时间就是高电平时间,即发出到返回接收的时间
DISABLE_TIM();//回响信号到来,开启定时器计数
t = TIM_GetCounter(TIM2);
length=(t+count*1000)/75.0;//通过回响信号计算距离
sum = length + sum ;
//printf("sum is %.2f,length is %.2f,t is %d,count is %d\r\n",sum,length,t,count);
TIM_SetCounter(TIM2,0);
count = 0;
Delay_Ms(100);
}
length = sum/5.0;
return length;
}
/*******************************************************************************
* Function Name : TIM2_IRQHandler
*******************************************************************************/
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志
count++;
}
}
#include "debug.h"
/*********************************************************************
* @fn IIC_Init
*
* @brief Initializes the IIC peripheral.
*
* @return none
*/
void IIC_Init(u32 bound, u16 address)//初始化IIC设置
{
GPIO_InitTypeDef GPIO_InitStructure = {0};//定义结构体变量GPIO_InitStructure,并将它初始化为0
I2C_InitTypeDef I2C_InitTSturcture = {0};//定义结构体变量I2C_InitTSturcture,并将它初始化为0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);//使能GPIOB和AFIO的时钟
GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);//将I2C1的引脚映射到其他的GPIO端口
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);//使能I2C1的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //设置SCK引脚(GPIO_Pin_8)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//工作模式为复用开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);//将GPIO_InitStructure结构体中的配置应用到GPIOB引脚上
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //设置SDA引脚(GPIO_Pin_9)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//工作模式为复用开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);//将GPIO_InitStructure结构体中的配置应用到GPIOB引脚上
I2C_InitTSturcture.I2C_ClockSpeed = bound;//设置I2C的时钟频率为bound
I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;//工作模式为标准模式(I2C_Mode_I2C)
I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9;//设置占空比为16:9(I2C_DutyCycle_16_9)
I2C_InitTSturcture.I2C_OwnAddress1 = address;//设置地址为address
I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;//使能应答功能(I2C_Ack_Enable)
I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置应答地址长度为7位(I2C_AcknowledgedAddress_7bit)
I2C_Init(I2C1, &I2C_InitTSturcture);//将I2C_InitTSturcture结构体中的配置应用到I2C1外设上
I2C_Cmd(I2C1, ENABLE);//使能I2C1外设
#if(I2C_MODE == HOST_MODE)//如果I2C_MODE被定义为HOST_MODE,则通过I2C_AcknowledgeConfig函数使能I2C1的应答功能
I2C_AcknowledgeConfig(I2C1, ENABLE);
#endif
}
#ifndef USER_I2C_H_
#define USER_I2C_H_
void IIC_Init(u32 bound, u16 address);
#endif /* USER_I2C_H_ */
#ifndef USER_SSD1306_H_
#define USER_SSD1306_H_
#define SSD1306_ADDRESS 0x78
#define MAX_COLUMN 128
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
#define OLED_MODE 0
void ssd1306_write_cmd(uint8_t data);
void ssd1306_write_date(uint8_t data);
void oled_init(void);
void oled_set_pos(uint8_t x, uint8_t y);
void OLED_Fill(unsigned char fill_Data);//全屏填充
void oled_display_on(void);
void oled_display_off(void);
void oled_clear(void);
void oled_clear_line(uint8_t line,uint8_t num);
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint16_t char_size);
void oled_show_string(uint8_t x, uint8_t y, char *str, uint8_t char_size);
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize);
void oled_display_test(void);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_WR_Byte(unsigned dat,unsigned cmd);//写入字节
void OLED_ShowCHinese(u8 x,u8 y,u8 no);
#endif /* USER_SSD1306_H_ */
#include "debug.h"
#include "ssd1306.h"
#include "IIC.h"
#include "oledfont.h"
void ssd1306_write_cmd(uint8_t data)//发送命令函数
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET)//while循环检查I2C1总线是否繁忙,即不为0
;
I2C_GenerateSTART(I2C1, ENABLE);//开始传输数据
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))//检查I2C1是否成功进入主模式
;
I2C_Send7bitAddress(I2C1, SSD1306_ADDRESS, I2C_Direction_Transmitter);//发送目标设备地址(SSD1306_ADDRESS)给I2C1总线,发送的数据是发送模式
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//检查I2C1是否成功进入主模式的发送模式
;
if(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET)//如果TXE标志位为RESET
{
I2C_SendData(I2C1, 0x00);//向I2C1总线发送一个字节的数据(0x00)
}
if(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET)//判断I2C1是否准备好发送数据
{
I2C_SendData(I2C1, data);//向I2C1总线发送需要发送的数据
}
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查I2C1是否成功发送完毕一个字节的数据
;
I2C_GenerateSTOP(I2C1, ENABLE);//结束数据传输
}
void ssd1306_write_date(uint8_t data)//写入数据
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET)//等待I2C总线不为0
;
I2C_GenerateSTART(I2C1, ENABLE);//启动传输
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))//等待主模式选择事件发生
;
I2C_Send7bitAddress(I2C1, SSD1306_ADDRESS, I2C_Direction_Transmitter);//发送从设备地址,指定传输方向为发送
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//等待主模式发送器模式选择事件发生
;
if(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET)//判断是否可以发送数据
{
I2C_SendData(I2C1, 0x40);//发送数据
}
if(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET)//再次判断是否可以发送数据
{
I2C_SendData(I2C1, data);// 发送实际数据
}
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//等待字节传输完成
;
I2C_GenerateSTOP(I2C1, ENABLE);//结束传输
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)//写入字节
{
if(cmd)
{
ssd1306_write_date(dat);
}
else {
ssd1306_write_cmd(dat);
}
}
//坐标设置:也就是在哪里显示
void oled_set_pos(uint8_t x, uint8_t y)
{
//以下3个寄存器只在页录址的模式下有效
ssd1306_write_cmd(0xb0 + y); //页地址设置 0xb0-0xb7
ssd1306_write_cmd(((x&0xf0)>>4)|0x10); //列高地址设置
ssd1306_write_cmd((x&0x0f)|0x01); //列低位地址设置
}
void OLED_Fill(unsigned char fill_Data)//全屏填充
{
unsigned char m,n;
for(m=0;m<8;m++)
{
ssd1306_write_cmd(0xb0+m); //page0-page1
ssd1306_write_cmd(0x00); //low column start address 列地址的低4位
ssd1306_write_cmd(0x10); //high column start address 列地址的高4位
for(n=0;n<128;n++)
{
ssd1306_write_date(fill_Data);
}
}
}
//开启oled显示
void oled_display_on(void)
{
ssd1306_write_cmd(0x8D); //SET DCDC命令
ssd1306_write_cmd(0x14); //DCDC ON
ssd1306_write_cmd(0xAF); // DISPLAY ON
}
//关闭oled显示
void oled_display_off(void)
{
ssd1306_write_cmd(0x8D); //SET DCDC命令
ssd1306_write_cmd(0x10); //DCDC OFF
ssd1306_write_cmd(0xAE); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样
void oled_clear(void)
{
uint8_t i, n;
for(i=0;i<8;i++)
{
ssd1306_write_cmd(0xb0+i); //设置页地址(0-7)
ssd1306_write_cmd(0x00); //设置显示位置-列低地址
ssd1306_write_cmd(0x10); //设置显示位置-列高地址
for(n=0;n<128;n++)
{
ssd1306_write_date(0); //更新显示
}
}
}
//清楚某一行
void oled_clear_line(uint8_t line, uint8_t num)
{
uint8_t i,n;
for(i=line;i<line+num;i++)
{
ssd1306_write_cmd(0xb0+i); //设置页地址(0-7)
ssd1306_write_cmd(0x00); //设置显示位置-列低地址
ssd1306_write_cmd(0x10); //设置显示位置-列高地址
for(n=0;n<128;n++)
{
ssd1306_write_date(0); //更新显示
}
}
}
//在指定位置显示一个字符,包括部分字符
//x:0-127, y:0-7
//Char_size:选择字体16/12
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint16_t char_size)
{
uint8_t c=0, i=0;
c = chr-32; //得到偏移后的值
if(x>MAX_COLUMN-1)
{
x=0;
y++;
}
if(char_size == 16)
{
oled_set_pos(x,y);
for(i=0;i<8;i++)
{
ssd1306_write_date(asc2_1608[c][i]);//先写上半部分
}
oled_set_pos(x,y+1);
for(i=0;i<8;i++)
{
ssd1306_write_date(asc2_1608[c][i+8]);//后写下半部分
}
}
}
//显示一个字符串
void oled_show_string(uint8_t x, uint8_t y, char *str, uint8_t char_size)
{
unsigned char j=0;
while(str[j]!='\0')
{
oled_show_char(x,y,str[j],char_size);
x+=8;
if(x>120)
{
x=0;
y+=2;
}
j++;//移动一次就是一个page,取值0-7
}
}
//--------------------------------------------------------------
/*
Prototype : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
Calls :
Parameters : x,y -- 起始点坐标(x:0~127, y:0~7);
ch[] -- 要显示的字符串;
TextSize -- 字符大小(1:6*8 ; 2:8*16)
Description : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
*/
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 6:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
oled_set_pos(x,y);
for(i=0;i<6;i++)
ssd1306_write_date(asc2_0806[c][i]);
x += 6;
j++;
}
}break;
case 12:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
oled_set_pos(x,y);
for(i=0;i<6;i++)
ssd1306_write_date(asc2_1206[c][i]);
oled_set_pos(x,y+1);
for(i=0;i<6;i++)
ssd1306_write_date(asc2_1206[c][i+6]);
x += 6;
j++;
}
}break;
case 16:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
oled_set_pos(x,y);
for(i=0;i<8;i++)
ssd1306_write_date(asc2_1608[c][i]);//先写上半部分
oled_set_pos(x,y+1);
for(i=0;i<8;i++)
ssd1306_write_date(asc2_1608[c][i+8]);//后写下半部分
x += 8;
j++;
}
}break;
case 24:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
oled_set_pos(x,y);
for(i=0;i<12;i++)
ssd1306_write_date(asc2_2412[c][i]);//先写上半部分
oled_set_pos(x,y+1);
for(i=0;i<12;i++)
ssd1306_write_date(asc2_2412[c][i+12]);//后写下半部分
oled_set_pos(x,y+2);
for(i=0;i<12;i++)
ssd1306_write_date(asc2_2412[c][i+24]);//后写下半部分
x += 12;
j++;
}
}break;
}
}
/**
* @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_show_char(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0',16);
}
}
//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{
u8 t,adder=0;
oled_set_pos(x,y);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
adder+=1;
}
oled_set_pos(x,y+1);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
adder+=1;
}
}
//OLED初始化
void oled_init(void)
{
IIC_Init(400000,0x02);//初始化I2C总线,设置传输速率为400kHz
Delay_Ms(600);
//ssd1306复位之后,默认是页寻址方式
ssd1306_write_cmd(0xAE); //发送命令字节0xAE,关闭显示
ssd1306_write_cmd(0x00); //发送命令字节0x00,设置列低地址
ssd1306_write_cmd(0x10); //发送命令字节0x10,设置列高地址
ssd1306_write_cmd(0x40); //发送命令字节0x40,设置起始行地址
ssd1306_write_cmd(0xB0); //发送命令字节0xB0,设置页地址
ssd1306_write_cmd(0x81); //发送命令字节0x81,设置对比度控制
ssd1306_write_cmd(0xFF); //--128
ssd1306_write_cmd(0xA1); // 发送命令字节0xA1,设置段重映射为0到127
ssd1306_write_cmd(0xA6); // 发送命令字节0xA6,设置显示正常模式
ssd1306_write_cmd(0xA8); //发送命令字节0xA8,设置复用率为1到64
ssd1306_write_cmd(0x3F); // 发送命令字节0x3F,设置Duty为1/32
ssd1306_write_cmd(0xC8); // 发送命令字节0xC8,设置扫描方向
ssd1306_write_cmd(0xD3); //发送命令字节0xD3,设置显示偏移量
ssd1306_write_cmd(0x00); //发送命令字节0x00,设置偏移量为0
ssd1306_write_cmd(0xD5); //发送命令字节0xD5,设置显示时钟分频比和振荡器频率
ssd1306_write_cmd(0x80);//发送命令字节0x80,设置显示时钟分频比为默认值
ssd1306_write_cmd(0xD8); //发送命令字节0xD8,设置区域颜色模式关闭
ssd1306_write_cmd(0x05); //发送命令字节0x05,设置区域颜色模式关闭
ssd1306_write_cmd(0xD9); //发送命令字节0xD9,设置预充电周期
ssd1306_write_cmd(0xF1); //发送命令字节0xF1,设置预充电周期为默认值
ssd1306_write_cmd(0xDA); //发送命令字节0xDA,设置COM引脚硬件配置
ssd1306_write_cmd(0x12);//发送命令字节0x12,设置COM引脚硬件配置
ssd1306_write_cmd(0xDB); //发送命令字节0xDB,设置Vcomh
ssd1306_write_cmd(0x30); //发送命令字节0x30,设置Vcomh为0.77倍的Vcc
ssd1306_write_cmd(0x8D); //发送命令字节0x8D,启用电荷泵
ssd1306_write_cmd(0x14); //发送命令字节0x14,启用电荷泵
ssd1306_write_cmd(0xAF); //发送命令字节0xAF,打开OLED面板显示
}
#include "debug.h"
#include <stdio.h>
#include "dht11.h"
#include "ssd1306.h"
#include "IIC.h"
#include "timer.h"
#include "gpio.h"
/* Global typedef */
/* Global define */
/* Global Variable */
/*********************************************************************
* @fn USARTx_CFG
*
* @brief Initializes the USART2 & USART3 peripheral.
*
* @return none
*
*/
u32 distance = 0;
#define LED_PIN GPIO_Pin_4
#define USART_REC_LEN 200 //定义最大接收字节数 200
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));//声明了USART2_IRQHandler函数作为具有特定中断类型的中断处理程序
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
u8 flang;
u16 temp = 0,humi = 0;
u8 Res;
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = LED_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART2_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;////定义GPIO的结构体变量
USART_InitTypeDef USART_InitStructure;///定义USART的结构体变量
NVIC_InitTypeDef NVIC_InitStructure = {0};///定义NVIC的结构体变量初始化为0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);////打开GPIOA端口的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//打开USART2的时钟
// 配置USART1引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//TX//打开GPIO_Pin_2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//配置为复用推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置输出速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//RX//打开GPIO_Pin_3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;////配置为浮空输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
USART_Init(USART2, &USART_InitStructure);//初始化USART2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//配置USART的中断使能
// 配置USART2
USART_InitStructure.USART_BaudRate = 115200;//设置波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据位长度为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位为1位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//使能接收和发送功能
USART_Init(USART2, &USART_InitStructure);////初始化USART2
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//设置USART2_IRQn为USART2的中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//设置抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//设置子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道
NVIC_Init(&NVIC_InitStructure);////初始化NVIC
USART_Cmd(USART2, ENABLE);//使能USART2
}
void USART_SendByte(uint8_t data)
{
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, data);
}
void Serial_SendString(char *String) //发送字符串
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++) //空字符代表结束位
{
USART_SendByte(String[i]);
}
}
uint32_t Serial_Pow(uint32_t X, uint32_t Y) //字符串 次方函数
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}
void Serial_SendNumber(uint32_t Number, uint8_t Length) //显示字符串数字
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
USART_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}
void LED_On(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4,Bit_RESET);
}
void LED_Off(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4,Bit_SET);
}
void wendu(void)//温度显示 ʾ
{
OLED_ShowNum(72,4,temp/10000,1);//
OLED_ShowNum(80,4,temp%10000/1000,1);//
OLED_ShowNum(88,4,temp%1000/100,1);//
OLED_ShowNum(96,4,temp%100/10,1);//
OLED_ShowNum(104,4,temp%10,1);//
oled_show_char(112,4,'C',16);//
}
void shidu(void)//湿度显示 ʾ
{
OLED_ShowNum(72,6,humi/100,1);//
OLED_ShowNum(80,6,humi%100/10,1);//
OLED_ShowNum(88,6,humi%10,1);//
oled_show_char(96,6,'%',16);//
oled_show_char(104,6,'R',16);//
oled_show_char(112,6,'H',16);//
}
void chaoshenbo(void)//超声波距离显示 ʾ
{
OLED_ShowNum(72,2,distance/1000,1);//
OLED_ShowNum(80,2,distance%1000/100,1);//
OLED_ShowNum(88,2,distance%100/10,1);//
OLED_ShowNum(96,2,distance%10,1);//
oled_show_char(104,2,'c',16);//
oled_show_char(112,2,'m',16);//
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();// ʱ
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
oled_init();//OLED ʼ
oled_clear();//
OLED_ShowStr(28,0,"CH32V307!",16);
Input_Capture_Init(1000-1,72-1);// 2KHz Ƶ
OLED_ShowCHinese(0,4,9);
OLED_ShowCHinese(16,4,10);
OLED_ShowCHinese(32,4,11);
OLED_ShowCHinese(48,4,12);
oled_show_char(64,4,':',16);//
OLED_ShowCHinese(0,6,9);
OLED_ShowCHinese(16,6,10);
OLED_ShowCHinese(32,6,13);
OLED_ShowCHinese(48,6,12);
oled_show_char(64,6,':',16);//
OLED_ShowCHinese(0,2,14);
OLED_ShowCHinese(16,2,15);
OLED_ShowCHinese(32,2,16);
OLED_ShowCHinese(48,2,17);
oled_show_char(64,2,':',16);//
GPIO_Configuration();
USART2_Configuration();//USART2 ʼ
LED_Off();
while(DHT11_Init())
{
printf("DHT11 Error \r\n");
Delay_Ms(1000);
}
while(1)
{
DHT11_Read_Data(&temp,&humi);
printf("\r\n temp:%d,humi:%d",temp,humi);
Delay_Ms(1000);//建议不要低于这个数值
wendu();//温度显示
shidu();//湿度显示
distance = Ultrasoniclength(); //
// printf("DISTANCE:%dcm\r\n",distance);// ֵ
chaoshenbo();//超声波距离显示 ʾ
Serial_SendString("温度:");
Serial_SendNumber(temp, 2);//发送字符串,长度为2
Serial_SendString("C ");
Serial_SendString("湿度:");
Serial_SendNumber(humi, 2);//发送字符串,长度为2
Serial_SendString("%RH ");
Serial_SendString("距离:");
Serial_SendNumber(distance, 3);//发送字符串,长度为3
Serial_SendString("cm ");
Serial_SendString("\r\n");
if(flang==1)
{
switch(Res)
{
case '1':
LED_Off();
break;
case '0':
LED_On();
break;
}
flang=0;
}
}
}
void USART2_IRQHandler(void)//USART2中断处理函数
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//判断接收寄存器非空中断被触发
{
Res = USART_ReceiveData(USART2);//取接收寄存器的内容并将其保存在RxBuffer1变量中
flang=1;
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
下面是演示效果图,可以看到OLED屏、串口助手和蓝牙APP上的数据一模一样。
![96f0fa99530d92571797bd05c62e7cc.jpg](data/attachment/forum/202310/20/192252rsesj3luhccu3zua.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "96f0fa99530d92571797bd05c62e7cc.jpg")
[/i][/i][/i][/i][/i][/i][/i][/i]