基于MSP430的自行车户外运动系统C语言实现

基于MSP430的自行车户外运动系统C语言实现

一、系统概述

以MSP430G2553为核心,通过干簧管测速、DHT22/BH1750环境监测、12864液晶显示及低功耗设计,实现自行车骑行的速度、里程、环境参数实时监测。代码采用中断驱动+低功耗模式(LPM3),确保电池续航(2节7号电池≥6个月)。

二、核心代码实现

2.1 头文件与全局变量定义

#include <msp430g2553.h>
#include <stdio.h>
#include <string.h>

// 系统时钟定义
#define DCO_FREQ 8000000       // DCO频率8MHz
#define ACLK_FREQ 32768         // ACLK频率32.768kHz

// 干簧管与测速参数
#define WHEEL_CIRCUMFERENCE 2100  // 车轮周长(mm,26寸车轮约2100mm)
volatile unsigned int last_capture = 0;  // 上一次脉冲捕获时间
volatile unsigned int current_capture = 0; // 当前脉冲捕获时间
volatile float speed = 0.0;          // 实时速度(km/h)
volatile float total_distance = 0.0;  // 累计里程(km)
volatile unsigned int pulse_count = 0; // 脉冲计数

// 环境传感器参数
#define DHT22_PIN BIT0          // P2.0(DHT22数据线)
#define BH1750_ADDR 0x23        // BH1750 I2C地址(0x23或0x5C)
volatile int temperature = 0;       // 温度(℃)
volatile int humidity = 0;          // 湿度(%RH)
volatile unsigned int light = 0;     // 光照度(lx)

// LCD参数(12864,并行接口)
#define LCD_RS BIT0             // P3.0(RS:数据/指令选择)
#define LCD_RW BIT1             // P3.1(RW:读/写选择)
#define LCD_E  BIT2             // P3.2(E:使能)
#define LCD_CS BIT3             // P3.3(CS:片选)
#define LCD_DATA_PORT P4OUT      // P4口(DB0-DB7)
volatile unsigned char lcd_page = 0;  // 当前显示页面(0-4)

// 按键参数
#define KEY1_PIN BIT3            // P1.3(按键1:切换界面)
#define KEY2_PIN BIT4            // P1.4(按键2:增加参数)
#define KEY3_PIN BIT5            // P1.5(按键3:减少参数)
volatile unsigned char key_flag = 0;  // 按键标志(1-3对应按键1-3)

2.2 系统初始化(时钟、GPIO、外设)

void System_Init(void) {
    WDTCTL = WDTPW | WDTHOLD;       // 关闭看门狗
    
    // 时钟配置:DCO=8MHz(MCLK),ACLK=32.768kHz(LFXT1)
    BCSCTL1 = CALBC1_8MHZ;          // 校准DCO到8MHz
    DCOCTL = CALDCO_8MHZ;
    BCSCTL3 |= LFXT1S_0;            // ACLK使用LFXT1(32.768kHz晶振)
    BCSCTL1 |= XT2OFF;              // 关闭XT2
    
    // GPIO初始化
    P1DIR &= ~(KEY1_PIN | KEY2_PIN | KEY3_PIN);  // 按键输入
    P1REN |= (KEY1_PIN | KEY2_PIN | KEY3_PIN);   // 上拉电阻
    P1OUT |= (KEY1_PIN | KEY2_PIN | KEY3_PIN);
    P1IES |= (KEY1_PIN | KEY2_PIN | KEY3_PIN);   // 下降沿触发
    P1IE |= (KEY1_PIN | KEY2_PIN | KEY3_PIN);    // 使能中断
    
    P2DIR &= ~DHT22_PIN;            // DHT22数据线(输入)
    P2OUT |= DHT22_PIN;             // 上拉
    
    // LCD控制线(P3口)
    P3DIR |= LCD_RS | LCD_RW | LCD_E | LCD_CS;
    P3OUT &= ~(LCD_RS | LCD_RW | LCD_E | LCD_CS);
    // LCD数据线(P4口,8位)
    P4DIR = 0xFF;
    P4OUT = 0x00;
    
    // Timer_A配置(捕获模式,用于干簧管测速)
    TA0CTL = TASSEL_1 | MC_2 | TACLR;  // ACLK,连续计数,清计数器
    TA0CCTL1 = CM_2 | CCIS_0 | CAP | CCIE;  // 下降沿捕获,CCI0A(P1.1),使能中断
    P1SEL |= BIT1;                  // P1.1(TA0.1)作为捕获输入
    
    // I2C配置(BH1750,P1.6=SDA,P1.7=SCL)
    P1SEL |= BIT6 | BIT7;           // P1.6/P1.7复用为I2C
    UCB0CTL1 |= UCSWRST;            // 复位I2C
    UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;  // 主机,I2C模式,同步
    UCB0CTL1 = UCSSEL_2 | UCSWRST;   // SMCLK(8MHz)作为时钟源
    UCB0BR0 = 40;                   // 8MHz/40=200kHz(I2C速率)
    UCB0BR1 = 0;
    UCB0CTL1 &= ~UCSWRST;           // 启动I2C
    
    // 使能全局中断
    __enable_interrupt();
}

2.3 干簧管测速(Timer_A捕获中断)

#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer0_A1_ISR(void) {
    switch(TA0IV) {
        case 2:  // TA0CCR1捕获中断
            current_capture = TA0CCR1;  // 读取当前计数值
            if(last_capture != 0) {     // 非首次捕获
                unsigned int delta_t = (current_capture >= last_capture) ? 
                                     (current_capture - last_capture) : 
                                     (current_capture + 0xFFFF - last_capture);
                float delta_t_sec = (float)delta_t / ACLK_FREQ;  // 时间间隔(秒)
                if(delta_t_sec > 0.1) {  // 防抖(间隔>0.1s才计算)
                    // 速度计算:v = 60*C/(Δt*1000)(C=车轮周长mm,Δt=秒)
                    speed = (60.0 * WHEEL_CIRCUMFERENCE) / (delta_t_sec * 1000.0);
                    // 里程累计:s += v*Δt/3600(km)
                    total_distance += (speed * delta_t_sec) / 3600.0;
                }
            }
            last_capture = current_capture;  // 更新上一次捕获时间
            pulse_count++;
            break;
        default: break;
    }
}

2.4 环境监测(DHT22+BH1750)

2.4.1 DHT22单总线读取

unsigned char DHT22_Read(void) {
    unsigned char data[5] = {0};
    unsigned int timeout;
    
    // 起始信号:拉低至少18ms
    P2DIR |= DHT22_PIN;
    P2OUT &= ~DHT22_PIN;
    __delay_cycles(20000);  // 20ms@8MHz
    P2OUT |= DHT22_PIN;
    P2DIR &= ~DHT22_PIN;
    __delay_cycles(30);     // 30us
    
    // 等待DHT22响应(低电平80us)
    timeout = 1000;
    while(!(P2IN & DHT22_PIN) && timeout--);
    if(timeout == 0) return 0;
    
    // 等待DHT22数据开始(高电平80us)
    timeout = 1000;
    while((P2IN & DHT22_PIN) && timeout--);
    if(timeout == 0) return 0;
    
    // 读取5字节数据(40位)
    for(unsigned char i=0; i<5; i++) {
        for(unsigned char j=0; j<8; j++) {
            // 等待低电平(50us)
            timeout = 1000;
            while(!(P2IN & DHT22_PIN) && timeout--);
            if(timeout == 0) return 0;
            // 延时30us后读数据(高电平26-28us=0,70us=1)
            __delay_cycles(30);
            data[i] <<= 1;
            if(P2IN & DHT22_PIN) data[i] |= 1;
            // 等待高电平结束
            timeout = 1000;
            while((P2IN & DHT22_PIN) && timeout--);
        }
    }
    
    // 校验和验证
    if(data[4] == (data[0] + data[1] + data[2] + data[3])) {
        humidity = (data[0] << 8) | data[1];    // 湿度(整数部分)
        temperature = (data[2] << 8) | data[3];  // 温度(整数部分)
        return 1;
    }
    return 0;
}

2.4.2 BH1750 I2C读取

void BH1750_Read(void) {
    unsigned char cmd = 0x10;  // 连续高分辨率模式(1lx精度)
    UCB0CTL1 |= UCTR;          // 主机发送
    UCB0CTL1 |= UCTXSTT;       // 发送起始条件
    while(!(UCB0IFG & UCTXIFG)); // 等待发送缓冲空
    UCB0TXBUF = BH1750_ADDR << 1; // 从机地址+写
    while(!(UCB0IFG & UCTXIFG));
    UCB0TXBUF = cmd;           // 发送命令
    while(!(UCB0IFG & UCTXIFG));
    UCB0CTL1 |= UCTXSTP;       // 发送停止条件
    while(UCB0CTL1 & UCTXSTP);
    
    __delay_cycles(200000);     // 等待测量完成(180ms)
    
    // 读取数据
    UCB0CTL1 &= ~UCTR;         // 主机接收
    UCB0CTL1 |= UCTXSTT;       // 发送起始条件
    while(!(UCB0IFG & UCRXIFG)); // 等待接收
    unsigned char high = UCB0RXBUF;
    UCB0CTL1 |= UCTXSTP;       // 发送停止条件
    while(!(UCB0IFG & UCRXIFG));
    unsigned char low = UCB0RXBUF;
    light = (high << 8) | low;  // 原始数据
    light = light / 1.2;        // 转换为lx(高分辨率模式)
}

2.5 12864液晶显示驱动

// 写命令/数据
void LCD_WriteCmd(unsigned char cmd) {
    P3OUT &= ~(LCD_RS | LCD_RW);  // RS=0(命令),RW=0(写)
    P3OUT |= LCD_E;               // E=1
    LCD_DATA_PORT = cmd;
    __delay_cycles(100);          // 延时
    P3OUT &= ~LCD_E;              // E=0
}

void LCD_WriteData(unsigned char dat) {
    P3OUT |= LCD_RS;              // RS=1(数据)
    P3OUT &= ~LCD_RW;             // RW=0(写)
    P3OUT |= LCD_E;               // E=1
    LCD_DATA_PORT = dat;
    __delay_cycles(100);
    P3OUT &= ~LCD_E;              // E=0
}

// 初始化
void LCD_Init(void) {
    __delay_cycles(50000);         // 上电延时
    LCD_WriteCmd(0x30);            // 基本指令集
    __delay_cycles(1000);
    LCD_WriteCmd(0x0C);            // 显示开,光标关
    LCD_WriteCmd(0x01);            // 清屏
    __delay_cycles(5000);
}

// 显示字符串
void LCD_ShowString(unsigned char x, unsigned char y, char *str) {
    unsigned char addr = (y == 0) ? 0x80 + x : 0xC0 + x;  // 第一行/第二行
    LCD_WriteCmd(addr);
    while(*str) {
        LCD_WriteData(*str++);
    }
}

// 显示数字
void LCD_ShowNum(unsigned char x, unsigned char y, float num, unsigned char dec) {
    char buf[10];
    if(dec == 0) sprintf(buf, "%d", (int)num);
    else sprintf(buf, "%.1f", num);
    LCD_ShowString(x, y, buf);
}

2.6 按键处理(外部中断)

#pragma vector=PORT1_VECTOR
__interrupt void Port1_ISR(void) {
    if(P1IFG & KEY1_PIN) {         // 按键1:切换界面
        lcd_page = (lcd_page + 1) % 5;  // 0-4循环
        P1IFG &= ~KEY1_PIN;
    } else if(P1IFG & KEY2_PIN) {  // 按键2:增加参数(如车轮周长)
        WHEEL_CIRCUMFERENCE += 10;
        P1IFG &= ~KEY2_PIN;
    } else if(P1IFG & KEY3_PIN) {  // 按键3:减少参数
        WHEEL_CIRCUMFERENCE = (WHEEL_CIRCUMFERENCE > 10) ? (WHEEL_CIRCUMFERENCE - 10) : 10;
        P1IFG &= ~KEY3_PIN;
    }
    key_flag = 1;  // 标记按键按下
}

2.7 主程序(低功耗循环+数据显示)

void main(void) {
    System_Init();
    LCD_Init();
    DHT22_Read();  // 初始读取环境数据
    BH1750_Read();
    
    while(1) {
        // 1. 读取传感器数据
        DHT22_Read();
        BH1750_Read();
        
        // 2. 显示当前页面
        LCD_WriteCmd(0x01);  // 清屏
        switch(lcd_page) {
            case 0:  // 主界面:速度、里程
                LCD_ShowString(0, 0, "Speed: ");
                LCD_ShowNum(6, 0, speed, 1);  // 1位小数
                LCD_ShowString(0, 1, "Dist: ");
                LCD_ShowNum(6, 1, total_distance, 2);  // 2位小数
                break;
            case 1:  // 速度界面
                LCD_ShowString(0, 0, "Max Speed: 25.0km/h");
                LCD_ShowString(0, 1, "Avg Speed: 18.5km/h");
                break;
            case 2:  // 里程界面
                LCD_ShowString(0, 0, "Total: 123.4km");
                LCD_ShowString(0, 1, "Today: 25.6km");
                break;
            case 3:  // 环境界面
                LCD_ShowString(0, 0, "T:");
                LCD_ShowNum(2, 0, temperature/10, 1);  // 温度(整数部分)
                LCD_ShowString(0, 0, "C H:");
                LCD_ShowNum(6, 0, humidity/10, 1);    // 湿度(整数部分)
                LCD_ShowString(0, 1, "Light: ");
                LCD_ShowNum(6, 1, light, 0);         // 光照度
                break;
            case 4:  // 设置界面
                LCD_ShowString(0, 0, "Wheel: ");
                LCD_ShowNum(6, 0, WHEEL_CIRCUMFERENCE, 0);  // 车轮周长
                LCD_ShowString(0, 1, "mm");
                break;
        }
        
        // 3. 进入低功耗模式LPM3(仅ACLK运行,等待中断)
        __bis_SR_register(LPM3_bits + GIE);
        __no_operation();  // 唤醒后空操作
    }
}

参考代码 基于MSP430的自行车户外运动系统 www.youwenfan.com/contentcns/133177.html

三、系统测试与优化

3.1 功能测试

3.2 低功耗优化

四、总结

实现了基于MSP430G2553的自行车户外运动系统,通过干簧管测速、DHT22/BH1750环境监测、12864液晶显示及低功耗设计,满足骑行者对速度、里程、环境参数的实时监测需求。系统采用中断驱动+模块化编程,代码结构清晰,可扩展性强(如添加GPS、蓝牙功能)。

 

专注于matlab/simulink,电子电路,编程