利用stm32实现电位器电压采集,看门狗,有低功耗模式,采集数据通过can总线发送,在canable上观测采集到的数据。

记录一下我的车载嵌入式学习过程
1.利用stm32采集电压数据,使用can协议发送,之后使用pcan在pc端查看采集到的数据。

下图是在电脑上查看采集到的电压数据

 

注意要设置好,波特率,确保烧录进单片机的波特率和软件中的波特率保持一致,不然无法显示,因为波特率设置不一致的时候,pcan分析仪会导致

  • 帧起始(SOF)或帧结束(EOF)识别错误。

  • CRC校验失败,接收方丢弃数据帧。

  • 位填充错误,触发错误帧(Error Frame),总线进入错误恢复状态

所以没有数据显示 

 2.滑动滤波算法,保持电压稳定

/* ADC滤波算法(优化初始化) */
uint16_t ADC_GetValue(void) {
    static uint16_t buffer[8] = {0};
    static uint8_t index = 0;
    static uint32_t sum = 0;
    static uint8_t initialized = 0;
    
    uint16_t raw = ADC_GetConversionValue(ADC1);
    
    /* 首次运行初始化缓冲区 */
    if(!initialized) {
        for(uint8_t i=0; i<8; i++) {
            buffer[i] = raw;
            sum += raw;
        }
        initialized = 1;
    }
    
    sum -= buffer[index];
    buffer[index] = raw;
    sum += raw;
    index = (index + 1) % 8;
    return sum / 8;
}

 3.使用看门狗检测程序是否卡死,如果卡死程序复位

4.当电压低于0.8V的时候可以进入低功耗模式,不过有个小问题没有解决,就是进入低功耗模式后任何中断源都可以唤醒系统,系统进入低功耗模式只能进入一秒,然后就被唤醒,需要解决,我明天再继续修改。

这是完整的程序,后续会接着改。

#include "stm32f10x.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* 硬件配置 */
#define LED_PIN         GPIO_Pin_13
#define LED_PORT        GPIOC
#define CAN_ID_ADC      0x123
#define LOW_POWER_THRESHOLD 1000  // ADC阈值约0.8V
#define OLED_LINE_ADC   1          // ADC值显示行
#define OLED_LINE_VOLT  2          // 电压值显示行

/* 校准参数(需实际测量) */
#define VREF_MEASURED   3.28f      // 实测VDDA电压(用万用表测量)
#define ADC_MAX         4095       // ADC满量程值

/* OLED显示函数声明 */
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

/* 全局变量 */
volatile uint32_t sysTick = 0;      // 系统滴答计数器

/* 函数原型 */
void Delay_ms(uint32_t ms);
void Hardware_Init(void);
uint16_t ADC_GetValue(void);
float ADC_ToVoltage(uint16_t adcVal);
void CAN_SendFrame(uint32_t id, uint8_t* data, uint8_t len);
void Enter_LowPower_Mode(void);

/* 系统滴答中断 */
void SysTick_Handler(void) {
    sysTick++;
}

/* 获取系统时间戳 */
uint32_t GetTick(void) {
    return sysTick;
}

int main(void) {
    Hardware_Init();
    uint32_t lastADC = 0;
    uint32_t lastRefresh = 0;
    
    /* OLED显示初始化 */
    OLED_Clear();
    OLED_ShowString(OLED_LINE_ADC, 1, "ADC:");
    OLED_ShowString(OLED_LINE_VOLT, 1, "VOLT:");
    GPIO_SetBits(GPIOA, GPIO_Pin_2);
    while(1) {
        /* 1秒读取ADC并发送CAN */
		
        if(GetTick() - lastADC >= 5000) {
            lastADC = GetTick();
            uint16_t adcVal = ADC_GetValue();
            uint8_t data[2] = {adcVal & 0xFF, (adcVal >> 8) & 0xFF};
            CAN_SendFrame(CAN_ID_ADC, data, 2);
            
            if(adcVal < LOW_POWER_THRESHOLD) {
                Enter_LowPower_Mode();
				Delay_ms(5000);
            }
        }
        
        /* 200ms刷新OLED */
        if(GetTick() - lastRefresh >= 200) {
            lastRefresh = GetTick();
            uint16_t adcVal = ADC_GetValue();
            float voltage = ADC_ToVoltage(adcVal);
            
            /* 显示ADC值和电压 */
            OLED_ShowNum(OLED_LINE_ADC, 6, adcVal, 4);
            
            char voltStr[16];
            sprintf(voltStr, "%.2fV", voltage);
            OLED_ShowString(OLED_LINE_VOLT, 6, voltStr);
        }
        
        /* 喂狗 */
        IWDG_ReloadCounter();
    }
}

/* 硬件初始化 */
void Hardware_Init(void) {
    /* 系统时钟初始化 */
    SystemInit();
    SysTick_Config(SystemCoreClock / 1000);
    
    /* 启用外设时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | 
                          RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    
    /* LED初始化(PC13) */
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    /* ADC初始化(PA0) */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    ADC_InitTypeDef ADC_InitStruct;
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStruct.ADC_ScanConvMode = DISABLE;
    ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStruct);
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
    /* CAN初始化(PA11/RX, PA12/TX) */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    CAN_InitTypeDef CAN_InitStruct;
    CAN_StructInit(&CAN_InitStruct);
    CAN_InitStruct.CAN_Mode = CAN_Mode_Normal;
    CAN_InitStruct.CAN_SJW = CAN_SJW_1tq;
    CAN_InitStruct.CAN_BS1 = CAN_BS1_8tq;
    CAN_InitStruct.CAN_BS2 = CAN_BS2_7tq;
    CAN_InitStruct.CAN_Prescaler = 4;  // 500bps
    CAN_Init(CAN1, &CAN_InitStruct);
    
    /* 看门狗初始化(1秒超时) */
    RCC_LSICmd(ENABLE);
    while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
    IWDG_SetPrescaler(IWDG_Prescaler_32);
    IWDG_SetReload(1250);
    IWDG_ReloadCounter();
    IWDG_Enable();
    
    /* OLED初始化 */
    OLED_Init();
}

/* ADC滤波算法(优化初始化) */
uint16_t ADC_GetValue(void) {
    static uint16_t buffer[8] = {0};
    static uint8_t index = 0;
    static uint32_t sum = 0;
    static uint8_t initialized = 0;
    
    uint16_t raw = ADC_GetConversionValue(ADC1);
    
    /* 首次运行初始化缓冲区 */
    if(!initialized) {
        for(uint8_t i=0; i<8; i++) {
            buffer[i] = raw;
            sum += raw;
        }
        initialized = 1;
    }
    
    sum -= buffer[index];
    buffer[index] = raw;
    sum += raw;
    index = (index + 1) % 8;
    return sum / 8;
}

/* 精确电压计算(基于实测VREF) */
float ADC_ToVoltage(uint16_t adcVal) {
    return (adcVal * VREF_MEASURED) / ADC_MAX;
}

/* CAN数据发送 */
void CAN_SendFrame(uint32_t id, uint8_t* data, uint8_t len) {
    CanTxMsg txMsg;
    txMsg.StdId = id;
    txMsg.IDE = CAN_Id_Standard;
    txMsg.RTR = CAN_RTR_Data;
    txMsg.DLC = len;
    memcpy(txMsg.Data, data, len);
    CAN_Transmit(CAN1, &txMsg);
    while(CAN_TransmitStatus(CAN1, CAN_FIFO0) != CAN_TxStatus_Ok);
}

/* 进入低功耗模式 */
void Enter_LowPower_Mode(void) {
    GPIO_ResetBits(LED_PORT, 13);
	GPIO_ResetBits(GPIOA, GPIO_Pin_2);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, DISABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, DISABLE);
    PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
	Delay_ms(5000);
//    SystemInit();
//    SystemCoreClockUpdate();
//    Hardware_Init();
}

///* 简单延时函数 */
//void Delay_ms(uint32_t ms) {
//    uint32_t start = GetTick();
//    while(GetTick() - start < ms);
//}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值