STM32应用开发——BH1750光照传感器详解
STM32应用开发——BH1750光照传感器详解
目录
- STM32应用开发——BH1750光照传感器详解
- 前言
- 1 硬件介绍
- 1.1 BH1750简介
- 1.2 硬件接线
- 2 软件编程
- 2.1 软件原理
- 2.1.1 IIC设备地址
- 2.1.2 IIC读写
- 2.1.3 BH1750指令集
- 2.1.4 BH1750工作流程
- 2.1.5 BH1750测量模式
- 2.2 测试代码
- 2.3 运行测试
- 2.4 常见问题
- 结束语
前言
几年前我发布了一篇关于STM32+BH1750光照传感器的博客,因为大学时期经验欠缺,无论是代码还是博客,都有很多不足之处,也收到了非常多同学的反馈。于是我重新整理了代码和文章,这是一个全新版本。
1 硬件介绍
1.1 BH1750简介
BH1750FVI 是一种用于两线式串行总线接口的数字型光强度传感器集成电路。这种集成电路可以根据收集的光线强度数据来调整液晶或者键盘背景灯的亮度。利用它的高分辨率可以探测较大范围的光强度变化。
注:这个模块只适用于室内环境或者学习使用,户外场景下会超量程。
BH1750的引脚如下:
引脚号 | 名称 | 功能 |
---|---|---|
1 | VCC | 电源正 |
2 | ADDR | 地址端口 |
3 | GND | 电源负 |
4 | SDA | IIC数据线 |
5 | DVI | IIC端口参考电压 |
6 | SCL | IIC时钟线 |
注:上述引脚指的是芯片的引脚,如果用的是某宝买的那种传感器模式,引出来的排针引脚排列是不同的。
参考外围电路如下:
1.2 硬件接线
本文使用STM32F103作为主控MCU,STM32和BH1750的引脚连接如下表:
STM32 | BH1750 | 功能 |
---|---|---|
VCC | VCC | 电源正,3.3V供电 |
GND | GND | 电源负 |
PB6 | SCL | IIC时钟线,必须外接上拉电阻 |
PB7 | SDA | IIC数据线,必须外接上拉电阻 |
GND | ADDR | 地址端口 |
注:BH1750用的是模块,模块上已经有接上拉电阻了,因此STM32这边就不需要再接了。另外,ADDR引脚默认有一个下拉电阻,因此不接地其实也是没关系的。
BH1750模块的电路图如下:
2 软件编程
2.1 软件原理
BH1750是一个IIC设备,通讯协议遵循标准IIC协议。只需要着重关注设备地址,发送命令,读取寄存器即可。
2.1.1 IIC设备地址
所有IIC设备都有一个专属的设备地址,BH1750也不例外,BH1750一共有2个设备地址,可以通过芯片的ADDR引脚切换。
数据手册原文如下:
翻译过来的话,设备地址的配置如下:
ADDR引脚电平 | 7bit地址 | 8bit地址 |
---|---|---|
高电平 | 0x5C | 0xB8 |
低电平 | 0x23 | 0x46 |
注:这里解释一下7bit地址和8bit地址。
IIC设备的地址一般来说都只有7位,也就是7bit地址,BH1750数据手册上面写的也是7bit地址。
但是设备地址并不是单独使用的,在地址后面需要补1个读写位(不懂的先看下IIC通信协议),也就相当于把7bit的地址左移了1位,那么得到的值就是8bit地址。
所以,在IIC进行读写的时候,要用7bit地址还是8bit地址取决于你这个读写函数里面有没有对这个地址进行移位操作。
2.1.2 IIC读写
1、IIC写入流程。
时序解释:先发送设备地址+读写位,再发送指令数据。
题外话:
大多数IIC设备,在发送的这串数据中,往往会把第1个字节的数据作为1个寄存器地址(有些也叫偏移),随后才是真正的数据。这样一来,就可以通过1个字节或多个字节的寄存器地址,来实现数据和命令的扩展。
不过BH1750可能是因为功能比较简单,它没有采用这种方式,读写似乎都在同一个寄存器里面操作。
2、IIC读取流程。
时序解释:先发送设备地址+读写位,随后读取两个数据(其中高位数据在前,低位数据在后)。
题外话:
这个读取的流程跟绝大多数IIC设备的读取流程是一样的。
2.1.3 BH1750指令集
在初始化BH1750时,通过发送1个字节的指令即可配置BH1750的模式。
详细指令集如下表:
2.1.4 BH1750工作流程
根据数据手册的描述,配置流程如下图:
总结一下这个流程:
第1步:给BH1750供电。
第2步:BH1750上电后默认为断电模式(此断电模式不是说芯片没有电,而是芯片没有进入工作模式)。
第3步:通过发送指令,把BH1750配置为通电模式(此时芯片进入工作模式)。
第4步:发送测量指令(测量方式有多种,详细信息看上面2.1.3的指令集)。
第5步:读取测量结果并转换成光照值。
按照上面这个流程走,就可以使BH1750正常工作。
2.1.5 BH1750测量模式
BH1750有6种测量模式,每种模式有各自的特点,详见下表:
测量模式 | 精度 | 测量时间 | 备注 |
---|---|---|---|
一次H分辨率模式 | 1 lx | 120ms | 该模式配置完成后会自动进行一次测量,测量完成后会切换到断电模式 |
一次H分辨率模式2 | 0.5 lx | 120ms | 该模式配置完成后会自动进行一次测量,测量完成后会切换到断电模式 |
一次L分辨率模式 | 4 lx | 16ms | 该模式配置完成后会自动进行一次测量,测量完成后会切换到断电模式 |
连续H分辨率模式 | 1 lx | 120ms | 该模式配置完成后会自动进行连续测量,无需重复配置 |
连续H分辨率模式2 | 0.5 lx | 120ms | 该模式配置完成后会自动进行连续测量,无需重复配置 |
连续L分辨率模式 | 4 lx | 16ms | 该模式配置完成后会自动进行连续测量,无需重复配置 |
总结:
L分辨率模式采集时间短,但精度差,适用于采集光照变化大且变化非常快的场景。
H分辨率模式采集时间长,但精度高,适用于光照变化速度不快的场景。
一次采集模式适用于采集间隔时间长或者需要省电的场景。
连续采集模式适用于对设备节能没有要求的大多数场景。
2.2 测试代码
根据上述原理,编写测试代码,主要的代码清单如下:
文件名 | 代码内容 |
---|---|
iic_software.h | 定义了软件IIC所需的引脚定义和电平设置,方便修改IO或者移植 |
iic_software.c | 软件IIC代码主体 |
iic_hardware.h | 定义了硬件IIC所需的引脚定义和电平设置,方便修改IO或者移植 |
iic_hardware.c | 硬件IIC代码主体 |
bh1750.h | 定义了BH1750的相关信息,如设备地址,命令等 |
bh1750.c | BH1750驱动主体,底层调用软件IIC接口 |
uart.c | 串口主体,用来打印测量过程和结果 |
main.c | 主函数 |
注:
1、除了串口的代码,其他代码全部都在下面贴出来了。串口的教程很多,这里就不细说了。
2、我这里采用软件IIC和硬件IIC两种方式测试,但实际上两种方式只能同时使用其中一种。
3、本文主要介绍如何驱动BH1750,尝试了几种测量模式,结果都由串口打印出来,不需要使用串口的话注释掉即可,如果需要现在在LCD屏或者OLED屏,请自行增加这部分的代码。
测试示例代码如下:
iic_software.h :
注:打开宏USE_SOFTWARE_IIC
,表示使用软件IIC。此时要注释掉iic_hardware.h
的宏USE_HARDWARE_IIC
。
#ifndef __IIC_SOFTWARE_H__
#define __IIC_SOFTWARE_H__#define USE_SOFTWARE_IIC // 使用软件IIC(注:软件IIC和硬件IIC只能同时使用一种)#ifdef USE_SOFTWARE_IIC
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"// IIC SCL引脚配置
#define IIC_SCL_CLOCK RCC_APB2Periph_GPIOB // 时钟
#define IIC_SCL_PORT GPIOB // 端口
#define IIC_SCL_PIN GPIO_Pin_6 // 引脚号
// IIC SCL电平设置
#define SET_IIC_SCL(s) s > 0 ? GPIO_SetBits(IIC_SCL_PORT, IIC_SCL_PIN) : GPIO_ResetBits(IIC_SCL_PORT, IIC_SCL_PIN)// IIC SDA引脚配置
#define IIC_SDA_CLOCK RCC_APB2Periph_GPIOB // 时钟
#define IIC_SDA_PORT GPIOB // 端口
#define IIC_SDA_PIN GPIO_Pin_7 // 引脚号
// IIC SDA电平设置
#define SET_IIC_SDA(s) s > 0 ? GPIO_SetBits(IIC_SDA_PORT, IIC_SDA_PIN) : GPIO_ResetBits(IIC_SDA_PORT, IIC_SDA_PIN)
#define READ_IIC_SDA GPIO_ReadInputDataBit(IIC_SDA_PORT, IIC_SDA_PIN)void iic_init(void);
uint8_t iic_write_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size);
uint8_t iic_receive_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size);
#endif
#endif
iic_software.c :
/*************************************************************************** 文件名 :iic_software.c* 描述 :软件模拟IIC程序
****************************************************************************/
#include "iic_software.h"
#include "delay.h"
#include "stdio.h"#ifdef USE_SOFTWARE_IIC // 使用软件IIC(注:软件IIC和硬件IIC只能同时使用一种)
/* IIC软件模拟时序延时函数 */
void iic_delay(uint16_t us)
{delay_us(us);
}/* IIC起始信号 */
void set_iic_sda_mode(uint8_t mode)
{GPIO_InitTypeDef GPIO_InitStruct;if(mode > 0){// 设置为输出模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏或推挽,外部需要接上拉电阻}else{// 设置为输入模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 浮空或上拉,外部需要接上拉电阻}GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Pin = IIC_SDA_PIN;GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct);
}/* IIC起始信号 */
void iic_start(void)
{SET_IIC_SDA(1);SET_IIC_SCL(1);iic_delay(5);SET_IIC_SDA(0); SET_IIC_SCL(0); iic_delay(5);
}/* IIC停止信号 */
void iic_stop(void)
{SET_IIC_SCL(0);SET_IIC_SDA(0); iic_delay(5); SET_IIC_SCL(1); SET_IIC_SDA(1); iic_delay(5);
}/* IIC发送应答信号 */
uint8_t iic_send_ack(int ack)
{set_iic_sda_mode(1);if(ack == 1){// 发送NACKSET_IIC_SDA(1); }else if(ack == 0){// 发送ACKSET_IIC_SDA(0); }else{return 0; // 入参有误,发送失败} SET_IIC_SCL(1); iic_delay(5); SET_IIC_SCL(0); iic_delay(5); return 1; // 发送成功
}/* IIC接收应答信号 */
uint8_t iic_wait_ack(void)
{uint8_t ack = 0;uint8_t timeout = 5;SET_IIC_SDA(1); set_iic_sda_mode(0); // SDA输入模式SET_IIC_SCL(1); iic_delay(5); while(timeout--) {// 等待从设备发送ACKif(READ_IIC_SDA == 0){// 读到应答信号ack = 1; }else{// 没有读到应答信号,继续等待iic_delay(1);if(timeout == 0){// 等待超时ack = 0; }}} SET_IIC_SCL(0); iic_delay(5); set_iic_sda_mode(1); // SDA输出模式return ack;
}/* IIC总线发送1个字节数据 */
uint8_t iic_send_byte(uint8_t dat)
{uint8_t i;for (i = 0; i < 8; i++){if(0x80 & dat){SET_IIC_SDA(1); }else{SET_IIC_SDA(0); }dat <<= 1;SET_IIC_SCL(1); iic_delay(5); SET_IIC_SCL(0); iic_delay(5);}return iic_wait_ack(); // 返回从设备应答信号
}/* IIC总线接收1个字节数据 */
uint8_t iic_receive_byte(void)
{uint8_t i;uint8_t dat = 0;uint8_t bit;set_iic_sda_mode(0); // SDA输入模式for (i = 0; i < 8; i++){dat <<= 1;SET_IIC_SCL(1);iic_delay(5);if(READ_IIC_SDA == 1){bit = 0X01;}else{bit = 0x00; }dat |= bit; //读数据 SET_IIC_SCL(0);iic_delay(5);} set_iic_sda_mode(1); // SDA输出模式return dat;
}/* IIC发送多个数据 */
uint8_t iic_write_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{uint8_t i;uint8_t result = 0;iic_start(); // 起始信号if(iic_send_byte(addr << 1)) // 发送设备地址(7bit地址){// 收到应答,发送成功for (i = 0; i < buf_size; i++) // 发送数据{if(iic_send_byte(buf[i])){// 收到应答,发送成功result = 1;}else{// 没有收到应答,发送失败result = 0; }}}iic_stop(); // 发送停止信号return result;
}/* IIC接收多个数据 */
uint8_t iic_receive_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{uint8_t i; uint8_t result = 0;iic_start(); // 起始信号if(iic_send_byte((addr << 1) | 1)) // 发送设备地址(7bit地址){for (i = 0; i < buf_size; i++) // 连续读取数据{*buf++ = iic_receive_byte(); if (i == buf_size - 1){iic_send_ack(1); // 最后一个数据需要回NACK}else{ iic_send_ack(0); // 发送ACK}}result = 1;}iic_stop(); // 停止信号return result;
}/* IIC初始化 */
void iic_init(void)
{GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(IIC_SCL_CLOCK, ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN;GPIO_Init(IIC_SCL_PORT, &GPIO_InitStruct); RCC_APB2PeriphClockCmd(IIC_SDA_CLOCK, ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Pin = IIC_SDA_PIN;GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct);
}
#endif
iic_hardware.h :
注:打开宏USE_HARDWARE_IIC
,表示使用硬件IIC。此时要注释掉iic_software.h
的宏USE_SOFTWARE_IIC
。
#ifndef __IIC_HARDWARE_H__
#define __IIC_HARDWARE_H__// #define USE_HARDWARE_IIC // 使用硬件IIC(注:软件IIC和硬件IIC只能同时使用一种)#ifdef USE_HARDWARE_IIC
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
#include "stm32f10x_dma.h"
#include "misc.h"// IIC 端口配置
#define IIC_PORT I2C1
#define IIC_PORT_CLOCK RCC_APB1Periph_I2C1
#define I2C_SPEED 400000 // IIC速率
#define I2C_SLAVE_ADDRESS7 0xA0 // IIC本机地址(无用)
#define IIC_LONG_TIMEOUT 10 * 0x1000 // IIC通讯超时时间// IIC SCL引脚配置
#define IIC_SCL_CLOCK RCC_APB2Periph_GPIOB // 时钟
#define IIC_SCL_PORT GPIOB // 端口
#define IIC_SCL_PIN GPIO_Pin_6 // 引脚号// IIC SDA引脚配置
#define IIC_SDA_CLOCK RCC_APB2Periph_GPIOB // 时钟
#define IIC_SDA_PORT GPIOB // 端口
#define IIC_SDA_PIN GPIO_Pin_7 // 引脚号// IIC DMA设置
#define I2C_DMA DMA1
// #define I2C_DMA_FLAG_TX_TC DMA1_IT_TC6
// #define I2C_DMA_FLAG_TX_GL DMA1_IT_GL6
// #define I2C_DMA_CHANNEL_TX DMA1_Channel6
// #define I2C_DMA_TX_IRQn DMA1_Channel6_IRQn
#define I2C_DMA_FLAG_RX_TC DMA1_IT_TC7
#define I2C_DMA_FLAG_RX_GL DMA1_IT_GL7
#define I2C_DMA_CHANNEL_RX DMA1_Channel7
#define I2C_DMA_RX_IRQn DMA1_Channel7_IRQn
#define I2C_DMA_CLK RCC_AHBPeriph_DMA1
#define I2C_DR_Address ((uint32_t)0x40005410)
#define I2C_DMA_PREPRIO 0 // DMA中断优先级
#define I2C_DMA_SUBPRIO 0 void iic_init(void);
uint8_t iic_write_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size);
uint8_t iic_receive_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size);
#endif
#endif
iic_hardware.c :
/*************************************************************************** 文件名 :iic_hardware.c* 描述 :硬件IIC程序
****************************************************************************/
#include "iic_hardware.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"#ifdef USE_HARDWARE_IIC // 使用软件IIC(注:软件IIC和硬件IIC只能同时使用一种)uint8_t dma_rx_buffer[2];
uint8_t dma_receive_flag = 0;/* IIC发送多个数据 */
uint8_t iic_write_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{ uint32_t timeout;/*!< While the bus is busy */timeout = IIC_LONG_TIMEOUT;while(I2C_GetFlagStatus(IIC_PORT, I2C_FLAG_BUSY)){if((timeout--) == 0) return 0;}/*!< Send START condition */I2C_GenerateSTART(IIC_PORT, ENABLE);/*!< Check EV5 and clear it */timeout = IIC_LONG_TIMEOUT;while(!I2C_CheckEvent(IIC_PORT, I2C_EVENT_MASTER_MODE_SELECT)){if((timeout--) == 0) return 0;}/*!< Send address for write */I2C_Send7bitAddress(IIC_PORT, (addr<<1), I2C_Direction_Transmitter);/*!< Check EV6 and clear it */timeout = IIC_LONG_TIMEOUT;while(!I2C_CheckEvent(IIC_PORT, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){if((timeout--) == 0) return 0;}/*!< Send N byte data */while(buf_size--){I2C_SendData(IIC_PORT, *buf);/*!< Check EV8 and clear it */timeout = IIC_LONG_TIMEOUT; while(!I2C_CheckEvent(IIC_PORT, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((timeout--) == 0) return 0;} }/*!< Send STOP condition */I2C_GenerateSTOP(I2C1, ENABLE);/* If all operations OK, return 1 */return 1;
}/*** @brief This function handles DMA1 Channel 7 interrupt request.* @param None* @retval None*/
void DMA1_Channel7_IRQHandler(void)
{if(DMA_GetITStatus(DMA1_IT_TC7)){DMA_ClearITPendingBit(DMA1_IT_GL7);I2C_AcknowledgeConfig(IIC_PORT, DISABLE);I2C_GenerateSTOP(IIC_PORT, ENABLE);dma_receive_flag = 1; // 已收到数据}
}/* IIC接收多个数据 */
uint8_t iic_receive_bytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{uint32_t timeout;/*!< While the bus is busy */timeout = IIC_LONG_TIMEOUT;while(I2C_GetFlagStatus(IIC_PORT, I2C_FLAG_BUSY)){if((timeout--) == 0) return 0;}/*!< Send START condition */I2C_GenerateSTART(IIC_PORT, ENABLE);/*!< Check EV5 and clear it */timeout = IIC_LONG_TIMEOUT;while(!I2C_CheckEvent(IIC_PORT, I2C_EVENT_MASTER_MODE_SELECT)){if((timeout--) == 0) return 0;}/*!< Send address for read */I2C_Send7bitAddress(IIC_PORT, (addr<<1), I2C_Direction_Receiver);/*!< Test on EV6 and clear it */timeout = IIC_LONG_TIMEOUT;while(!I2C_CheckEvent(IIC_PORT, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){if((timeout--) == 0) return 0;} I2C_AcknowledgeConfig(IIC_PORT, ENABLE);/* Inform the DMA that the next End Of Transfer Signal will be the last one */I2C_DMALastTransferCmd(IIC_PORT, ENABLE); /* Configure the DMA Rx Channel with the buffer address and the buffer size */DMA_Cmd(I2C_DMA_CHANNEL_RX, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel7, buf_size);DMA_Cmd(I2C_DMA_CHANNEL_RX, ENABLE); /* 接收数据 */timeout = IIC_LONG_TIMEOUT;while(!dma_receive_flag){// 等待数据接收完成if((timeout--) == 0) {dma_receive_flag = 0;return 0;}}memcpy(buf, dma_rx_buffer, buf_size);/* If all operations OK, return 1 */return 1;
}/* IIC初始化 */
void iic_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure; DMA_InitTypeDef DMA_InitStructure; I2C_InitTypeDef I2C_InitStructure; /*!< I2C_SCL_GPIO_CLK and I2C_SDA_GPIO_CLK Periph clock enable */RCC_APB2PeriphClockCmd(IIC_SCL_CLOCK | IIC_SDA_CLOCK, ENABLE);/*!< I2C Periph clock enable */RCC_APB1PeriphClockCmd(IIC_PORT_CLOCK, ENABLE);/*!< GPIO configuration */ /*!< Configure I2C pins: SCL */GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);/*!< Configure I2C pins: SDA */GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure); /* Configure and enable I2C DMA RX Channel interrupt */NVIC_InitStructure.NVIC_IRQChannel = I2C_DMA_RX_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = I2C_DMA_PREPRIO;NVIC_InitStructure.NVIC_IRQChannelSubPriority = I2C_DMA_SUBPRIO;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure); /*!< I2C DMA RX channels configuration *//* Enable the DMA clock */RCC_AHBPeriphClockCmd(I2C_DMA_CLK, ENABLE);/* I2C RX DMA Channel configuration */DMA_DeInit(I2C_DMA_CHANNEL_RX);DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C_DR_Address;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&dma_rx_buffer;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 2; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(I2C_DMA_CHANNEL_RX, &DMA_InitStructure); /* Enable the DMA Channels Interrupts */DMA_ITConfig(I2C_DMA_CHANNEL_RX, DMA_IT_TC, ENABLE); /*!< I2C configuration *//* I2C configuration */I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;/* Apply I2C configuration after enabling it */I2C_Init(IIC_PORT, &I2C_InitStructure);/* I2C Peripheral Enable */I2C_Cmd(IIC_PORT, ENABLE);/* Enable the I2C peripheral DMA requests */I2C_DMACmd(IIC_PORT, ENABLE);
}
#endif
bh1750.h :
#ifndef __BH1750_H__
#define __BH1750_H__#include "stm32f10x.h"#define BH1750_ADDRESS_LOW 0x23 // addr low 7bit:0x23 8bit:0x46
#define BH1750_ADDRESS_HIGH 0x5C // addr high 7bit:0x5C 8bit:0xB8#define BH1750_ADDRESS BH1750_ADDRESS_LOW /*bh1750 registers define */
#define BH1750_POWER_ON 0x01 // power on
#define BH1750_POWER_DOWN 0x00 // power down
#define BH1750_RESET 0x07 // reset
#define BH1750_CON_H_RES_MODE 0x10 // Continuously H-Resolution Mode
#define BH1750_CON_H_RES_MODE2 0x11 // Continuously H-Resolution Mode2
#define BH1750_CON_L_RES_MODE 0x13 // Continuously L-Resolution Mode
#define BH1750_ONE_H_RES_MODE 0x20 // One Time H-Resolution Mode
#define BH1750_ONE_H_RES_MODE2 0x21 // One Time H-Resolution Mode2
#define BH1750_ONE_L_RES_MODE 0x23 // One Time L-Resolution Modeuint8_t bh1750_init(void);
void bh1750_read_example(void);#endif
bh1750.c :
/*************************************************************************** 文件名 :bh1750.c* 描述 :光强传感模块
****************************************************************************/
#include "bh1750.h"
#include "delay.h"
#include "iic_hardware.h"
#include "iic_software.h"#define LOG_ENABLE#ifdef LOG_ENABLE#include "stdio.h"#define LOG printf
#else#define LOG(format, ...)
#endifuint8_t current_mode; // BH1750的测量模式
float current_light; // BH1750的测量光照值// BH1750延时函数
void bh1750_delay(uint16_t ms)
{// ms级延时,BH1750每次测量都需要时间,该函数用于等待测量结果delay_ms(ms);
}// 写命令
uint8_t bh1750_write_cmd(uint8_t cmd)
{return iic_write_bytes(BH1750_ADDRESS, &cmd, 1);
}// 写寄存器
uint8_t bh1750_read_regs(uint8_t *buf, uint8_t buf_size)
{return iic_receive_bytes(BH1750_ADDRESS, buf, buf_size);
}// 复位
uint8_t bh1750_reset(void)
{return bh1750_write_cmd(BH1750_RESET);
}
// 打开电源
uint8_t bh1750_power_on(void)
{return bh1750_write_cmd(BH1750_POWER_ON);
}// 关闭电源
uint8_t bh1750_power_down(void)
{return bh1750_write_cmd(BH1750_POWER_DOWN);
}// 设置测量模式
uint8_t bh1750_set_measure_mode(uint8_t mode)
{uint8_t result = 0;if(bh1750_write_cmd(mode)){result = 1;}return result;
}// 单次读取光照值
uint8_t bh1750_single_read_light(uint8_t mode, float *light)
{// 单次测量模式在测量后会自动设置为断电模式 uint8_t temp[2];uint8_t result = 0;if(mode != BH1750_ONE_H_RES_MODE && mode != BH1750_ONE_H_RES_MODE2 && mode != BH1750_ONE_L_RES_MODE){LOG("bh1750 single read measure mode error! mode:0x%02x\n", mode);return result;}if(bh1750_set_measure_mode(mode)) // 每次采集前先设置模式{LOG("bh1750 set measure mode success! mode:0x%02x\n", mode);current_mode = mode;switch (mode){case BH1750_ONE_H_RES_MODE: // 单次H分辨率模式(精度1lx,测量时间120ms)bh1750_delay(120); // 等待采集完成break;case BH1750_ONE_H_RES_MODE2: // 单次H分辨率模式(精度0.5lx,测量时间120ms)bh1750_delay(120); // 等待采集完成break;case BH1750_ONE_L_RES_MODE: // 单次L分辨率模式(精度4lx,测量时间16ms)bh1750_delay(16); // 等待采集完成break;default:break;}if(bh1750_read_regs(temp, 2)) // 读取测量结果{*light = ((float)((temp[0] << 8) + temp[1]) / 1.2); // 换算成光照值result = 1;}else{LOG("bh1750 read light failed!");}}else{LOG("bh1750 set measure mode failed! mode:0x%02x\n", mode);return result;}return result;
}// 连续读取光照值
uint8_t bh1750_continuous_read_light(uint8_t mode, float *light)
{ uint8_t temp[2];uint8_t result = 0;if(mode != BH1750_CON_H_RES_MODE && mode != BH1750_CON_H_RES_MODE2 && mode != BH1750_CON_L_RES_MODE){LOG("bh1750 continuous read measure mode error! mode:0x%02x\n", mode);return result;}if(mode != current_mode){// 要使用的测量模式和BH1750当前的模式不同,则配置成相同模式if(bh1750_set_measure_mode(mode)){LOG("bh1750 set measure mode success! mode:0x%02x\n", mode);current_mode = mode;}else{// 模式设置失败LOG("bh1750 set measure mode failed! mode:0x%02x\n", mode);return result;}switch (mode){case BH1750_CON_H_RES_MODE: // 连续H分辨率模式(精度1lx,测量时间120ms)bh1750_delay(120); // 等待采集完成break;case BH1750_CON_H_RES_MODE2: // 连续H分辨率模式(精度0.5lx,测量时间120ms)bh1750_delay(120); // 等待采集完成break;case BH1750_CON_L_RES_MODE: // 连续L分辨率模式(精度4lx,测量时间16ms)bh1750_delay(16); // 等待采集完成break;default:break;}}if(bh1750_read_regs(temp, 2)) // 读取测量结果{*light = ((float)((temp[0] << 8) + temp[1]) / 1.2); // 换算成光照值result = 1;}else{LOG("bh1750 read light failed!");}return result;
}// BH1750初始化
uint8_t bh1750_init(void)
{uint8_t result = 0;iic_init(); // IIC初始化result = bh1750_power_on(); // 打开BH1750电源current_mode = 0;return result;
}// BH1750读取例程
void bh1750_read_example(void)
{
#if 1// 单次采集模式if(bh1750_single_read_light(BH1750_ONE_H_RES_MODE2, ¤t_light)){LOG("bh1750 single read light: %0.1f\n", current_light);}bh1750_delay(100);
#else// 连续采集模式if(bh1750_continuous_read_light(BH1750_CON_H_RES_MODE2, ¤t_light)){LOG("bh1750 continuous read light: %0.1f\n", current_light);}bh1750_delay(120); // 注:因为连续读取函数里面没有做延迟等待处理,因此这里的等待时间如果小于测量时间,那么读取到的值有可能是上一轮测量的值
#endif
}
main.c :
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "bh1750.h"int main(void)
{ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);delay_init(); uart_init(115200); bh1750_init();while(1){bh1750_read_example();}
}
2.3 运行测试
通过逻辑分析仪抓取IIC通讯的波形,分别测试了一次H分辨率模式2
和连续H分辨率模式2
,结果如下:
1、一次H分辨率模式2时序测量
初始化设备:
先发送通电命令(0x01),再设置为单次 H 分辨率模式 2(0x21)。
读取光照值(设备地址为0x23):
串口打印结果:
2、连续H分辨率模式2时序测量
初始化设备:
先发送通电命令(0x01),再设置为连续 H 分辨率模式 2(0x11)。
读取光照值(设备地址为0x23):
串口打印结果:
3、其他测量
1、测量软件IIC时钟周期:
软件模拟IIC的速率约75kHz。
查看数据手册,BH7150对CLK时钟要求是小于等于400kHz,而IIC设备常用的速率是100k,200k和400k,速率慢点会更稳定,但通信时间会加长,因此最好根据实际情况调整。需要调快的话可以通过减少上面模拟IIC收发函数的CLK延时时间来实现。
2、测量硬件IIC时钟周期:
硬件IIC速率设置的是400kHz,实测很稳定。
测试结果:IIC波形没有异常,串口结果没有异常,BH1750工作一切正常。
2.4 常见问题
从我发布第一篇关于BH1750的文章以来,收到了非常多的私信和评论,也收集了不少问题,总结下来基本上都是IIC通信问题。
就拿我上面的测试用例来说,如果出现以下log,就说明STM32和BH1750的通信是失败的。(举个例子,不接入BH1750,让MCU自己跑,就会出现以下log,因为此时STM32就是访问不到从设备,正确连接设备以后,这个log就不会出现了)
排查方向:
1、检查STM32和BH1750各自的电源,接线是否正确,供电电压是否正常,可以借助万用表测量。
2、检查STM32使用的IIC的引脚和BH1750的IIC引脚是否一致且正确连接。
3、检查IIC总线上面是否有外部上拉电阻,阻值是否合理(一般2-10kΩ),如果用的是模块的话,可以看下模块的原理图,确认一下是否有上拉电阻。
4、检查BH1750的设备地址和代码配置的是否一致。
5、检查MCU和BH1750之间的接线是否正确且导通(之前有遇到过杜邦线内部断开的情况),可以借助万用表测量。
6、确认MCU和BH1750是否有损坏(之前有同学说BH1750模块被焊坏了)。
7、建议先用本文的测试用例跑一遍,因为这个代码经过测试,至少能保证软件的正确性,以此来排查硬件问题。有些同学是在我这移植了部分代码,或者在这基础上增加了其他代码,导致出现了bug。
8、如果以上方法都没用,就需要借助万用表或者逻辑分析仪,实际去抓取一下IIC通信的波形,首先要确认的是STM32是否发送了设备地址,BH1750是否有应答,如果没有,要么接线异常,要么地址不对,要么就是传感器坏了,大概率都是这些问题。
总结:在软件经过测试并保证正常运行的情况下,如果出现IIC通信异常问题(如上述log),归根结底还是硬件问题。
结束语
关于STM32如何驱动BH1750的讲解就到这里,如果还有什么问题,欢迎在评论区留言。
源码下载:
链接:https://pan.baidu.com/s/1aX2bYBAOgnkBlL2lzmGYjg
提取码:1234
如果这篇文章能够帮到你,就…你懂的。
相关文章:
STM32应用开发——BH1750光照传感器详解
STM32应用开发——BH1750光照传感器详解 目录 STM32应用开发——BH1750光照传感器详解前言1 硬件介绍1.1 BH1750简介1.2 硬件接线 2 软件编程2.1 软件原理2.1.1 IIC设备地址2.1.2 IIC读写2.1.3 BH1750指令集2.1.4 BH1750工作流程2.1.5 BH1750测量模式 2.2 测试代码2.3 运行测试…...
java jar包加密 jar-protect
介绍 java 本身是开放性极强的语言,代码也容易被反编译,没有语言层面的一些常规保护机制,jar包很容易被反编译和破解。 受classfinal(已停止维护)设计启发,针对springboot日常项目开发,重新编写安全可靠的jar包加壳加密技术,用于保护软件版权。 使用说…...
NMEA/观测文件/导航电文
NMEA-0183 NMEA-0183是美国国家海洋电子协会为海用电子设备制定的标准格式。它包含了定位时间,纬度,经度,高度,定位所用的卫星数,DOP,差分状态和校正时段等很多信息。 参考:GPS NMEA数据包解析…...
HTTPS的工作原理深入解析
在当今互联网时代,网络安全已经成为了一个备受关注的话题。随着越来越多的个人隐私和商业数据被传输在网络中,如何确保这些数据在传输过程中的安全性成为了每个网络开发者和用户关注的核心问题之一。而HTTPS(HyperText Transfer Protocol Sec…...
pandas.core.frame.DataFrame怎么进行对象内容的读写
在 Python 中,pandas.core.frame.DataFrame 是 Pandas 数据库的核心数据结构,可以方便地读取和操作表格数据。以下是几种常见的读取内容的方法: 读取特定列 通过列名获取数据。 # 假设 df 是一个 DataFrame data df["列名"] # …...
OFCA-OpenHarmony人才认证题库答案
单选题 1.[单选题] 位于后台的应用,启动组件需校验的权限是: A: ohos.permission.DISTRIBUTED_DATASYNC B: ohos.permission.START_ABILITIES_FROM_BACKGROUND C: ohos.permission.ABILITY_BACKGROUND_COMMUNICATION D: ohos.permission.START_INVISIBLE_ABIL…...
若依微服务如何获取用户登录信息
文章目录 1、需求提出2、应用场景3、解决思路4、注意事项5、完整代码第一步:后端获取当前用户信息第二步:前端获取当前用户信息 6、运行结果后端测试:前端展示: 总结 1、需求提出 在微服务架构中,获取当前用户的登录信…...
题目 2778: 判断数正负
题目 2778: 判断数正负 时间限制: 2s 内存限制: 192MB 提交: 12161 解决: 6681 题目描述 给定一个整数N,判断其正负。 输入格式 一个整数N(-109 < N < 109) 输出格式 如果N > 0, 输出positive; 如果N 0, 输出zero; 如果N < 0, 输…...
【Hexo】博客自动生成AI摘要
工具介绍 如何让博客支持AI摘要,使用TianliGPT自动生成文章的AI摘要 摘要AI-文章摘要生成工具 文章摘要是一个专业的文字摘要生成工具,你可以将需要提取摘要的文本内容发送给TianliGPT,稍等一会他就可以给你发送一个基于这段文本内容的摘要。…...
vue3-count-to实现数字动态增长效果
vue3-count-to 是一个用于 Vue 3的数字计数动画库,常用于在页面上实现数字的动态增长效果,类似于从某个起始值渐变到目标值的效果。它可以用来显示各种数字、统计数据或展示动画效果。 1 安装 vue3-count-to 首先,你需要安装 vue3-count-to …...
第一课【输入输出】(题解)
1.向世界问好 题目描述 编程输出以下内容: Hello World! Im a C program. 输入格式 本题无输入。 输出格式 请按照样例输出,注意大小写、空格、感叹号,句号,单引号都必须使用英文输入法里的符号。 样例输入/输出 输入数据 1 本题无…...
边缘AI和智能音频专家XMOS全球首家增值经销商(VAR)落地中国
强强合作——XMOS与飞腾云达成全球首家增值经销协议以用智能音频技术和产品服务全球厂商和消费者 中国深圳,2024年12月——全球领先的软件定义系统级芯片(SoC)开发商XMOS宣布:公司已与飞腾云科技达成增值分销协议,授权…...
实战 | 某院校小程序记录
视频教程在我主页简介里 目录: 前言: 渗透思路 1.绕过前端 2.信息泄露 3.爆破用户账号密码 4.信息泄露2 结束 前言: 遇到一个学校小程序的站点,只在前端登录口做了校验,后端没有任何校验,奇葩弱口令离…...
正则表达式——参考视频B站《奇乐编程学院》
智能指针 一、背景🎈1.1. 模式匹配🎈1.2. 文本替换🎈1.3. 数据验证🎈1.4. 信息提取🎈1.5. 拆分字符串🎈1.6. 高级搜索功能 二、原料2.1 参考视频2.2 验证网址 三、用法3.1 限定符3.1.1 ?3.1.2 *3.1.3 3.1.…...
【Vue + Print.js】前端打印, 自定义字体大小, 自定义样式, 封装共享样式
在前端开发中,打印功能是一个常见的需求,尤其是在生成报表、打印用户资料或者导出文档时,通常需要通过前端代码进行打印。Print.js 是一个非常流行的 JavaScript 库,它提供了简单而强大的打印功能,允许你灵活控制打印内…...
python模拟练习第一期
问题一 如果一个数 p 是个质数,同时又是整数 a的约数,则 p 称为 a的一个质因数。 请问 2024 有多少个质因数? 步骤 1: 分解 2024 首先,2024 是偶数,说明可以被 2 整除。我们从 2 开始进行除法分解: 202…...
Java-25 深入浅出 Spring - 实现简易Ioc-01 Servlet介绍 基本代码编写
点一下关注吧!!!非常感谢!!持续更新!!! 大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了: MyBatisÿ…...
华硕奥创软件在线安装和离线安装方法
华硕奥创软件在线安装和离线安装方法 1. 华硕奥创软件介绍2. 华硕奥创软件在线安装2.1 第一种2.2 第二种 3. 华硕奥创软件离线安装3.1 概述3.2 华硕奥创软件离线包下载方式 4. 卸载华硕奥创软件4.1 概述4.2 华硕奥创卸载软件下载与使用方式 结束语 1. 华硕奥创软件介绍 华硕奥…...
AI监控赋能健身馆与游泳馆全方位守护,提升安全效率
一、AI视频监控技术的崛起 随着人工智能技术的不断发展,AI视频监控正成为各行业保障安全、提升效率的关键工具。相比传统监控系统,AI技术赋予监控系统实时分析、智能识别和精准预警的能力,让“被动监视”转变为“主动防控”。 二、AI监控应用…...
SQL Server数据库还原差异备份
适用范围:SQL Server 本主题介绍如何使用 SQL Server Management Studio 或 Transact-SQL 在 SQL Server 中还原差异数据库备份。 限制和局限 不允许在显式或隐式事务中使用 RESTORE。 无法在早期版本的 SQL Server 中还原较新版本的 SQL Server创建的备份。 在…...
Python 给 Excel 写入数据的四种方法
Python 在数据处理领域应用广泛,其中与 Excel 文件的交互是常见需求之一。 本文将介绍四种使用 Python 给 Excel 文件写入数据的方法,并结合生活中的例子进行解释,帮助新手小白快速上手。 1. 使用 openpyxl 库 openpyxl 是一个用于读写 Exc…...
实验14 RNN的记忆能力和梯度爆炸实验
一 循环神经网络的记忆能力 1.数据集构建 创建了一个DigitSumDataset 类,包括初始化函数init、数据生成函数 generate_data、数据加载函数 load_data、__len__ 方法、__getitem__ 方法。 init函数:接受的参数是data_path( 存放数据集的目录…...
【电子通识】电流倒灌为什么需要注意?
电流倒灌是一个很常见的问题,以“IO电流倒灌”为关键词在百度上进行搜索,可以找到很多相关案例。 电流倒灌问题在5V电平的单片机时代几乎不会发生,主要是因为5V单片的IO耐压值高,单片机内部结构对IO保护设计很好。 到了3.3V单片机时代,这类问题有一定的偶发性,但…...
Elasticsearch 集群部署
Elasticsearch 是一个分布式的搜索和分析引擎,广泛应用于日志分析、全文搜索、实时数据分析等场景。它以其高性能、高可用性和易用性而著称。本文档将引导您完成一个基本的 Elasticsearch 集群配置,包括节点间的通信、客户端访问、安全设置等关键步骤。我…...
Windows系统VSCode 搭建ESP-IDF环境
VS Code,安装ESP-IDF插件 快捷键CTRLSHIFTP,弹出显示所有命令的窗口,选择ESP-IDF的欢迎 使用第一个选项,要选择一个ESP-IDF版本,选最新的就行 点击Install,等待下载 提示安装成功,如果过程中出现python已存…...
在centos 7.9上面安装mingw交叉编译工具
1.说明 为了在centos上面编译windows的程序,需要安装mingw工具,mingw工具是可以编译windows程序的一些工具链,使用方式和linux一致 2.下载脚本 使用脚本方式编译,github的脚本位置:https://github.com/Zeranoe/ming…...
【Java笔记】LinkedList 底层结构
一、LinkedList 的全面说明 LinkedList底层实现了双向链表和双端队列特点可以添加任意元素(元素可以重复),包括null线程不安全,没有实现同步 二、LinkedList 的底层操作机制 三、LinkedList的增删改查案例 public class LinkedListCRUD { public stati…...
【深入理解java中的设计模式】
深入理解java中的设计模式 设计模式是软件工程中的最佳实践,它们提供了解决特定问题的模板或蓝图。在Java中,设计模式通常被分为三大类:创建型模式、结构型模式和行为型模式。 创建型模式 单例模式 (Singleton Pattern) 描述: 保证一个类…...
详解下c语言下的多维数组和指针数组
在实际c语言编程中,三维及以上数组我们使用的很少,二维数组我们使用得较多。说到数组,又不得关联到指针,因为他们两者的联系太紧密了。今天我们就详细介绍下c语言下的多维数组(主要是介绍二维数组)和指针。 一、二维数组 1.1&am…...
如何使用 Python 发送 HTTP 请求?
在Python中发送HTTP请求最常用的库是requests,它提供了简单易用的API来发送各种类型的HTTP请求。 除此之外,还有标准库中的http.client(以前叫做httplib)和urllib,但它们相对更底层,代码量较大,…...
活动预告 |【Part2】Microsoft Azure 在线技术公开课:基础知识
课程介绍 参加“Azure 在线技术公开课:基础知识”活动,培养有助于创造新的技术可能性的技能并探索基础云概念。参加我们举办的本次免费培训活动,扩充自身的云模型和云服务类型知识。你还可以查看以计算、网络和存储为核心的 Azure 服务。 课…...
神经网络基础-激活函数
文章目录 1. 什么是激活函数2. sigmoid 激活函数3. tanh 激活函数4. ReLU 激活函数5. SoftMax 激活函数6. 其他常见的激活函数7. 激活函数的选择方法 1. 什么是激活函数 激活函数用于对每层的输出数据进行变换, 进而为整个网络注入了非线性因素。此时, 神经网络就可以拟合各种…...
Debedium如何忽略Oracle的purge命令
报错 截至目前3.0版本,Debezium的Oracle Connector并不支持purge table这个指令。 所以,在使用Debezium解析Oracle变更的时候,如果在源端执行了类似 purge table "$BIN… 的语句,就会导致Debezium罢工,日志里显…...
基于 webRTC Vue 的局域网 文件传输工具
文件传输工具,匿名加密,只需访问网页,即可连接到其他设备,基于 webRTC 和 Vue.js coturn TURN 服务器 docker pull coturn/coturn docker run -d --networkhost \-v $(pwd)/my.conf:/etc/coturn/turnserver.conf \coturn/coturn…...
opencv Canny边缘检测
canny阈值越高,检测到的边缘数量越少 # 导入OpenCV库,用于图像处理 import cv2 import numpy as np # 从matplotlib库中导入pyplot模块,用于绘制图像 from matplotlib import pyplot as plt # 创建一个名为window的窗口,窗口大小自…...
数仓高频面试 | 数仓为什么要分层
大家好,我是大D呀。 关于数仓分层,在面试过程中几乎是必问的。不过,面试官一般也不会直接考你数仓为什么要分层,而是在你介绍项目时,可能会换一种形式来穿插着问,比如数据链路为什么要这样设计,…...
Coconut:探索大语言模型的连续思维链推理能力
目录 简介: 什么是Coconut? 为什么我们需要Coconut? Coconut如何工作? 实验结果怎么样? Coconut的优势: 结论: 简介: 你有没有想过,计算机是如何像人类一样思考问…...
2024年安徽省职业院校技能大赛水利工程BIM建模与应用
2024年安徽省职业院校技能大赛 赛 项 规 程 赛项名称: 水利工程BIM建模与应用 赛项组别: 高职组 承办单位: 安徽水利水电职业技术学院 目录 一、赛项名称… 1 二、竞赛目标… 1 三、竞赛内容… 2 四、竞赛方式… 3 五、竞赛流程… 4 七、技术…...
vue常用命令汇总
nvm 一个nodejs版本管理工具,解决node.js各种版本存在不兼容现象可以通过它可以安装和切换不同版本的node.js。 npm 可以管理 nodejs 的第三方插件。 vue-cli 是Vue提供的一个官方cli,专门为单页面应用快速搭建繁杂的脚手架。 nginx 是一个高性能的HTTP和反向代理we…...
ios上架构建版本没苹果电脑怎么上传
在app store上架的时候,遇到下图的问题: 点击蓝色加号的时候,并没有构建版本可以选择 从图中可以看出,它给我们推荐了很多上传工具,比如xcode、transporter或命令行工具之类的,但是这些工具都是只能在苹果…...
某名校考研自命题C++程序设计——近10年真题汇总(上)
本帖更新一些某校的编程真题,总体来说不难,考察的都是基本功,92高校大一期末的难度,不过有些细节颇为繁琐,各位还是需要一定程度上注意的~ 目录 一.分数求和 二.大小写字母转换 三.判断当年天序 四.交替合并字符串…...
【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?
在人工智能领域,我们一直在追求让机器像人类一样思考。然而,即使是最先进的AI,也常常被诟病缺乏“常识”,难以理解复杂问题,更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方,…...
️️️ 避坑指南:如何修复国密gmssl 库填充问题并提炼优秀加密实践20241212
🛡️ 避坑指南:如何修复国密gmssl 库填充问题并提炼优秀加密实践 ✨ 引言 在当下的数据安全环境中,SM4作为中国国家密码算法的代表性选择,被广泛应用于金融、通信和政府领域。然而,在实际开发中,即便是开…...
鸿蒙ArkTS如何实现数据可视化:饼状图/柱状图/折线图
鸿蒙数据可视化系列 在我们鸿蒙APP开发中,经常需要使用到一些图表的开发,本文主要介绍使用 McCharts 框架绘制柱状图的方法 配套指导视频地址:鸿蒙数据可视化系列-McCharts使用介绍_哔哩哔哩_bilibili 1. 第三方库介绍 1.1. McCharts Mc…...
Win10环境vscode+latex+中文快速配置
安装vscodelatex workshop 配置: {"liveServer.settings.donotVerifyTags": true,"liveServer.settings.donotShowInfoMsg": true,"explorer.confirmDelete": false,"files.autoSave": "afterDelay","exp…...
Android14 AOSP支持短按关机
修改frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java diff --git a/base/services/core/java/com/android/server/policy/PhoneWindowManager.java b/base/services/core/java/com/android/server/policy/PhoneWindowManager.java in…...
康耐视智能相机(Insight)通过ModbusTCP发送字符串到倍福(BECKHOFF)PLC中
文章目录 1.背景2.分析3.实现3.1.PLC的ModbusTCP_Server3.1.1.安装TF6250-Modbus-TCP3.1.2.PLC设置 3.2.智能相机的ModbusTCP_Client3.2.1.了解ModbusTCP的协议3.2.2.根据协议写代码3.2.2.1.纯函数代码3.2.2.2.脚本代码 3.2.3.非脚本处理时的代码逻辑图3.2.4.关于代码的问题及解…...
OpenCV 图像变换与处理实战
OpenCV快速通关 第一章:OpenCV 简介与环境搭建 第二章:OpenCV 图像基本操作 第三章:OpenCV 图像变换与处理实战 OpenCV 图像变换与处理实战 OpenCV快速通关OpenCV 图像变换与处理实战一、OpenCV 基础与图像处理概览二、图像变换理论精析三、…...
meta llama 大模型一个基础语言模型的集合
LLaMA 是一个基础语言模型的集合,参数范围从 7B 到 65B。我们在数万亿个 Token 上训练我们的模型,并表明可以专门使用公开可用的数据集来训练最先进的模型,而无需诉诸专有的和无法访问的数据集。特别是,LLaMA-13B 在大多数基准测试…...
【MySQL — 数据库基础】深入理解数据库服务与数据库关系、MySQL连接创建、客户端工具及架构解析
目录 1. 数据库服务&数据库&表之间的关系 1.1 复习 my.ini 1.2 MYSQL服务基于mysqld启动而启动 1.3 数据库服务的具体含义 1.4 数据库服务&数据库&表之间的关系 2. 客户端工具 2.1 客户端连接MySQL服务器 2.2 客…...