MSP430 微控制器应用开发技术详解
1. 星期计算算法
在星期计算过程中,通过一系列步骤逐步简化公式并得出结果。首先,在步骤 (DW - C2) 计算下一个小部分,并将部分结果存储到变量
cd_help2
中:
w = (d + cd_help1 + y + (y / 4) + (c / 4) - 2 x c) mod 7
cd_help2 = y / 4
经过此步骤,公式可进一步简化为:
w = (d + cd_help1 + y + cd_help2 + (c / 4) - 2 x c) mod 7
最后一个部分结果存储在变量
cd_help3
中(步骤 (DW - C3)):
cd_help3 = y / 4
经过最终简化,公式变为:
w = (d + cd_help1 + y + cd_help2 + cd_help3 - 2 x c) mod 7
此公式在步骤 (DW - C4x) 中进行计算。最后,在步骤 (DW - C5) 将计算值存储到变量
rtc_wday
中,从而得到星期信息。
2. AT30TSE754 温度传感器应用
2.1 主功能函数
i2c_temp_1()
该函数负责处理 AT30TSE754 传感器的屏幕显示和操作。在主温度传感器屏幕上,可进行以下操作:
- 按“*”键离开。
- 按“A”键手动更改传感器的 I2C 地址。
- 按“2”键在显示 2 位或 4 位小数之间切换。
- 按“1”键更改分辨率(9 到 12 位)。
以下是
i2c_temp_1()
函数的代码:
void i2c_temp_1()
{
gv_format = 0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print(“AT30TSE754 (*)”);
lcd.setCursor(0,1);
lcd.print(“I2C Ad: (A)”);
lcd.setCursor(0,2);
lcd.print(“Temp: (2)”);
lcd.setCursor(0,3);
lcd.print(“Res : bit (1)”);
while(active_key != 0xFF) read_kbd();
while(active_key != 0x0E)
{
Wire.beginTransmission(i2c_add);
Wire.write(0x00);
i2c_answer = Wire.endTransmission(1);
if (i2c_answer == 0)
{
lcd.setCursor(0,2);
lcd.print(“Temp: “);
lcd.setCursor(17,2);
lcd.print(“(2)”);
lcd.setCursor(0,3);
lcd.print(“Res : “);
lcd.setCursor(13,3);
lcd.print(“bit (1)”);
Wire.requestFrom(i2c_add, 2, 1); //I2C address / 2 bytes // stop
v_temp_msb = 0xFF;
v_temp_lsb = 0xFF;
v_temp_msb = Wire.read();
v_temp_lsb = Wire.read();
w_temp_ub(6,2,0);
read_t_config_reg();
if (active_key == 0x01)
{
t_change_res();
read_t_config_reg();
while(active_key != 0xFF) read_kbd();
}
lcd.setCursor(10,3);
temp_res = temp_res + 9;
w_print_dec1_nlz(temp_res);
}
else
{
lcd.setCursor(0,2);
lcd.print(“No response “);
lcd.setCursor(0,3);
lcd.print(“from Sensor “);
}
w_i2c_add(8,1);
read_kbd();
delay(200);
if (active_key == 0x0A)
{
i2c_t_change_add();
while(active_key != 0xFF) read_kbd();
}
if (active_key == 0x02)
{
gv_format++;
if (gv_format == 2) gv_format = 0;
while(active_key != 0xFF) read_kbd();
}
}
gv_refresh = 1;
}
2.2 更改 I2C 地址
AT30TSE754 可通过 8 个不同的 I2C 地址访问,当前地址存储在变量
i2c_add
中。要手动更改地址,可调用
i2c_t_change_add()
函数:
void i2c_t_change_add(void)
{
i2c_add++; //(CA - 1)
if (bitRead(i2c_add,3) == 0) i2c_add = 0b01001000; //(CA - 2) 111->000
}
该函数先增加
i2c_add
的值(步骤 (CA - 1)),当出现“地址溢出”时,将地址设置为最低值(步骤 (CA - 2))。
2.3 搜索 I2C 温度传感器
可通过
find_i2c_temp()
函数自动搜索连接的 I2C 温度传感器:
void find_i2c_temp()
{
i2c_add = 0b01001000; //(ST - 1) we start with lowest i2c address
while(1) //(ST - 2) end - less loop starts here
{
Wire.beginTransmission(i2c_add); //(ST - 3) ping the sensor
Wire.write(0x00); //(ST - 4) write dummy value
i2c_answer = Wire.endTransmission(1); //(ST - 5) get tge result of our ping
if (i2c_answer == 0) return; //(ST - 6) leave, if there was an answer
i2c_add++; //(ST - 7) increment i2c address
if (bitRead(i2c_add,3) == 0) //(ST - 8) address overflow?
{
i2c_add = 0b01001000; //(ST - 9) re - set i2c address to the lowest
return; //(ST - 10) leave the search, nothig found
}
}
}
该函数从最低 I2C 地址(步骤 (ST - 1))开始,在一个循环(步骤 (ST - 2))中对每个地址进行“ping”操作(步骤 (ST - 3) 和 (ST - 4)),获取结果(步骤 (ST - 5))。若有响应则返回(步骤 (ST - 6)),若无响应则增加地址(步骤 (ST - 7)),若地址溢出则重置地址并结束搜索(步骤 (ST - 8) 到 (ST - 10))。
2.4 配置寄存器操作
2.4.1 读取配置寄存器
read_t_config_reg()
函数用于读取传感器的当前配置:
void read_t_config_reg()
{
Wire.beginTransmission(i2c_add); //(TCR - 1) open i2c communication
Wire.write(0x01); //(TCR - 2) set pointer to the config.reg.
Wire.endTransmission(1); //(TCR - 3) finish i2c comm.
Wire.requestFrom(i2c_add, 1, 1); //(TCR - 4) open i2c for read: I2C address / 1 byte // stop
v_config_msb = Wire.read(); //(TCR - 5) read config.reg.
//(TCR - 6) “calculate” the resolution based on info from config.reg.:
temp_res = 2 * bitRead(v_config_msb,6) + bitRead(v_config_msb,5);
}
该函数先打开 I2C 通信(步骤 (TCR - 1)),将指针设置到配置寄存器地址(步骤 (TCR - 2)),结束通信(步骤 (TCR - 3)),再打开读取模式(步骤 (TCR - 4)),读取配置寄存器值(步骤 (TCR - 5)),最后根据配置寄存器信息计算分辨率(步骤 (TCR - 6))。
2.4.2 更改分辨率
read_t_config_reg()
函数用于读取传感器的当前配置:
void t_change_res()
{
temp_res++; //(TCU - 1) increase the resolution
if (temp_res > 3) temp_res = 0x00; //(TCU - 2) overflow – set back to 00
v_config_msb = temp_res * 32; //(TCU - 3) calculate the new value for config. reg.
// all other config bits are eq zero
Wire.beginTransmission(i2c_add); //(TCU - 4) open i2c comm.
Wire.write(0x01); //(TCU - 5) send 01h – the address of config.reg.
Wire.write(v_config_msb); //(TCU - 6) send the configuration byte
Wire.endTransmission(1); //(TCU - 7) finish i2c communication
}
该函数先增加分辨率(步骤 (TCU - 1)),处理溢出情况(步骤 (TCU - 2)),计算新的配置寄存器值(步骤 (TCU - 3)),然后打开 I2C 通信(步骤 (TCU - 4)),发送配置寄存器地址(步骤 (TCU - 5))和新配置字节(步骤 (TCU - 6)),最后结束通信(步骤 (TCU - 7))。
分辨率与配置寄存器位的对应关系如下表所示:
| Bits 14:13 | MSB bits 6:5 | Resolution |
| — | — | — |
| 00 | 9 - bits (default after power - up) |
| 01 | 10 - bits |
| 10 | 11 - bits |
| 11 | 12 - bits |
2.5 流程图
graph TD;
A[开始] --> B[初始化 i2c_add 为最低地址];
B --> C{是否有响应};
C -- 是 --> D[结束,i2c_add 为有效地址];
C -- 否 --> E[i2c_add 加 1];
E --> F{是否地址溢出};
F -- 是 --> G[重置 i2c_add 为最低地址,结束];
F -- 否 --> C;
3. HYT939 湿度和温度传感器
3.1 触发测量
hyt939_start()
函数用于触发 HYT939 传感器的测量:
byte hyt939_start()
{
Wire.beginTransmission(0b00101000); //(H1 - 1) I2C Address = 0101 000x = 50h
return(Wire.endTransmission(1)); //(H1 - 2)
}
该函数通过开始 I2C 通信(步骤 (H1 - 1))并结束通信(步骤 (H1 - 2))来触发湿度和温度测量。
3.2 读取测量结果
hyt939_read()
函数用于读取测量结果并计算湿度和温度:
void hyt939_read()
{
Wire.requestFrom(0b00101000, 4, 1); //(H2 - 1) I2C address / 4 bytes // stop
v_h939_cap_msb = Wire.read(); //(H2 - 2) read humidity / MSB
v_h939_cap_lsb = Wire.read(); //(H2 - 3) read humidity / LSB
v_h939_t_msb = Wire.read(); //(H2 - 4) read temperature / MSB
v_h939_t_lsb = Wire.read(); //(H2 - 5) read temperature / LSB
v_h939_cap_msb = v_h939_cap_msb & 0b00111111;
v_h939_c_raw = v_h939_cap_msb * 256 + v_h939_cap_lsb;
h939_rf = (float)((float)v_h939_c_raw / 16383 ) * 100;
h939_rf_new = (float)h939_rf * 10;
if(h939_rf_new != h939_rf_old) lv_c2++;
else lv_c2 = 0;
if(lv_c2 == 3)
{
h939_rf_result = h939_rf;
h939_rf_old = h939_rf_new;
lv_c2 = 0;
}
if (h939_rf_result == 0)
{
h939_rf_result = h939_rf;
h939_rf_old = (float)h939_rf * 10;
}
v_h939_t_lsb = v_h939_t_lsb & 0b11111100;
v_h939_t_raw = (v_h939_t_msb * 64) + (v_h939_t_lsb / 4);
h939_temp_long = 165 * (long)v_h939_t_raw;
h939_temp = (float)((float)h939_temp_long/16383) - 40;
h939_t_new = (float)h939_temp * 10;
if(h939_t_new != h939_t_old) lv_c3++;
else lv_c3 = 0;
if(lv_c3 == 3)
{
h939_temp_result = h939_temp;
h939_t_old = h939_t_new;
lv_c3 = 0;
}
if (h939_temp_result == 0)
{
h939_temp_result = h939_temp;
h939_t_old = (float)h939_temp * 10;
}
}
该函数先通过 I2C 读取 4 个字节的数据(步骤 (H2 - 1) 到 (H2 - 5)),然后分别计算湿度和温度。在计算过程中,包含了“抗闪烁效应”,即当测量结果连续三次相同时才接受该测量值,以稳定 LCD 显示信息。
3.3 流程图
graph TD;
A[开始] --> B[触发测量];
B --> C[读取 4 字节数据];
C --> D[计算湿度];
D --> E{湿度结果是否稳定};
E -- 是 --> F[记录湿度结果];
E -- 否 --> D;
C --> G[计算温度];
G --> H{温度结果是否稳定};
H -- 是 --> I[记录温度结果];
H -- 否 --> G;
F --> J[结束];
I --> J;
4. 键盘驱动
4.1 键盘硬件连接与通信原理
键盘由 PCF8574A 芯片驱动,通过 I2C 总线与微控制器通信。矩阵键盘由 4 行 4 列组成,其中列(P4 - P7)作为键盘矩阵的输入,行(P0 - P3)作为输出。
4.2 I2C 通信例程
get_col()
函数用于读取特定列中被按下的按钮:
byte get_col(byte in_col) //(K - 1) get active button in a column
{
Wire.beginTransmission(0b00111111); //(K - 2) start write to PCF8574AN -> 0111 111x
Wire.write(in_col); //(K - 3) write required byte
Wire.endTransmission(1); //(K - 4) finish write phase
Wire.requestFrom(0b00111111, 1, 1); //(K - 5) request 1 byte: I2C address / 1 byte
// stop
return(Wire.read()); //(K - 6) get the requested byte
}
该函数的操作步骤如下:
1. 开始向 PCF8574A 芯片进行写操作(步骤 (K - 2))。
2. 写入所需的字节(步骤 (K - 3))。
3. 结束写操作(步骤 (K - 4))。
4. 请求读取 1 个字节的数据(步骤 (K - 5))。
5. 返回读取到的字节(步骤 (K - 6))。
4.3 主键盘驱动
read_kbd()
函数用于读取键盘输入,并设置两个全局变量:
active_key
和
act_key_ascii
。
active_key
是按下按键的十六进制代码,
act_key_ascii
是按键的 ASCII 码。
以下是
read_kbd()
函数的代码:
void read_kbd()
{
//Part A – read the keyboard
active_key = 0xFF; //default - no key is active
in_kbd_7 = get_col(0b01111111);
pcf8574_send(in_kbd_7);
in_kbd_6 = get_col(0b10111111);
pcf8574_send(in_kbd_6);
in_kbd_5 = get_col(0b11011111);
pcf8574_send(in_kbd_5);
in_kbd_4 = get_col(0b11101111);
pcf8574_send(in_kbd_4);
get_col(0b11111111);
pcf8574_send(0xFF);
//Part B – analyze
switch(in_kbd_7)
{
case 0b01111110:
active_key = 0x01; //key 1
break;
case 0b01111101:
active_key = 0x04; //key 4
break;
case 0b01111011:
active_key = 0x07; //key 7
break;
case 0b01110111:
active_key = 0x0E; //key *
break;
}
switch(in_kbd_6)
{
case 0b10111110:
active_key = 0x02; //key 2
break;
case 0b10111101:
active_key = 0x05; //key 5
break;
case 0b10111011:
active_key = 0x08; //key 8
break;
case 0b10110111:
active_key = 0x00; //key 0
break;
}
switch(in_kbd_5)
{
case 0b11011110:
active_key = 0x03; //key 3
break;
case 0b11011101:
active_key = 0x06; //key 6
break;
case 0b11011011:
active_key = 0x09; //key 9
break;
case 0b11010111:
active_key = 0x0F; //key #
break;
}
switch(in_kbd_4)
{
case 0b11101110:
active_key = 0x0A; //key A
break;
case 0b11101101:
active_key = 0x0B; //key B
break;
case 0b11101011:
active_key = 0x0C; //key C
break;
case 0b11100111:
active_key = 0x0D; //key D
break;
}
//Part C – calculate ASCII code
act_key_ascii = 0xFF;
if (active_key != 0xFF)
{
act_key_ascii = active_key + 48;
if (active_key > 0x09) act_key_ascii = active_key + 55;
}
if (act_key_ascii == ‘E’) act_key_ascii = ‘*’;
if (act_key_ascii == ‘F’) act_key_ascii = ‘#’;
}
该函数的具体操作步骤如下:
1.
Part A:读取键盘
- 将
active_key
初始化为
0xFF
,表示没有按键被按下。
- 依次读取 P7 - P4 列的按键状态,并将结果存储在
in_kbd_7
-
in_kbd_4
变量中。
2.
Part B:分析按键信息
- 根据
in_kbd_7
-
in_kbd_4
的值,通过
switch
语句判断哪个按键被按下,并将对应的十六进制代码赋值给
active_key
。
3.
Part C:计算 ASCII 码
- 如果
active_key
不为
0xFF
,则根据
active_key
的值计算按键的 ASCII 码,并存储在
act_key_ascii
中。
- 对
act_key_ascii
进行特殊处理,将
E
替换为
*
,将
F
替换为
#
。
4.4 按键代码与 ASCII 码对应关系
按键标签、列、行、
active_key
和
act_key_ascii
的对应关系如下表所示:
| 按键标签 | 列 | 行 | active_key | act_key_ascii | 字符 |
| — | — | — | — | — | — |
| 0 | P6 | P3 | 00h | 48d = 30h | 0 |
| 1 | P7 | P0 | 01h | 49d = 31h | 1 |
| 2 | P6 | P0 | 02h | 50d = 32h | 2 |
| 3 | P5 | P0 | 03h | 51d = 33h | 3 |
| 4 | P7 | P1 | 04h | 52d = 34h | 4 |
| 5 | P6 | P1 | 05h | 53d = 35h | 5 |
| 6 | P5 | P1 | 06h | 54d = 36h | 6 |
| 7 | P7 | P2 | 07h | 55d = 37h | 7 |
| 8 | P6 | P2 | 08h | 56d = 38h | 8 |
| 9 | P5 | P2 | 09h | 57d = 39h | 9 |
| A | P4 | P0 | 0Ah | 54d = 41h | A |
| B | P4 | P1 | 0Bh | 55d = 42h | B |
| C | P4 | P2 | 0Ch | 56d = 43h | C |
| D | P4 | P3 | 0Dh | 57d = 44h | D |
| * | P7 | P3 | 0Eh | 42d = 2Ah | * |
| # | P5 | P3 | 0Fh | 35d = 23h | # |
| 无 | – | – | FFh | FFh | n/a |
4.5 流程图
graph TD;
A[开始] --> B[初始化 active_key 为 0xFF];
B --> C[读取 P7 - P4 列按键状态];
C --> D[分析 P7 列按键信息];
D --> E[分析 P6 列按键信息];
E --> F[分析 P5 列按键信息];
F --> G[分析 P4 列按键信息];
G --> H{是否有按键被按下};
H -- 是 --> I[计算按键 ASCII 码];
H -- 否 --> J[结束];
I --> J;
综上所述,我们详细介绍了星期计算算法、AT30TSE754 温度传感器应用、HYT939 湿度和温度传感器以及键盘驱动的相关知识和代码实现。通过这些内容,我们可以更好地理解和应用 MSP430 微控制器在实际项目中。希望这些信息能为大家带来帮助和启发。
超级会员免费看
58

被折叠的 条评论
为什么被折叠?



