Lab5 uC/OS 室温计

实验连接示意图

所用器材列表

硬件

- STM32F103核心板一块
- USB串口板一块
- 面包板一块
- 两位7段数码管(共阳)一颗
- 360Ω 1/8W电阻2颗
- DHT-11温湿度传感器1个
- 面包线若干

软件

- 编译软件
- Fritzing

外部设备连线图

实验步骤、操作的命令和结果

1.设计输出方案,画连线示意图

2.在面包板上连线,完成外部电路

3.编写C/C++程序,测试程序和电路

1) 测试、实现uC/OS对GPIO的访问
2) 实现DHT-11数据的读
3) 实现以时分复用方式在四位7段数码管上一次显示0000-9999的数字
4) 用两个uC/OS任务,一个定时读DHT-11数据,一个轮询驱动数码管,一秒一次显示当前湿度和温度。注意处理好两个任务之间的数据共享

源代码

7段数码管程序

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
void GPIO_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;

RCC_DeInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13
| GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11
| GPIO_Pin_12;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0
| GPIO_Pin_1
| GPIO_Pin_2
| GPIO_Pin_3
| GPIO_Pin_4
| GPIO_Pin_5
| GPIO_Pin_6
| GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void Delay_ms(int times){
OSTimeDly(OS_TICKS_PER_SEC / 1000 * times);
}

void digit_select(int index){
// 通过输出高电平选择点亮某个数字
BitAction v[4];
int i;
for (i=0; i<4; i++){
if (index == i){
v[i] = Bit_SET;
}else{
v[i] = Bit_RESET;
}
}
GPIO_WriteBit(GPIOA, GPIO_Pin_11, v[0]);
GPIO_WriteBit(GPIOA, GPIO_Pin_12, v[1]);
GPIO_WriteBit(GPIOC, GPIO_Pin_13, v[2]);
GPIO_WriteBit(GPIOC, GPIO_Pin_14, v[3]);
}

void digit_show(int dight, int point){
// 通过对引脚输出低电平点亮数码管段
int segment, i, base;
BitAction v[8];
switch (dight){
case 0 : segment = 0xee; break; // 0b11101110 0 -> 7
case 1 : segment = 0x24; break; // 0b00100100
case 2 : segment = 0xba; break; // 0b10111010
case 3 : segment = 0xb6; break; // 0b10110110
case 4 : segment = 0x74; break; // 0b01110100
case 5 : segment = 0xd6; break; // 0b11010110
case 6 : segment = 0xde; break; // 0b11011110
case 7 : segment = 0xa4; break; // 0b10100100
case 8 : segment = 0xfe; break; // 0b11111110
case 9 : segment = 0xf6; break; // 0b11110110
default : segment = 0xda; break; // 0b11011010 error state
}
segment |= point != 0; // 小数点为最低位
base = 1 << 8;
for (i=0; i<8; i++){
base >>= 1;
// segment中某位的1表示点亮,而输出低电平为点亮
if ((segment & base )== 0){
v[i] = Bit_SET;
}else{
v[i] = Bit_RESET;
}
}
GPIO_WriteBit(GPIOA, GPIO_Pin_0, v[0]);
GPIO_WriteBit(GPIOA, GPIO_Pin_1, v[1]);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, v[2]);
GPIO_WriteBit(GPIOA, GPIO_Pin_3, v[3]);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, v[4]);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, v[5]);
GPIO_WriteBit(GPIOA, GPIO_Pin_6, v[6]);
GPIO_WriteBit(GPIOA, GPIO_Pin_7, v[7]);
}

void led_show(int digit){
// 时分复用的方式输出数字,每次调用led_show只输出一位数字
static int index = -1;
int i;
int base = 1000;
index = (index + 1) % 4;
for (i=0; i<index; i++){
base /= 10;
}
digit = (digit / base) % 10;
digit_select(index);
digit_show(digit, 0);
}

int ledValue = 0; // 这个值由task0写入,task1读取,都是单向的,所以可以不考虑线程之间的冲突

void LED0_task(void* pdata){
while (1){
ledValue++;
Delay_ms(200);
}
}

void LED1_task(void* pdata){
while(1){
led_show(ledValue);
Delay_ms(7);
}
}

DHT11数据读取及显示

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#define MAX_TICS 100000
#define DHT11_OK 0
#define DHT11_NO_CONN 1
#define DHT11_CS_ERROR 2
#define DHT11_PORT GPIOB
#define DHT11_PIN GPIO_Pin_0

void Delay_us(int times){
// 我自己测大约这个delay函数需要的时间是1.4us * @times
unsigned int i;
for (i=0; i<times; i++){
// 外层循环一次差不多1us,不过不是特别精确,所以不需要内层的循环了
/*
unsigned int j;
for (j=0; j<0x3fff; j++){
}
*/

}
}

void ErrorState(int state){
// 使用死循环自杀,并显示错误码
while (1){
led_show(state);
Delay_us(4000);
}
}

void DHT11_Set(int state){
// 设置DHT11 GPIO口的值
BitAction s;
if (state){
s = Bit_SET;
}else{
s = Bit_RESET;
}
GPIO_WriteBit(DHT11_PORT, DHT11_PIN, s);
}

void DHT11_Pin_OUT(){
// 调整DHT11 GPIO口为输出模式
GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);

DHT11_Set(1);
}

void DHT11_Pin_IN(){
// 调整DHT11_GPIO口为输入模式
GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);

DHT11_Set(1);
}

uint8_t DHT11_Check(){
// 获取DHT11 GPIO口的数据,下方算式与调用函数等价
return (DHT11_PORT->IDR & DHT11_PIN)> 0;
//return GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN);
}

void DHT11_Wait(int state, int place){
// 等待GPIO口变为state的值,超时自动自杀
int loopCnt = MAX_TICS;
while (DHT11_Check() != state){
if (loopCnt -- == 0){
ErrorState(1000 + state * 1000 + place);
}
}
}

void DHT11_Rst(){
// stm32端输出握手信号
DHT11_Pin_OUT();
DHT11_Set(0);
Delay_us(25000);
DHT11_Set(1);
Delay_us(40);
DHT11_Set(0);
// 转为接收模式准备读入DHT11的握手信号
DHT11_Pin_IN();
}

int val = 10;

uint8_t DHT11_Read_Byte(){
// 读入一个Byte
int i, cnt;
uint8_t data = 0;
for (i=0; i<8; i++){
cnt = 0;
data <<= 1;
// 当前为低电平,等待高电平
DHT11_Wait(1, ++val);

// 计算高电平持续的时间
while (DHT11_Check() > 0){
Delay_us(1);
cnt++;
}
// 持续的足够久则为bit 1
data |= cnt > 5;
}
return data;
}

uint8_t DHT11_Read_Data(uint8_t *buf){
// 从DHT11内读取数据的函数
int i;
unsigned int cpu_sr;
// 为了关闭中断进入临界区
OS_ENTER_CRITICAL();
val = 10;
// 发送握手消息
DHT11_Rst();
// 如果给予了回复
if (DHT11_Check() == 0){
// 等待低电平过去
DHT11_Wait(1, 2);
// 等待高电平过去
DHT11_Wait(0, 3);
// 握手完成,开始读取40个bit
for (i=0; i<5; i++){
buf[i] = DHT11_Read_Byte();
}

// 重新将GPIO口置为输出模式
DHT11_Pin_OUT();
OS_EXIT_CRITICAL();

// 判断校验和是否满足要求
if (buf[0] + buf[1] + buf[2] + buf[3] == buf[4]){
return DHT11_OK;
}else{
return DHT11_CS_ERROR;
}
}else{
// 该分支表示没有收到回复
OS_EXIT_CRITICAL();
return DHT11_NO_CONN;
}
}

uint8_t DHT11_Humidity(uint8_t *buf){
// 返回湿度
return buf[0];
}

uint8_t DHT11_Temperature(uint8_t *buf){
// 返回温度
return buf[2];
}