18、MSP430 微控制器应用开发技术详解

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 微控制器在实际项目中。希望这些信息能为大家带来帮助和启发。

【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点探讨其系统建模与控制策略,结合Matlab代码与Simulink仿真实现。文章详细分析了无人机的动力学模型,特别是引入螺旋桨倾斜机构后带来的全驱动特性,使其在姿态与位置控制上具备更强的机动性与自由度。研究涵盖了非线性系统建模、控制器设计(如PID、MPC、非线性控制等)、仿真验证及动态响应分析,旨在提升无人机在复杂环境下的稳定性和控制精度。同时,文中提供的Matlab/Simulink资源便于读者复现实验并进一步优化控制算法。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真经验的研究生、科研人员及无人机控制系统开发工程师,尤其适合从事飞行器建模与先进控制算法研究的专业人员。; 使用场景及目标:①用于全驱动四旋翼无人机的动力学建模与仿真平台搭建;②研究先进控制算法(如模型预测控制、非线性控制)在无人机系统中的应用;③支持科研论文复现、课程设计或毕业课题开发,推动无人机高机动控制技术的研究进展。; 阅读建议:建议读者结合文档提供的Matlab代码与Simulink模型,逐步实现建模与控制算法,重点关注坐标系定义、力矩分配逻辑及控制闭环的设计细节,同时可通过修改参数和添加扰动来验证系统的鲁棒性与适应性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值