外观
六、温湿度获取
1 电路分析
桌面温湿度仪上使用一个SHT40温湿度传感器模块用于检测温湿度,SHT40采用IIC进行通信,模块上R8、R9是IIC通信的上拉电阻,起到“线与”的作用,支持挂载多个设备。
![]() | ![]() |
---|
1.1 IIC介绍
IIC是一种两线式双向同步串行总线协议,双向的意思是指通信双方均可发送与接受数据;同步是指通信双方具有相同的时钟脉冲(SCL线)。
- 在IIC中把设备分为主设备与从设备,一般来说,谁控制时钟线谁就是主设备;
- IIC设备均有一个器件地址,在多个设备通信时,根据器件地址来进行辨别;
- IIC是半双工通信,同一时间内仅支持单向通信(只有一根数据线);
- 单片机的IIC引脚通常设置为开漏输出,通过外部的上拉电阻来输出高电平,这样做的好处是防止多个设备通信时,信号混乱。
在整个IIC通信过程中,主要包含以下几个过程;
- 主机开始时序;
- 主机发送地址时序;
- 主机等待从机应答时序;
- 主机发送读/写数据时序;
- 主机等待从机应答时序;
- 停止时序;
C
//SCL为高电平期间,SDA从高电平往低电平进行跳变
//延时时间根据期间不同需要进行对应的改变
void iic_start(void)
{
IIC_SDA(1);
IIC_SCL(1);
iic_delay();
IIC_SDA(0);
iic_delay();
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
C
//SCL为高电平期间,SDA从低电平往高电平跳变
void iic_stop(void)
{
IIC_SDA(0);
IIC_SCL(1);
iic_delay();
IIC_SDA(1);
iic_delay();
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
C
//有应答返回0,没有应答返回1
uint8_t iic_wait_ack(void)
{
IIC_SDA(1); //主机释放SDA总线
iic_delay();
IIC_SCL(1); //拉高时钟线,等待从机返回ACK
iic_delay();
if(IIC_READ_SDA() == 1) //如果数据线没有变化
{
iic_stop();
return 1;
}
IIC_SCL(0); //SCL低电平表示结束ACK检查
iic_delay();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
C
void iic_ack(void)
{
IIC_SCL(0); //先拉低时钟线
iic_delay();
IIC_SDA(0); //数据线为低电平,表示应答
iic_delay();
IIC_SCL(1);
iic_delay();
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
C
void iic_nack(void)
{
IIC_SCL(0); //先拉低时钟线
iic_delay();
IIC_SDA(1); //数据线为高电平,表示非应答
iic_delay();
IIC_SCL(1);
iic_delay();
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
C
void iic_send_byte(uint8_t data)
{
uint8_t i = 0;
for(i =0;i<8;i++)
{
IIC_SDA((data&0x80)>>7);
iic_delay();
IIC_SCL(1);
iic_delay();
IIC_SCL(0);
data=data<<1;
}
IIC_SDA(1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
C
uint8_t iic_read_byte(uint8_t ack)
{
uint8_t receive = 0;
uint8_t i = 0;
for(i=0;i<8;i++)
{
receive = receive<<1;
IIC_SCL(1);
iic_delay();
if(IIC_READ_SDA()==1)
{
receive++;
}
IIC_SCL(0);
iic_delay();
}
if(ack){
iic_ack();
}
else{
iic_nack();
}
return receive;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.2 SHT40IIC介绍
找到并打开SHT40的相关数据手册,重点查看IIC地址与数据传输协议。由数据手册可知,0x44是IIC地址;当地址最低位是0,表示读数据,最低位是1,表示写数据; 当发送0xFD指令时,代表高精度测量温湿度;通过对数据进行公式计算可以得出温湿度; SHT40温湿度传感器数据手册
2 代码编写
2.1 创建工程
2.2 设置参数
根据原理图,设置晶振引脚、下载引脚、SHT40-IIC引脚、数码管引脚的参数;
注意打开I2C功能,否则不会生成实际IIC代码,相关设置基本不需要改动;
将SN74HC595引脚设置为推挽输出、快速模式,同时依据原理图对其进行重命名,方便后续代码编写;
配置时钟树,这里主晶振没有使用外部而是内部晶振,外部低速晶振暂时没有使用,先不需要配置;
最后填写输出工程的名称,选择工程输出路径以及适配的IDE;
生成相关文件的.c与.h文件;
选择HAL库;
创建工程,随后打开,这个Cubemx软件就可以关闭了;
2.3 编译配置过程
正常应该是0错误0警告;
点击魔术棒,选择debug,选择合适的下载器,并配置下载器选项;
2.4编写测试代码
在gpio.c文件的用户区编写测试代码,SN74HC595的引脚初始化已经生成好了,这里将之前编写好的数码管显示驱动移植过来,这个步骤我就不重复介绍了;
在工程中,增多了一个i2c文件,这里存放有相关的初始化代码;重点是i2c的句柄变量,这个后续在进行i2c协议发送和接收时都会使用,这个句柄在i2c.h头文件中进行了声明,后续我们直接使用即可。
这里直接在主函数中编写相关的SHT40驱动代码;根据前面SHT40相关的介绍,这里我们直接调用HAL库的主机IIC发送函数即可;
主机发送函数调用参数说明(接收函数基本一致);
- *hi2c------------IIC句柄;
- DevAddress----设备地址,8位数据,在发送内会自动在末尾进行填充;
- *pData----------待发送的数据;
- Size-------------数据长度;
- Timeout--------响应超时时间;
在主函数中对温湿度进行读取,同时放大十倍后取出每一个数据在数码管上进行显示。注意SHT40地址,默认是7位,发送是8位,需要进行左移,然后加入发送与读取指令在最后一位,虽然HAL库代码中已经给我们自动添加了,这里我们手动也填充一下,防止后续移植出问题。
在循环中不要加入延时操作,因为数码管是采用留影的效果进行显示,若时间较长显示效果就变差了。
C
I2C_HandleTypeDef hi2c1;
1
C
HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)SHT40_Write, (uint8_t *)writeData, 1, HAL_MAX_DELAY);
HAL_Delay(10);
HAL_I2C_Master_Receive(&hi2c1, (uint16_t)SHT40_Read, (uint8_t *)readData, 6, HAL_MAX_DELAY);
Temperature = (1.0 * 175 * (readData[0] * 256 + readData[1])) / 65535.0 - 45;
Humidity = (1.0 * 125 * (readData[3] * 256 + readData[4])) / 65535.0 - 6.0;
1
2
3
4
5
6
2
3
4
5
6
C
#define SHT40_Write (0x44<<1) //写入地址
#define SHT40_Read ((0x44<<1)+1) //读出地址
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t num = 0;
uint8_t writeData[1] = {0xFD};
uint8_t readData[6] = {0};
uint16_t Temp = 0,Humi = 0;
double Temperature = 0;
double Humidity = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
HAL_DeInit();
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
//使数码管全部熄灭
HAL_Delay(100);
SN74HC595_Send_Data(SN_DIG,0x00);
SN74HC595_Send_Data(SN_LED1,0x00);
SN74HC595_Send_Data(SN_LED2,0x00);
HAL_Delay(100);
HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)SHT40_Write, (uint8_t *)writeData, 1, HAL_MAX_DELAY);
HAL_Delay(10);
HAL_I2C_Master_Receive(&hi2c1, (uint16_t)SHT40_Read, (uint8_t *)readData, 6, HAL_MAX_DELAY);
Temperature = (1.0 * 175 * (readData[0] * 256 + readData[1])) / 65535.0 - 45;
Humidity = (1.0 * 125 * (readData[3] * 256 + readData[4])) / 65535.0 - 6.0;
Temp = (uint16_t)(Temperature * 10);
Humi = (uint16_t)(Humidity * 10);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
num++;
if(num < 100)
{
ShowNum(1,1,Temp/100);
ShowNum(1,2,Temp/ 10 % 10);
ShowNum(1,3,Temp%10);
}
else if(num < 200)
{
ShowNum(2,1,Humi/100);
ShowNum(2,2,Humi/ 10 % 10);
ShowNum(2,3,Humi%10);
}
else{
num = 0;
ShowNum(1,1,Temp/100);
ShowNum(1,2,Temp/ 10 % 10);
ShowNum(1,3,Temp%10);
}
}
/* USER CODE END 3 */
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82