STM32读取SHT3x系列温湿度传感器,标准库和HAL库
STM32读取SHT3x系列(SHT30、SHT31、SHT35)温湿度传感器的数据并显示在0.96寸OLED屏上。
我下面提供两份代码,一份是标准库使用硬件I2C的,另一份是HAL库使用软件模拟IIC的。
我用的单片机是STM32F103C8T6,温湿度传感器是SHT30。
STM32软件I2C读取AM2320温湿度传感器数据:https://blog.zeruns.tech/archives/695.html
STM32使用硬件I2C读取SHTC3温湿度传感器:https://blog.zeruns.tech/archives/692.html
STM32单片机读取AHT10温湿度传感器数据:https://blog.zeruns.tech/archives/693.html
实现效果图
I2C协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备(那些电平转化芯片),现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C只有一跟数据总线 SDA(Serial Data Line),串行数据总线,只能一位一位的发送数据,属于串行通信,采用半双工通信
半双工通信:可以实现双向的通信,但不能在两个方向上同时进行,必须轮流交替进行,其实也可以理解成一种可以切换方向的单工通信,同一时刻必须只能一个方向传输,只需一根数据线.
对于I2C通讯协议把它分为物理层和协议层物理层规定通讯系统中具有机械、电子功能部分的特性(硬件部分),确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准(软件层面)。
I2C物理层
I2C 通讯设备之间的常用连接方式
(1) 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线SDA(Serial Data Line ),一条串行时钟线SCL(Serial Data Line )。数据线即用来表示数据,时钟线用于数据收发同步
(3) 总线通过上拉电阻接到电源。当 I2C 设备空闲时会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
I2C通信时单片机GPIO口必须设置为开漏输出,否则可能会造成短路。
关于更多STM32的I2C相关信息和使用方法可以看这篇文章:https://url.zeruns.tech/JC0Ah
我这里就不详细讲解了。
SHT3x温湿度传感器
SHT3x数据手册下载地址:
原版:https://url.zeruns.tech/uWNnP 提取码:icqu
中文版(机翻):https://url.zeruns.tech/n9b9t 提取码:vedy
浏览数据手册可以得到一个大概信息,SHT30是一个可以检测温度和湿度的传感器,
温度范围:-40℃~125℃
湿度范围:0%~100%
工作电压:2.4v~5.5v
通讯方式:i2c
时钟频率:0 ~ 1000kHz
找到如下几个关键信息
SHT3x设备地址和读写命令
SHT3x的地址可以通过第2个引脚接高或低电平来设置,我在淘宝买的模块的传感器2脚是通过一个电阻接到VCC,也就是默认是0x44。
在实际的使用过程中,SHT3x的设备地址需要与读写数据/命令方向位组成一个字节同时发送,字节的最低位为读写数据/命令方向位,高7位是SHT3x的设备地址。
如果要通过I2C写数据或命令给SHT3x,在I2C起始信号之后,需要发送“1000 1000”,即0x88给SHT3x,除了通过高7位“1000 100”(0x44)的设备地址寻址还通过最低位“0”通知SHT3x接下来是写数据或命令操作。
如果要通过I2C读取SHT3x中的数据,在I2C起始信号之后,需要发送“1000 1001”,即0x89给SHT3x,除了通过高7位“1000 100”的设备地址寻址还通过最低位“1”通知SHT3x接下来是读取数据的操作。
简单来说就是,0x88表示写数据,0x89表示读数据。不过使用STM32硬件I2C时只需要输入0x88就行,最低位标准库会处理的。
读取温湿度数据
可知,不同的命令,获取的数据方式不一样,有单次测量和周期测量模式,还有一个Clock Stretching Enable 和 Disable的区别。
Clock Stretching是时钟拉伸的意思。如果使用Clock Stretching Enable命令的话,那么发送完测量命令之后,在SHT3x测量温度湿度数据的过程中,SHT3x会拉低I2C的时钟线SCL,通过这样来禁止主机发送命令给SHT3x,只有当SHT3x完成温度湿度数据测量时,SHT3x才会释放时钟线SCL。
如果使用Clock Stretching Disable命令的话,在SHT3x测量数据的过程中,SHT3x并不会拉低I2C的时钟线SCL,只是如果主机在SHT3x测量数据的过程中发送命令或数据的时候,发送读取指令SHT3x是会返回非应答信号的,只有测量完成再发送读取指令才会返回应答信号。
周期测量模式可以让传感器自动测量并保存数据,可以设置每秒测量0.5/1/2/4/10次,然后通过读取指令0xE000可以读取最新的测量结果。
从数据手册可知,一个测量周期包概括2个步骤:
- 发送测量命令
- 读取测量完成之后的数据。
以上测量命令和读取命令可以在数据手册中查询。
总结如下:
- 发送测量命令:先发送写入指令(0x88),再发送唤醒指令高位(0x2C),再发送唤醒指令低位(0x0D)。
- 读取数据并等待测量完成:发送读取指令(0x89),等待从机释放SCL总线。
- 接收数据:连续接收6个字节数据。这6个字节的第1-2个字节就是温度数值,第3个字节是温度校验。第4-5个字节是湿度数值,第6个字节是湿度校验。接收最后一个字节后发送非应答信号。
- 处理数据:对数据进行CRC校验并处理。
数据的计算
由SHT3x数据手册可知
例如:采集到的湿度数值是0x6501,换算成十进制是25857。
则:湿度 = 100 * 25857 / (65536 - 1) = 39.45 (单位:%)
采集到的温度数值是0x6600,换算成十进制是26112。
则:温度 = -45 + 175 * 26112 / (65536 - 1) = 24.72 (单位:℃)
需要用的元件
STM32最小系统板:https://s.click.taobao.com/AJEGiNu
SHT3x模块:https://s.click.taobao.com/55xGiNu
OLED模块:https://s.click.taobao.com/0dlG0Ou
杜邦线:https://s.click.taobao.com/xAkAJRu
面包板:https://s.click.taobao.com/ShJAJRu
ST-LINK V2:https://s.click.taobao.com/C8ftZRu
程序
这里就放出标准库版的main.c、SHT3x.c和oled.c这三个主要的代码,其他的请下载下面链接的压缩包。
标准库版:https://url.zeruns.tech/a49EX 提取码:8nn5
HAL库版:https://url.zeruns.tech/p3og4 提取码:v9wc
SHT3x和OLED模块的SCL接PB6,SDA接PB7。
使用VSCode代替Keil实现STM32和51单片机的开发:https://blog.zeruns.tech/archives/690.html
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "IWDG.h"
#include "SHT3x.h"uint16_t numlen(uint16_t num);int main(void)
{IWDG_Configuration(); //初始化看门狗OLED_Init(); //初始化OLED屏SHT3x_Init(); //初始化SHT3x/*OLED_ShowString(1, 1, "T:");OLED_ShowString(3, 1, "H:");*/uint8_t Chinese[]={0,1};OLED_ShowChinese(1,1,Chinese,2);//温度uint8_t Chinese1[]={2,3};OLED_ShowChinese(3,1,Chinese1,2);//湿度uint8_t Chinese2[]={4};OLED_ShowChinese(1,11,Chinese2,1);//℃uint32_t a=0;uint16_t err_count=0;while (1){a++;OLED_ShowNum(2, 14, a, 2);if(a==99)a=0;float Temp,Hum; //声明变量存放温湿度数据if(ReadSHT3x(&Hum,&Temp)) //读取温湿度数据{if(Temp>=0){char String[10];sprintf(String, "+%.2f", Temp);//格式化字符串输出到字符串变量OLED_ShowString(1, 5, String); //显示温度sprintf(String, " %.2f%%", Hum); //格式化字符串输出到字符串变量OLED_ShowString(3, 5, String); //显示湿度}else{char String[10];sprintf(String, "%.2f", Temp);//格式化字符串输出到字符串变量OLED_ShowString(1, 5, String); //显示温度sprintf(String, " %.2f%%", Hum); //格式化字符串输出到字符串变量OLED_ShowString(3, 5, String); //显示湿度}}else{err_count++;OLED_ShowNum(1,14, err_count, 2); //显示错误次数计数}/*https://blog.zeruns.tech*/Delay_ms(200); //延时200毫秒IWDG_FeedDog(); //喂狗(看门狗,超过1秒没有执行喂狗则自动复位)}
}/*** @brief 计算整数长度* @param num 要计算长度的整数* @retval 长度值*/
uint16_t numlen(uint16_t num)
{uint16_t len = 0; // 初始长度为0for(; num > 0; ++len) // 判断num是否大于0,否则长度+1num /= 10; // 使用除法进行运算,直到num小于1return len; // 返回长度的值
}
SHT3x.c
#include "stm32f10x.h"
#include "Delay.h"/*SHT3x地址*/
#define SHT3x_ADDRESS 0x44<<1 //从机地址是7位,所以左移一位/*设置使用哪一个I2C*/
#define I2Cx I2C1/*
https://blog.zeruns.tech
*//*** @brief CRC校验,CRC多项式为:x^8+x^5+x^4+1,即0x31* @param DAT 要校验的数据* @retval 校验码*/
uint8_t SHT3x_CRC_CAL(uint16_t DAT)
{uint8_t i,t,temp;uint8_t CRC_BYTE;CRC_BYTE = 0xFF;temp = (DAT>>8) & 0xFF;for(t = 0; t < 2; t++){CRC_BYTE ^= temp;for(i = 0;i < 8;i ++){if(CRC_BYTE & 0x80){CRC_BYTE <<= 1;CRC_BYTE ^= 0x31;}else{CRC_BYTE <<= 1;}}if(t == 0){temp = DAT & 0xFF;}}return CRC_BYTE;
}/*发送起始信号*/
void SHT3x_I2C_START(){while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//等待总线空闲I2C_GenerateSTART(I2Cx, ENABLE);//发送起始信号while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//检测EV5事件
}/*发送停止信号*/
void SHT3x_I2C_STOP(){I2C_GenerateSTOP(I2Cx, ENABLE);//发送停止信号
}/*** @brief 发送两个字节数据* @param MSB 高8位* @param LSB 低8位* @retval 无*/
void SHT3x_WriteByte(uint8_t MSB,uint8_t LSB)
{SHT3x_I2C_START(); //发送起始信号I2C_Send7bitAddress(I2Cx, SHT3x_ADDRESS, I2C_Direction_Transmitter); //发送设备写地址while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //检测EV6事件I2C_SendData(I2Cx, MSB);//发送高8位数据while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检测EV8事件I2C_SendData(I2Cx, LSB);//发送低8位数据while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检测EV8事件I2C_GenerateSTOP(I2Cx, ENABLE);//发送停止信号}/*** @brief 读取数据* @retval 读取到的字节数据*/
uint8_t SHT3x_ReadData()
{while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//检测EV7事件return I2C_ReceiveData(I2Cx);//读取数据并返回
}/*软件复位SHT3x*/
void SHT3x_SoftReset(void)
{SHT3x_WriteByte(0x30,0xA2); //重新初始化SHT3x
}/*引脚初始化*/
void SHT3x_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //使能I2C1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟/*STM32F103芯片的硬件I2C1: PB6 -- SCL; PB7 -- SDA */GPIO_InitTypeDef GPIO_InitStructure; //定义结构体配置GPIOGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //设置输出模式为开漏输出,需接上拉电阻GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOI2C_DeInit(I2Cx); //将外设I2C寄存器重设为缺省值I2C_InitTypeDef I2C_InitStructure; //定义结构体配置I2CI2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //工作模式I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,Tlow/Thigh = 2I2C_InitStructure.I2C_OwnAddress1 = 0x30; //主机的I2C地址,用不到则随便写,无影响I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能应答位I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置地址长度7位I2C_InitStructure.I2C_ClockSpeed = 400000; //I2C传输速度,400K,根据自己所用芯片手册查看支持的速度。 I2C_Init(I2Cx, &I2C_InitStructure); //初始化I2CI2C_Cmd(I2Cx, ENABLE); //启用I2C//SHT3x_WriteByte(0X27,0X21); //周期数据采集模式(每秒10次,Medium Repeatability)Delay_us(300);//延时300微秒
}/*** @brief 读取SHT3x数据* @param *Hum 湿度* @param *Temp 温度* @retval 1 - 读取成功;0 - 读取失败*/
uint8_t ReadSHT3x(float *Hum,float *Temp)
{uint16_t HumData,TempData,HumCRC,TempCRC;//声明变量存放读取的数据//SHT3x_WriteByte(0XE0,0X00); //发送指令,获取数据,周期数据采集模式用SHT3x_WriteByte(0X2C,0X0D); //发送单次测量指令(启动时钟延伸,Medium Repeatability)SHT3x_I2C_START();//发送起始信号I2C_Send7bitAddress(I2Cx,SHT3x_ADDRESS,I2C_Direction_Receiver);//发送设备读地址while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//检测EV6事件TempData = SHT3x_ReadData();//读取温度高8位数据TempData=TempData<<8; //左移8位TempData |= SHT3x_ReadData();//读取温度低8位数据TempCRC = SHT3x_ReadData(); //读取温度CRC校验数据HumData = SHT3x_ReadData(); //读取湿度高8位数据HumData=HumData<<8; //左移8位HumData |= SHT3x_ReadData();//读取湿度低8位数据I2C_AcknowledgeConfig(I2Cx,DISABLE); //关闭应答信号HumCRC = SHT3x_ReadData(); //读取湿度CRC校验数据SHT3x_I2C_STOP(); //发送停止信号I2C_AcknowledgeConfig(I2Cx,ENABLE); //开启应答信号if( SHT3x_CRC_CAL(HumData)==HumCRC && SHT3x_CRC_CAL(TempData)==TempCRC ){ //对接收到数据进行CRC校验*Hum = (float)HumData*100/(65536-1); //将接收的16位二进制数据转换为10进制湿度数据*Temp = (float)TempData*175/(65536-1)-45; //将接收的16位二进制数据转换为10进制温度数据return 1;}return 0;
}
oled.c
#include "stm32f10x.h"
#include "OLED_Font.h"/*OLED屏地址*/
#define OLED_ADDRESS 0x78/*设置哪一个使用I2C*/
#define I2Cx I2C1/*
https://blog.zeruns.tech
*//*引脚初始化*/
void OLED_I2C_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //使能I2C1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟/*STM32F103芯片的硬件I2C: PB6 -- SCL; PB7 -- SDA */GPIO_InitTypeDef GPIO_InitStructure;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; //设置输出模式为开漏输出,需接上拉电阻GPIO_Init(GPIOB, &GPIO_InitStructure);I2C_DeInit(I2Cx); //将外设I2C寄存器重设为缺省值I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //工作模式I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,Tlow/Thigh = 2I2C_InitStructure.I2C_OwnAddress1 = 0x30; //主机的I2C地址,用不到则随便写,无影响I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能应答位I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置地址长度7位I2C_InitStructure.I2C_ClockSpeed = 400000; //I2C传输速度,400K,根据自己所用芯片手册查看支持的速度。 I2C_Init(I2Cx, &I2C_InitStructure);I2C_Cmd(I2Cx, ENABLE);
}void I2C_WriteByte(uint8_t addr,uint8_t data)
{while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//发送起始信号I2C_GenerateSTART(I2Cx, ENABLE);//检测EV5事件while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//发送设备写地址I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);//检测EV6事件while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);//发送要操作设备内部的地址I2C_SendData(I2Cx, addr);//检测EV8_2事件while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));I2C_SendData(I2Cx, data);//发送数据while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//发送停止信号I2C_GenerateSTOP(I2Cx, ENABLE);
}/*** @brief OLED写命令* @param Command 要写入的命令* @retval 无*/
void OLED_WriteCommand(unsigned char Command)//写命令
{I2C_WriteByte(0x00, Command);
}/*** @brief OLED写数据* @param Data 要写入的数据* @retval 无
*/
void OLED_WriteData(unsigned char Data)//写数据
{I2C_WriteByte(0x40, Data);
}/*** @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 start 列开始位置,范围:1~16* @param end 列开始位置,范围:1~16* @retval 无*/
void OLED_Clear_Part(uint8_t Line, uint8_t start, uint8_t end)
{ uint8_t i,Column;for(Column = start; Column <= end; Column++){OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分for (i = 0; i < 8; i++){OLED_WriteData(0x00); //显示上半部分内容}OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分for (i = 0; i < 8; 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 Chinese 要显示的中文字在字库数组中的位置* @retval 无*/
void OLED_ShowWord(uint8_t Line, uint8_t Column, uint8_t Chinese)
{ uint8_t i;OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);for (i = 0; i < 8; i++){OLED_WriteData(OLED_F16x16[Chinese][i]); }OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8 + 8);for (i = 1; i < 8; i++){OLED_WriteData(OLED_F16x16[Chinese][i+8]); }OLED_SetCursor((Line - 1) * 2 +1, (Column - 1) * 8);for (i = 0; i < 8; i++){OLED_WriteData(OLED_F16x16[Chinese][i+16]);}OLED_SetCursor((Line - 1) * 2 +1, (Column - 1) * 8 + 8);for (i = 1; i < 8; i++){OLED_WriteData(OLED_F16x16[Chinese][i+16+8]);}
}/*** @brief OLED显示一串中文字* @param Line 行位置,范围:1~4* @param Column 列位置,范围:1~16* @param Chinese[] 要显示的中文字在字库数组中的位置,数组里放每个字的位置* @param Length 要显示中文的长度,范围:1~8* @retval 无*/
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t *Chinese,uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++){OLED_ShowWord(Line, Column + i*2,Chinese[i]);}
}/*** @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清屏
}
推荐阅读
- 高性价比和便宜的VPS/云服务器推荐: https://www.awsl9527.cn/archives/41.html
- 使用NPS搭建内网穿透服务器,带Web面板:https://www.awsl9527.cn/archives/748.html
- Linux搭建网站教程,建站教程:https://www.awsl9527.cn/archives/1094.html
- 我的世界服务器搭建教程:https://www.awsl9527.cn/archives/tag/minecraft
- 基于STM32和HC-SR04模块实现超声波测距功能:https://blog.zeruns.tech/archives/680.html
- ESP8266开发环境搭建及项目演示:https://blog.zeruns.tech/archives/526.html
STM32读取SHT3x系列温湿度传感器,标准库和HAL库相关推荐
- Nordic 蓝牙芯片上开发 SHT3x系列温湿度传感器
第1部分 SHT31传感器介绍 1.1芯片简介 SHT3X系列是由瑞士Sensirion生产的高精度温湿度传感器,也是Sensirion公司目前主打的温湿度传感器系列.现在网上常见的相关资料调试的基本 ...
- 外设驱动库开发笔记11:SHT3x系列温湿度传感器驱动
在我们的产品中经常会遇到温湿度检测的需求.可以用于检测温湿度的传感器元件也有很多.我们经常使用的SHT各系列数字温湿度传感器来实现应用需求.在这里我们将设计并实现SHT3x系列温湿度传感器的驱动. 1 ...
- 【STM32】标准库与HAL库对照学习教程外设篇--红外避障传感器
[STM32]标准库与HAL库对照学习教程外设篇--红外避障传感器 一.前言 二.准备工作 三.红外避障传感器 1.传感器原理说明 2.传感器特性 四.标准库使用红外传感器 1.实验程序 2.实验效果 ...
- STM32实现光照强度传感器(BH1750)(标准库与HAL库实现)
目录 元器件类型选择 接线表设计 实现过程 标准库实现 HAL库实现 代码下载 元器件类型选择 单片机选择:STM32F103 光照度模块:维特智能出品的 BH1750 模块被企业封装过,将采集的光照 ...
- 【STM32】标准库与HAL库对照学习教程外设篇--超声波测距传感器
[STM32]标准库与HAL库对照学习教程外设篇--超声波测距传感器 一.前言 二.准备工作 三.超声波测距传感器 1.原理说明 2.使用说明 四.标准库使用传感器 1.实验程序 2.实验效果 五.H ...
- 【STM32学习】(30)STM32实现18B20温度采集(标准库和HAL库实现)
单片机型号:STM32F103 源码下载链接:(2条消息) STM32实现18B20温度采集(标准库和HAL库实现)-电信文档类资源-CSDN下载 现要求完成温度采集并在串口或液晶屏上显示.我们这里使 ...
- STM32标准库、HAL库特点与应用
新手在入门STM32的时候,一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配置寄存器进行开发. 对于刚入门的朋友可能没法直观了解这些不同开发发方式彼此之间的区别,本文试图以一种非常直白的方 ...
- 【STM32】标准库与HAL库对照学习教程十三--软件IIC控制AT24C02
[STM32]标准库与HAL库对照学习教程十三--软件IIC控制AT24C02 一.前言 二.准备工作 三.AT24C02(EEPROM)介绍 1.AT24C02简介 2.引脚功能 3.设备地址 四. ...
- STM32实现六轴姿态测量陀螺仪模块JY61P(标准库与HAL库实现)
本模块支持串口采用串口实现数据采集和处理 设备型号选择 目录 设备型号选择 六轴姿态测量陀螺仪模块简介 产品概述 产品特点 引脚说明 模块UART与MCU连接 应用领域 模块与单片机的接线表设计 标准 ...
最新文章
- 自定义Spark Partitioner提升es-hadoop Bulk效率
- Redis数据持久化
- 漫谈MySQL权限安全,威力加强版
- 阿里巴巴1682亿背后的“企业级”高效持续交付
- 晕,我的VBSCRIPT语法还没过关
- 木马藏身于系统进程中
- linux下的网桥介绍
- 《Python数据分析》第二版.第三章.[学习笔记][Jupyter notebook]
- 哪个中年IT男不是一边面对危机,一边咬牙硬抗【转】
- 解决java使用Runtime.exec执行linux复杂命令不成功问题
- 【已解决】 c8812在eclipse上调试打不出log来?求帮助如何解决?!!!
- 接口自动化:淘宝的登录、搜索商品、确认订单、付款流程
- μC/OS-II兼容层——让基于μC/OS-II开发的应用层无感地迁移到RT-Thread操作系统
- PMP-12项目采购管理
- 二维码:MP3音频世界的进化
- 我为什么鼓励你读计算机博士
- 美丽的数据 :数据可视化与信息可视化浅谈
- GNU C++ 智能指针3-- 解析_Sp_counted_base类
- 宽和窄俯卧撑哪个更难_窄距俯卧撑到底该多窄?
- C语言实现BMP图片的放大缩小
热门文章
- php怎么设置表格大小,如何设置excel表中的单元格大小统一
- TCP的FIN/RST Cookie
- 急!!!微信公众号数据迁移后openid无法转换
- ieee14节点 matlab,《IEEE14节点电力网络分析》.doc
- UOJ#310. 【UNR #2】黎明前的巧克力
- 手机拍照打卡活动制作方案,通过拍照不聚集活动,函数参数(Function parameters)是在函数定义中所列的名称。
- 家园防线 | 斐乐的手敲大型庭院物联网灌溉系统(持续更新)
- (NLP)文本预处理
- 深度linux系统wifi信号变弱,在Deepin 20社区版下WiFi网速变慢的处理
- ajax与spry的关系,用Spry框架来简化AJAX