本次通过BMP280传感器采集到大气压数据与温度数据,然后通过蓝牙发送到微信小程序面板(谢谢@刘工)。
与此同时,微信小程序可以控制两盏灯的亮灭与亮度调节。
一、BMP280传感器介绍
BMP280是一款基于博世公司APSM工艺的小封装低功耗数字复合传感器,它可以测量环境温度和大气压强。气压敏感元件是一个低噪高精度高分辨率绝对大气压力压电式感应元件;温度感测元件具有低噪高分辨率特性,温度值可以对气压进行温度补偿自校正。
以下是BMP280气压传感器的参数:
供电电压 DC:1.7~3.6V
温度测量范围0~65℃
气压测量范围300~1100hPa(百帕斯卡)
气压测量误差±1hPa
数字接口类型IIC(从模式3.4MHz)或SPI(3线或4线制从模式10MHz)
BMP280的驱动代码:
#define BMP280_PRESSURE_OSR (BMP280_OVERSAMP_8X) #define BMP280_TEMPERATURE_OSR (BMP280_OVERSAMP_16X) #define BMP280_MODE (BMP280_PRESSURE_OSR<<2|BMP280_TEMPERATURE_OSR<<5|BMP280_NORMAL_MODE) typedef struct { u16 dig_T1; /* calibration T1 data */ s16 dig_T2; /* calibration T2 data */ s16 dig_T3; /* calibration T3 data */ u16 dig_P1; /* calibration P1 data */ s16 dig_P2; /* calibration P2 data */ s16 dig_P3; /* calibration P3 data */ s16 dig_P4; /* calibration P4 data */ s16 dig_P5; /* calibration P5 data */ s16 dig_P6; /* calibration P6 data */ s16 dig_P7; /* calibration P7 data */ s16 dig_P8; /* calibration P8 data */ s16 dig_P9; /* calibration P9 data */ s32 t_fine; /* calibration t_fine data */ } bmp280Calib; bmp280Calib bmp280Cal; static u8 bmp280ID=0; static bool isInit=false; static s32 bmp280RawPressure=0; static s32 bmp280RawTemperature=0; static void bmp280GetPressure(void); static void presssureFilter(float* in,float* out); static float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/); //BMP280引脚输出模式控制 void BMP280_IIC_SDA_OUT(void)//SDA输出方向配置 { /* GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=BMP280_IIC_SDA_PIN; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//SDA推挽输出 GPIO_Init(BMP280_IIC_PORT,&GPIO_InitStructure); */ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } void BMP280_IIC_SDA_IN(void)//SDA输入方向配置 { /* GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=BMP280_IIC_SDA_PIN; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//SCL上拉输入 GPIO_Init(BMP280_IIC_PORT,&GPIO_InitStructure);*/ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } //初始化IIC void BMP_IIC_Init(void) { /* GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(BMP280_IIC_CLK, ENABLE ); GPIO_InitStructure.GPIO_Pin=BMP280_IIC_SCL_PIN|BMP280_IIC_SDA_PIN; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP ; //推挽输出 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(BMP280_IIC_PORT,&GPIO_InitStructure); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_SET);*/ } //产生IIC起始信号 void BMP_IIC_Start(void) { BMP280_IIC_SDA_OUT(); //sda线输出 HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); delay_us(4); HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_RESET); //START:when CLK is high,DATA change form high to low delay_us(4); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); //钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void BMP_IIC_Stop(void) { BMP280_IIC_SDA_OUT(); //sda线输出 HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_RESET); //STOP:when CLK is high DATA change form low to high delay_us(4); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_SET); //发送I2C总线结束信号 delay_us(4); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 u8 BMP_IIC_Wait_Ack(void) { u8 ucErrTime=0; BMP280_IIC_SDA_IN(); //SDA设置为输入 HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_SET);delay_us(1); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET);delay_us(1); while(HAL_GPIO_ReadPin(GPIOB, SDA_Pin)) { ucErrTime++; if(ucErrTime>250) { BMP_IIC_Stop(); return 1; } } HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); //时钟输出0 return 0; } //产生ACK应答 void BMP_IIC_Ack(void) { HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); BMP280_IIC_SDA_OUT(); HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_RESET); delay_us(2); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); delay_us(2); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); } //不产生ACK应答 void BMP_IIC_NAck(void) { HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); BMP280_IIC_SDA_OUT(); HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_SET); delay_us(2); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); delay_us(2); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void BMP_IIC_Send_Byte(u8 txd) { u8 t; BMP280_IIC_SDA_OUT(); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); //拉低时钟开始数据传输 for(t=0;t<8;t++) { //BMP280_IIC_SDA=(txd&0x80)>>7; if((txd&0x80)>>7) HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_SET); else HAL_GPIO_WritePin(GPIOB, SDA_Pin, GPIO_PIN_RESET); txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的 HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); delay_us(2); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); delay_us(2); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 BMP_IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; BMP280_IIC_SDA_IN(); //SDA设置为输入 for(i=0;i<8;i++ ) { HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_RESET); delay_us(2); HAL_GPIO_WritePin(GPIOB, SCL_Pin, GPIO_PIN_SET); receive<<=1; if(HAL_GPIO_ReadPin(GPIOB, SDA_Pin))receive++; delay_us(1); } if (!ack) BMP_IIC_NAck(); //发送nACK else BMP_IIC_Ack(); //发送ACK return receive; } //从指定地址读出一个数据 //ReadAddr:开始读数的地址 //返回值 :读到的数据 u8 iicDevReadByte(u8 devaddr,u8 addr) { u8 temp=0; BMP_IIC_Start(); BMP_IIC_Send_Byte(devaddr); //发送器件写命令 BMP_IIC_Wait_Ack(); BMP_IIC_Send_Byte(addr); //发送低地址 BMP_IIC_Wait_Ack(); BMP_IIC_Start(); BMP_IIC_Send_Byte(devaddr|1); //发送器件读命令 BMP_IIC_Wait_Ack(); temp=BMP_IIC_Read_Byte(0); BMP_IIC_Stop(); //产生一个停止条件 return temp; } //连续读多个字节 //addr:起始地址 //rbuf:读数据缓存 //len:数据长度 void iicDevRead(u8 devaddr,u8 addr,u8 len,u8 *rbuf) { int i=0; BMP_IIC_Start(); BMP_IIC_Send_Byte(devaddr); BMP_IIC_Wait_Ack(); BMP_IIC_Send_Byte(addr); //地址自增 BMP_IIC_Wait_Ack(); BMP_IIC_Start(); BMP_IIC_Send_Byte(devaddr|1); BMP_IIC_Wait_Ack(); for(i=0; i<len; i++) { if(i==len-1) { rbuf[i]=BMP_IIC_Read_Byte(0); //最后一个字节不应答 } else rbuf[i]=BMP_IIC_Read_Byte(1); } BMP_IIC_Stop( ); } //从指定地址写入一个数据 //WriteAddr :写入数据的目的地址 //DataToWrite:要写入的数据 void iicDevWriteByte(u8 devaddr,u8 addr,u8 data) { BMP_IIC_Start(); BMP_IIC_Send_Byte(devaddr); //发送器件写命令 BMP_IIC_Wait_Ack(); BMP_IIC_Send_Byte(addr); //发送低地址 BMP_IIC_Wait_Ack(); BMP_IIC_Send_Byte(data); //发送字节 BMP_IIC_Wait_Ack(); BMP_IIC_Stop(); //产生一个停止条件 } //连续写多个字节 //addr:起始地址 //wbuf:写数据缓存 //len:数据的长度 void iicDevWrite(u8 devaddr,u8 addr,u8 len,u8 *wbuf) { int i=0; BMP_IIC_Start(); BMP_IIC_Send_Byte(devaddr); BMP_IIC_Wait_Ack(); BMP_IIC_Send_Byte(addr); //地址自增 BMP_IIC_Wait_Ack(); for(i=0; i<len; i++) { BMP_IIC_Send_Byte(wbuf[i]); BMP_IIC_Wait_Ack(); } BMP_IIC_Stop( ); } bool BMP280Init(void) { if (isInit) return true; //BMP_IIC_Init(); /*初始化I2C*/ HAL_Delay(20); bmp280ID=iicDevReadByte(BMP280_ADDR,BMP280_CHIP_ID); /* 读取bmp280 ID*/ if(bmp280ID==BMP280_DEFAULT_CHIP_ID) printf("BMP280 ID IS: 0x%X\n",bmp280ID); else return false; /* 读取校准数据 */ iicDevRead(BMP280_ADDR,BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG,24,(u8 *)&bmp280Cal); iicDevWriteByte(BMP280_ADDR,BMP280_CTRL_MEAS_REG,BMP280_MODE); iicDevWriteByte(BMP280_ADDR,BMP280_CONFIG_REG,5<<2); /*配置IIR滤波*/ isInit=true; return true; } static void bmp280GetPressure(void) { u8 data[BMP280_DATA_FRAME_SIZE]; // read data from sensor iicDevRead(BMP280_ADDR,BMP280_PRESSURE_MSB_REG,BMP280_DATA_FRAME_SIZE,data); bmp280RawPressure=(s32)((((uint32_t)(data[0]))<<12)|(((uint32_t)(data[1]))<<4)|((uint32_t)data[2]>>4)); bmp280RawTemperature=(s32)((((uint32_t)(data[3]))<<12)|(((uint32_t)(data[4]))<<4)|((uint32_t)data[5]>>4)); } // Returns temperature in DegC, resolution is 0.01 DegC. Output value of "5123" equals 51.23 DegC // t_fine carries fine temperature as global value static s32 bmp280CompensateT(s32 adcT) { s32 var1,var2,T; var1=((((adcT>>3)-((s32)bmp280Cal.dig_T1<<1)))*((s32)bmp280Cal.dig_T2))>>11; var2=(((((adcT>>4)-((s32)bmp280Cal.dig_T1))*((adcT>>4)-((s32)bmp280Cal.dig_T1)))>>12)*((s32)bmp280Cal.dig_T3))>>14; bmp280Cal.t_fine=var1+var2; T=(bmp280Cal.t_fine*5+128)>>8; return T; } // 以 Q24.8 格式(24 个整数位和 8 个小数位)将压力以 Pa 为单位返回为无符号 32 位整数。 // “24674867”的输出值表示 24674867/256 = 96386.2 Pa = 963.862 hPa static uint32_t bmp280CompensateP(s32 adcP) { int64_t var1,var2,p; var1=((int64_t)bmp280Cal.t_fine)-128000; var2=var1*var1*(int64_t)bmp280Cal.dig_P6; var2=var2+((var1*(int64_t)bmp280Cal.dig_P5)<<17); var2=var2+(((int64_t)bmp280Cal.dig_P4)<<35); var1=((var1*var1*(int64_t)bmp280Cal.dig_P3)>>8)+((var1*(int64_t)bmp280Cal.dig_P2)<<12); var1=(((((int64_t)1)<<47)+var1))*((int64_t)bmp280Cal.dig_P1)>>33; if (var1==0) return 0; p=1048576-adcP; p=(((p<<31)-var2)*3125)/var1; var1=(((int64_t)bmp280Cal.dig_P9)*(p>>13)*(p>>13))>>25; var2=(((int64_t)bmp280Cal.dig_P8)*p)>>19; p=((p+var1+var2)>>8)+(((int64_t)bmp280Cal.dig_P7)<<4); return(uint32_t)p; } #define FILTER_NUM 5 #define FILTER_A 0.1f /*限幅平均滤波法*/ static void presssureFilter(float* in,float* out) { static u8 i=0; static float filter_buf[FILTER_NUM]={0.0}; double filter_sum=0.0; u8 cnt=0; float deta; if(filter_buf[i]==0.0f) { filter_buf[i]=*in; *out=*in; if(++i>=FILTER_NUM) i=0; } else { if(i) deta=*in-filter_buf[i-1]; else deta=*in-filter_buf[FILTER_NUM-1]; if(fabs(deta)<FILTER_A) { filter_buf[i]=*in; if(++i>=FILTER_NUM) i=0; } for(cnt=0;cnt<FILTER_NUM;cnt++) { filter_sum+=filter_buf[cnt]; } *out=filter_sum /FILTER_NUM; } } void BMP280GetData(float* pressure,float* temperature,float* asl) { static float t; static float p; bmp280GetPressure(); t=bmp280CompensateT(bmp280RawTemperature)/100.0; p=bmp280CompensateP(bmp280RawPressure)/25600.0; presssureFilter(&p,pressure); *temperature=(float)t; /*单位度*/ *pressure=(float)p ; /*单位hPa*/ *asl=bmp280PressureToAltitude(pressure); /*转换成海拔*/ } #define CONST_PF 0.1902630958 //(1/5.25588f) Pressure factor #define FIX_TEMP 25 // Fixed Temperature. ASL is a function of pressure and temperature, but as the temperature changes so much (blow a little towards the flie and watch it drop 5 degrees) it corrupts the ASL estimates. // TLDR: Adjusting for temp changes does more harm than good. /* * Converts pressure to altitude above sea level (ASL) in meters */ static float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/) { if (*pressure>0) { return((pow((1015.7f/ *pressure),CONST_PF)-1.0f)*(FIX_TEMP+273.15f))/0.0065f; } else { return 0; } } 连接实物图:
在main.c里面调用:BMP280Init()来初始化BMP280传感器。通过串口打印,可以看到成功获取了压力与温度数据。
二、显示模块:OLED
OLED(Organic Light-Emitting Diode)显示屏,即有机发光二极管显示屏,是一种新型显示技术。它的工作原理是通过电流驱动有机材料发光,无需背光灯,采用非常薄的有机材料涂层和玻璃基板(或柔性有机基板)。
OLED显示屏具有诸多优点。首先,由于无需背光灯,其结构更轻更薄,可视角度更大。其次,OLED显示屏能够显著节省耗电量,提高能效。此外,它的色彩丰富、分辨率高,响应速度快,图像稳定。同时,OLED自身能够发光,因此其视域范围也要宽很多。
OLED显示屏的应用范围广泛。在博物馆和展览中,OLED透明屏可用于展示文物、艺术品、历史信息等,提供沉浸式的观展体验。在建筑和室内设计领域,OLED透明屏可以应用于建筑物的玻璃幕墙、楼梯扶手和室内装饰等,增强建筑的现代感和创意性。此外,OLED显示屏还广泛应用于汽车内部、户外广告牌、舞台和演艺表演等领域。
OLED本身是没有显存的,它的显存是依赖于SSD1306显存芯片提供的
从使用的角度上,我们驱动该OLED屏幕显示,实际是与驱动芯片SSD1306通信,让SSD1306控制OLED点阵显示。SSD1306相当于一个中介,我们只需要了解SSD1306的功能、寄存器、总线、驱动流程等参数或工作方式,根据SSD1306的工作方式通信即可。
SSD1306
特性
分辨率:128 x 64 点阵
电源:对于集成逻辑电路,VDD=1.65V---3.3V;对于面板驱动,VCC=7V---15V
点阵驱动:OLED驱动输出电压,最大15V;SEG最大源电流:100uA;COM最大汇电流:15mA;256阶对比度亮度电流控制
内置128 x 64位SRAM显示缓冲区
引脚可选择的MCU接口:8位6800/8080串并接口、 3/4线串行外围接口、I2C接口
水平和垂直方向的屏幕保持连续滚动功能
RAM写同步信号
可编程帧速率和复用率
行重映射和列重映射
片内内置振荡器
COG和COF的芯片封装
工作温度范围广:-40°C至85°C
OLED驱动:
#include "oled.h" #include "stdlib.h" #include "oledfont.h" #include "main.h" extern I2C_HandleTypeDef hi2c1; uint8_t OLED_GRAM[144][8]; void OLED_ColorTurn(uint8_t i) { if(i==0) { WriteCmd(0xA6); } if(i==1) { WriteCmd(0xA7); } } void OLED_DisplayTurn(uint8_t i) { if(i==0) { WriteCmd(0xC8); WriteCmd(0xA1); } if(i==1) { WriteCmd(0xC0); WriteCmd(0xA0); } } void IIC_delay(void) { uint16_t t=300; while(t--); } void HAL_I2C_WriteByte(uint8_t addr,uint8_t data) { uint8_t TxData[2] = {addr,data}; HAL_I2C_Master_Transmit(&hi2c1,0X78,(uint8_t*)TxData,2,10); } /************************************************************** Prototype : void WriteCmd(uint8_t IIC_Command) Parameters : IIC_Command return : none Description : ***************************************************************/ void WriteCmd(uint8_t IIC_Command) { HAL_I2C_WriteByte(0x00, IIC_Command); } /************************************************************** Prototype : void WriteDat(uint8_t IIC_Data) Parameters : IIC_Data return : none Description : ***************************************************************/ void WriteDat(uint8_t IIC_Data) { HAL_I2C_WriteByte(0x40, IIC_Data); } void Send_Byte(uint8_t dat){ //I2C_SendData(I2C1, dat); //while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE)); HAL_I2C_Master_Transmit(&hi2c1, 0x78, &dat, 1, 0xffffffff); } void OLED_WR_Byte(uint8_t dat,uint8_t mode) { if(mode){ //Send_Byte(0x40); WriteDat(dat); } else{ //Send_Byte(0x00); WriteCmd(dat); } //Send_Byte(dat); //I2C_Stop(); } void OLED_DisPlay_On(void) { WriteCmd(0x8D); WriteCmd(0x14); WriteCmd(0xAF); } void OLED_DisPlay_Off(void) { WriteCmd(0x8D); WriteCmd(0x10); WriteCmd(0xAE); } void OLED_Refresh(void) { uint8_t i,n; for(i=0;i<8;i++) { WriteCmd(0xb0+i); WriteCmd(0x00); WriteCmd(0x10); for(n=0;n<128;n++) { WriteDat(OLED_GRAM[n][i]); } } } void OLED_Clear(void) { uint8_t i,n; for(i=0;i<8;i++) { for(n=0;n<128;n++) { OLED_GRAM[n][i]=0;//锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟� } } } //x:0~127 //y:0~63 //t:1 void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t) { uint8_t i,m,n; i=y/8; m=y%8; n=1<<m; if(t){OLED_GRAM[x][i]|=n;} else { OLED_GRAM[x][i]=~OLED_GRAM[x][i]; OLED_GRAM[x][i]|=n; OLED_GRAM[x][i]=~OLED_GRAM[x][i]; } } void OLED_DrawLine(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t mode) { uint16_t t; int xerr=0,yerr=0,delta_x,delta_y,distance; int incx,incy,uRow,uCol; delta_x=x2-x1; // delta_y=y2-y1; uRow=x1;// uCol=y1; if(delta_x>0)incx=1; // else if (delta_x==0)incx=0;// else {incx=-1;delta_x=-delta_x;} if(delta_y>0)incy=1; else if (delta_y==0)incy=0;// else {incy=-1;delta_y=-delta_x;} if(delta_x>delta_y)distance=delta_x; // else distance=delta_y; for(t=0;t<distance+1;t++) { OLED_DrawPoint(uRow,uCol,mode);// xerr+=delta_x; yerr+=delta_y; if(xerr>distance) { xerr-=distance; uRow+=incx; } if(yerr>distance) { yerr-=distance; uCol+=incy; } } } void OLED_DrawCircle(uint8_t x,uint8_t y,uint8_t r) { int a, b,num; a = 0; b = r; while(2 * b * b >= r * r) { OLED_DrawPoint(x + a, y - b,1); OLED_DrawPoint(x - a, y - b,1); OLED_DrawPoint(x - a, y + b,1); OLED_DrawPoint(x + a, y + b,1); OLED_DrawPoint(x + b, y + a,1); OLED_DrawPoint(x + b, y - a,1); OLED_DrawPoint(x - b, y - a,1); OLED_DrawPoint(x - b, y + a,1); a++; num = (a * a + b * b) - r*r;// if(num > 0) { b--; a--; } } } void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size1,uint8_t mode) { uint8_t i,m,temp,size2,chr1; uint8_t x0=x,y0=y; if(size1==8)size2=6; else size2=(size1/8+((size1%8)?1:0))*(size1/2); // chr1=chr-' '; for(i=0;i<size2;i++) { if(size1==8) {temp=asc2_0806[chr1][i];} else if(size1==12) {temp=asc2_1206[chr1][i];} else if(size1==16) {temp=asc2_1608[chr1][i];} else if(size1==24) {temp=asc2_2412[chr1][i];} else return; for(m=0;m<8;m++) { if(temp&0x01)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp>>=1; y++; } x++; if((size1!=8)&&((x-x0)==size1/2)) {x=x0;y0=y0+8;} y=y0; } } void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t size1,uint8_t mode) { while((*chr>=' ')&&(*chr<='~'))// { OLED_ShowChar(x,y,*chr,size1,mode); if(size1==8)x+=6; else x+=size1/2; chr++; } } //m^n uint32_t OLED_Pow(uint8_t m,uint8_t n) { uint32_t result=1; while(n--) { result*=m; } return result; } void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size1,uint8_t mode) { uint8_t t,temp,m=0; if(size1==8)m=2; for(t=0;t<len;t++) { temp=(num/OLED_Pow(10,len-t-1))%10; if(temp==0) { OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode); } else { OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode); } } } void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t num,uint8_t size1,uint8_t mode) { uint8_t m,temp; uint8_t x0=x,y0=y; uint16_t i,size3=(size1/8+((size1%8)?1:0))*size1; // for(i=0;i<size3;i++) { if(size1==16) {temp=Hzk1[num][i];}// else if(size1==24) {temp=Hzk2[num][i];}// else if(size1==32) {temp=Hzk3[num][i];}// else if(size1==64) {temp=Hzk4[num][i];}// else return; for(m=0;m<8;m++) { if(temp&0x01)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp>>=1; y++; } x++; if((x-x0)==size1) {x=x0;y0=y0+8;} y=y0; } } void OLED_ScrollDisplay(uint8_t num,uint8_t space,uint8_t mode) { uint8_t i,n,t=0,m=0,r; while(1) { if(m==0) { OLED_ShowChinese(128,24,t,16,mode); // t++; } if(t==num) { for(r=0;r<16*space;r++) // { for(i=1;i<144;i++) { for(n=0;n<8;n++) { OLED_GRAM[i-1][n]=OLED_GRAM[i][n]; } } OLED_Refresh(); } t=0; } m++; if(m==16){m=0;} for(i=1;i<144;i++) // { for(n=0;n<8;n++) { OLED_GRAM[i-1][n]=OLED_GRAM[i][n]; } } OLED_Refresh(); } } void OLED_ShowPicture(uint8_t x,uint8_t y,uint8_t sizex,uint8_t sizey,uint8_t BMP[],uint8_t mode) { uint16_t j=0; uint8_t i,n,temp,m; uint8_t x0=x,y0=y; sizey=sizey/8+((sizey%8)?1:0); for(n=0;n<sizey;n++) { for(i=0;i<sizex;i++) { temp=BMP[j]; j++; for(m=0;m<8;m++) { if(temp&0x01)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp>>=1; y++; } x++; if((x-x0)==sizex) { x=x0; y0=y0+8; } y=y0; } } } void OLED_Init(void) { HAL_Delay(1000); WriteCmd(0xAE);//--turn off oled panel WriteCmd(0x00);//---set low column address WriteCmd(0x10);//---set high column address WriteCmd(0x40);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F) WriteCmd(0x81);//--set contrast control register WriteCmd(0xCF);// Set SEG Output Current Brightness WriteCmd(0xA1);//--Set SEG/Column Mapping WriteCmd(0xC8);//Set COM/Row Scan Direction WriteCmd(0xA6);//--set normal display WriteCmd(0xA8);//--set multiplex ratio(1 to 64) WriteCmd(0x3f);//--1/64 duty WriteCmd(0xD3);//-set display offset Shift Mapping RAM Counter (0x00~0x3F) WriteCmd(0x00);//-not offset WriteCmd(0xd5);//--set display clock divide ratio/oscillator frequency WriteCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec WriteCmd(0xD9);//--set pre-charge period WriteCmd(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock WriteCmd(0xDA);//--set com pins hardware configuration WriteCmd(0x12); WriteCmd(0xDB);//--set vcomh WriteCmd(0x40);//Set VCOM Deselect Level WriteCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02) WriteCmd(0x02);// WriteCmd(0x8D);//--set Charge Pump enable/disable WriteCmd(0x14);//--set(0x10) disable WriteCmd(0xA4);// Disable Entire Display On (0xa4/0xa5) WriteCmd(0xA6);// Disable Inverse Display On (0xa6/a7) OLED_Clear(); WriteCmd(0xAF); //Set display on added by sensoryoung HAL_Delay(200); }
三、蓝牙交互:
在按键按下后,发送压力与温度数据到微信小程序:
__USED void P2P_SERVER_Switch_c_SendNotification(void) /* Property Notification */ { P2P_SERVER_APP_SendInformation_t notification_on_off = Switch_c_NOTIFICATION_OFF; P2P_SERVER_Data_t p2p_server_notification_data; p2p_server_notification_data.p_Payload = (uint8_t*)a_P2P_SERVER_UpdateCharData; p2p_server_notification_data.Length = 0; /* USER CODE BEGIN Service1Char2_NS_1 */ if(P2P_SERVER_APP_Context.ButtonControl.ButtonStatus == 0x00) { P2P_SERVER_APP_Context.ButtonControl.ButtonStatus = 0x01; } else { P2P_SERVER_APP_Context.ButtonControl.ButtonStatus = 0x00; } /* a_P2P_SERVER_UpdateCharData[0] = 0x01; Device Led selection a_P2P_SERVER_UpdateCharData[1] = P2P_SERVER_APP_Context.ButtonControl.ButtonStatus; */ #if 1 BMP280GetData(&bmp280_press,&bmp280,&haiba); // LCD_ShowIntNum(160,190,(uint16_t)bmp280_press,4,RED,WHITE,16); // LCD_ShowFloatNum1(160,210,bmp280,4,RED,WHITE,16); OLED_Clear(); OLED_ShowString(0,0," Smarter Board",8,1); OLED_ShowPicture(16, 22, 20, 20, BMP_Pressure_20x20, 1); OLED_ShowPicture(72, 22, 24, 24, BMP_Temperature_24x24, 1); static uint8_t PressureArrary[8]; sprintf(PressureArrary,"%d hPa" , (uint16_t)bmp280_press); OLED_ShowString(0,56,PressureArrary,8,1); //OLED_ShowNum(0,56,(uint16_t)bmp280_press,4,8,1); static uint8_t tempCharArrary[6]; sprintf(tempCharArrary,"%.2f" , bmp280); OLED_ShowString(64,56,tempCharArrary,8,1); OLED_ShowPicture(100, 56, 8, 8, BMP_Degree_8x8, 1); OLED_Refresh(); printf("Pressure: =%.2f hPa Temp:%.2f锟斤拷C \r\n", bmp280_press, bmp280); #endif a_P2P_SERVER_UpdateCharData[0] = ((uint16_t)bmp280_press)/10; a_P2P_SERVER_UpdateCharData[1] = ((uint16_t)bmp280_press)%10; a_P2P_SERVER_UpdateCharData[2] = ((uint8_t)bmp280)/10; a_P2P_SERVER_UpdateCharData[3] = ((uint8_t)bmp280)%10; /* Update notification data length */ p2p_server_notification_data.Length = (p2p_server_notification_data.Length) + 4; if(P2P_SERVER_APP_Context.Switch_c_Notification_Status == Switch_c_NOTIFICATION_ON) { LOG_INFO_APP("-- P2P APPLICATION SERVER : INFORM CLIENT BUTTON 1 PUSHED\n"); notification_on_off = Switch_c_NOTIFICATION_ON; } else { LOG_INFO_APP("-- P2P APPLICATION SERVER : CAN'T INFORM CLIENT - NOTIFICATION DISABLED\n"); } /* USER CODE END Service1Char2_NS_1 */ if (notification_on_off != Switch_c_NOTIFICATION_OFF) { P2P_SERVER_UpdateValue(P2P_SERVER_SWITCH_C, &p2p_server_notification_data); } /* USER CODE BEGIN Service1Char2_NS_Last */ /* USER CODE END Service1Char2_NS_Last */ return; }
在接收到来自微信小程序的数据后,解析是否调节小灯亮度。
void P2P_SERVER_Notification(P2P_SERVER_NotificationEvt_t *p_Notification) { /* USER CODE BEGIN Service1_Notification_1 */ /* USER CODE END Service1_Notification_1 */ switch(p_Notification->EvtOpcode) { /* USER CODE BEGIN Service1_Notification_Service1_EvtOpcode */ /* USER CODE END Service1_Notification_Service1_EvtOpcode */ case P2P_SERVER_LED_C_READ_EVT: /* USER CODE BEGIN Service1Char1_READ_EVT */ LOG_INFO_APP("-- Inside p2p server app.c: Line 120: P2P_SERVER_LED_C_READ_EVT\n"); /* USER CODE END Service1Char1_READ_EVT */ break; case P2P_SERVER_LED_C_WRITE_NO_RESP_EVT: /* USER CODE BEGIN Service1Char1_WRITE_NO_RESP_EVT */ if(p_Notification->DataTransfered.p_Payload[1] == 0x01) { BSP_LED_On(LED_BLUE); LOG_INFO_APP("-- P2P APPLICATION SERVER : LED1 ON\n"); P2P_SERVER_APP_Context.LedControl.Led1 = 0x01; /* LED1 ON */ } if(p_Notification->DataTransfered.p_Payload[1] == 0x00) { BSP_LED_Off(LED_BLUE); LOG_INFO_APP("-- P2P APPLICATION SERVER : LED1 OFF\n"); P2P_SERVER_APP_Context.LedControl.Led1 = 0x00; /* LED1 OFF */ } if(p_Notification->DataTransfered.p_Payload[0] == 0x03) { BSP_LED_Off(LED_BLUE); LOG_INFO_APP("-- P2P APPLICATION SERVER : LED PWM CONTROL, PWM Value = %d\n", p_Notification->DataTransfered.p_Payload[1] ); pwm_value =p_Notification->DataTransfered.p_Payload[1]; //P2P_SERVER_APP_Context.LedControl.Led1 = 0x00; /* LED1 OFF */ } /* USER CODE END Service1Char1_WRITE_NO_RESP_EVT */ break; case P2P_SERVER_SWITCH_C_NOTIFY_ENABLED_EVT: /* USER CODE BEGIN Service1Char2_NOTIFY_ENABLED_EVT */ P2P_SERVER_APP_Context.Switch_c_Notification_Status = Switch_c_NOTIFICATION_ON; LOG_INFO_APP("-- P2P APPLICATION SERVER : NOTIFICATION ENABLED\n"); LOG_INFO_APP(" \n\r"); /* USER CODE END Service1Char2_NOTIFY_ENABLED_EVT */ break; case P2P_SERVER_SWITCH_C_NOTIFY_DISABLED_EVT: /* USER CODE BEGIN Service1Char2_NOTIFY_DISABLED_EVT */ P2P_SERVER_APP_Context.Switch_c_Notification_Status = Switch_c_NOTIFICATION_OFF; LOG_INFO_APP("-- P2P APPLICATION SERVER : NOTIFICATION DISABLED\n"); LOG_INFO_APP(" \n\r"); /* USER CODE END Service1Char2_NOTIFY_DISABLED_EVT */ break; default: /* USER CODE BEGIN Service1_Notification_default */ /* USER CODE END Service1_Notification_default */ break; } /* USER CODE BEGIN Service1_Notification_2 */ /* USER CODE END Service1_Notification_2 */ return; }
在上面的代码里获取到PWM值,然后main程序内控制亮度:
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Config code for STM32_WPAN (HSE Tuning must be done before system clock configuration) */ MX_APPE_Config(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* Configure the peripherals common clocks */ PeriphCommonClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_GPDMA1_Init(); MX_RAMCFG_Init(); MX_RTC_Init(); MX_RNG_Init(); MX_ICACHE_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ MX_USART1_UART_Init(); OLED_Init(); OLED_Refresh(); HAL_Delay(500); long currentMillis = 0; long lastMillis = 0; BMP280Init(); printf("BME280 Init Done\r\n"); MX_APPE_Init(NULL); /* Infinite loop */ /* USER CODE BEGIN WHILE */ BSP_LED_On(LED_BLUE); HAL_Delay(300); BSP_LED_Off(LED_BLUE); HAL_Delay(300); BSP_LED_On(LED_GREEN); HAL_Delay(300); BSP_LED_Off(LED_GREEN); HAL_Delay(300); BSP_LED_On(LED_RED); HAL_Delay(300); BSP_LED_Off(LED_RED); HAL_Delay(300); BSP_LED_Off(LED_BLUE);BSP_LED_Off(LED_GREEN);BSP_LED_Off(LED_RED); static volatile uint8_t pwm_value_old = 0; while (1) { /* USER CODE END WHILE */ MX_APPE_Process(); //BreathLED(pwm_value_old); BSP_LED_On(LED_RED); delay_us(pwm_value); BSP_LED_Off(LED_RED); delay_us(100 - pwm_value); }//end of while /* USER CODE END 3 */ }
上面的代码的PWM调光亮度采用的最简单的GPIO模拟PWM方式,原理如下。控制的是板载LD3即红色的LED。
BSP_LED_On(LED_RED); delay_us(pwm_value); BSP_LED_Off(LED_RED); delay_us(100 - pwm_value);
四、微信小程序,参考刘工的文件。
如下是实物,可以看到微信小程序获取了温度与压力数据,微信小程序的PWM数值会反应到板载红色LED灯亮度上。
项目代码:
项目视频: https://www.bilibili.com/video/BV1fFrsY5EZ6/?spm_id_from=333.1365.list.card_archive.click