找回密码
立即注册
搜索
热搜: 活动 交友 discuz
发新帖

116

积分

0

好友

12

主题
发表于 2023-10-20 19:29:20 | 查看: 440| 回复: 0 IP:广东省东莞市 电信

本次实验主要内容是把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上的数据一模一样。

580ae0ce867d971a39f7256f085dfa4.png

![96f0fa99530d92571797bd05c62e7cc.jpg](data/attachment/forum/202310/20/192252rsesj3luhccu3zua.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 96f0fa99530d92571797bd05c62e7cc.jpg"96f0fa99530d92571797bd05c62e7cc.jpg")

9d6e34a3fbde9528de82dce2b27e855.jpg [/i][/i][/i][/i][/i][/i][/i][/i]

DHT11 蓝牙模块 超声波模块显示在蓝牙APP.rar

583.72 KB, 下载次数: 0

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|国产电子社区 ( 沪ICP备2023018578号-1|

苏公网安备 32011102010465号


)|网站地图

GMT+8, 2024-5-29 22:07 , Processed in 0.068754 second(s), 22 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表