BMS STM32 V4.0.0.0

This commit is contained in:
2026-08-25 17:30:40 +08:00
commit 3ae7bcb054
313 changed files with 115734 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
/**
******************************************************************************
* @file gpio.c
* @author Jerry Cai
* @version V2.1
* @date 19-April-2022
* @brief gpio program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
//【引脚】
//GPIOA
#define PIN_T1 GPIO_Pin_5
#define PIN_T2 GPIO_Pin_4
#define PIN_T3 GPIO_Pin_1
#define PIN_T4 GPIO_Pin_0
//GPIOC
#define PIN_LOAD_VOL GPIO_Pin_4
//【ADC通道】
#define CH_T1 ADC_Channel_5
#define CH_T2 ADC_Channel_4
#define CH_T3 ADC_Channel_1
#define CH_T4 ADC_Channel_0
#define CH_LOAD ADC_Channel_14
int16_t TemperatureAverage; // 平均温度
int16_t TemperatureMax; // 最高温度
int16_t TemperatureMin; // 最低温度
uint16_t TemperatureMaxIndex; // 最高温度序号
uint16_t TemperatureMinIndex; // 最低温度序号
uint32_t loadvol; //负载检测的电压
//ADC单通道单次转换
void uf_ADC_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//PC4 负载电压检测脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = PIN_LOAD_VOL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//PA0.1.4.5 T1~T4输入引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = PIN_T1 | PIN_T2 | PIN_T3 | PIN_T4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6,ADC最大时间不能超过14M
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
//指定通道ADC值
uint16_t ADC_GetVal(uint8_t ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
//负载电压检测
void LOAD_VOL(void)
{
uint16_t adcvol;
adcvol = ADC_GetVal(CH_LOAD);
loadvol = adcvol * 40 * 3300 / 4095; //将AD值线性变换到0~3.3V的范围,表示电压
}
//温度处理
void MCU_TemperaProcess(void)
{
uint16_t adctmp[4]; //adc read value
uint16_t resntc[4]; //res calc value
uint16_t mcutmp[4];
uint16_t T[4]; //4路MCU
uint8_t i;
//获取ADC值
adctmp[0] = ADC_GetVal(CH_T1);
adctmp[1] = ADC_GetVal(CH_T2);
adctmp[2] = ADC_GetVal(CH_T3);
adctmp[3] = ADC_GetVal(CH_T4);
resntc[0] = (1000 * adctmp[0]) / (4096-adctmp[0]);
resntc[1] = (1000 * adctmp[1]) / (4096-adctmp[1]);
resntc[2] = (1000 * adctmp[2]) / (4096-adctmp[2]);
resntc[3] = (1000 * adctmp[3]) / (4096-adctmp[3]);
mcutmp[0] = TEMP_Cal(resntc[0]);
mcutmp[1] = TEMP_Cal(resntc[1]);
mcutmp[2] = TEMP_Cal(resntc[2]);
mcutmp[3] = TEMP_Cal(resntc[3]);
bmsMem.mcu_T1 = mcutmp[0];
bmsMem.mcu_T2 = mcutmp[1];
bmsMem.mcu_T3 = mcutmp[2];
bmsMem.mcu_T4 = mcutmp[3];
//当至少有1个温度可用时
if((paraMem.temp_disable & 0x0F) != 0x0F)
{
uint8_t act_num = 0; //有效个数
if((paraMem.temp_disable & BIT0) == 0)
{
T[act_num] = bmsMem.mcu_T1;
act_num++;
}
if((paraMem.temp_disable & BIT1) == 0)
{
T[act_num] = bmsMem.mcu_T2;
act_num++;
}
if((paraMem.temp_disable & BIT2) == 0)
{
T[act_num] = bmsMem.mcu_T3;
act_num++;
}
if((paraMem.temp_disable & BIT3) == 0)
{
T[act_num] = bmsMem.mcu_T4;
act_num++;
}
//平均温度
TemperatureAverage = 0;
for(i=0;i<act_num;i++)
{
TemperatureAverage += T[i];
}
TemperatureAverage = TemperatureAverage/act_num;
//最高最低温度
TemperatureMax = T[0];
TemperatureMin = T[0];
TemperatureMaxIndex = 0;
TemperatureMinIndex = 0;
for(i=0;i<act_num;i++)
{
if(TemperatureMax < T[i])
{
TemperatureMax = T[i];
TemperatureMaxIndex = i;
}
if(TemperatureMin > T[i])
{
TemperatureMin = T[i];
TemperatureMinIndex = i;
}
}
}
//当4路温度都不可用时
else
{
TemperatureAverage = 0;
TemperatureMin = 0;
TemperatureMax = 0;
TemperatureMinIndex = 0;
TemperatureMaxIndex = 0;
}
//sum of all packs
bmsMem.can_temp = TemperatureAverage;
bmsMem.can_TempMax = TemperatureMax;
bmsMem.can_TempMaxIndex = TemperatureMaxIndex;
bmsMem.can_TempMin = TemperatureMin;
bmsMem.can_TempMinIndex = TemperatureMinIndex;
//当至少有1个温度可用时
if((paraMem.temp_disable & 0x0F) != 0x0F)
{
//电芯温度告警和告警释放
Trigger_mcuTAlarm(); //电芯温度告警
Release_mcuTAlarm(); //电芯温度告警恢复
//电芯温度保护和保护释放
Trigger_mcuTProtect(); //电芯温度保护相关
Release_mcuTProtect(); //电芯温度保护释放
#if DO2_Warm
//加热
WARM_Ctrl(); //电芯低温启动加热,温度升高释放
#endif
}
//当4路温度都不可用时
else
{
bmsMem.bStatus2 &= ~0xF000;
bmsMem.temperaStatus &= ~0x000F;
}
//电流告警和告警释放
Trigger_CurAlarm(); //电流告警
Release_CurAlarm(); //电流告警恢复
//电流保护和保护释放
Trigger_CurProtect(); //电流保护
Release_CurProtect(); //电流保护释放
/*电流保护连续出现的计算*/
Trigger_CurProtectLock();
}
+213
View File
@@ -0,0 +1,213 @@
/**
******************************************************************************
* @file tim.c
* @author Jerry
* @version V2.1
* @date 19-April-2022
* @brief tim program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
#define CAN_MON_CNT 1000 //1000*10ms = 10s
CanTxMsg TxMessage[20];
CanRxMsg RxMessage;
uint8_t TxMailBox[20];
uint8_t CAN_SendCount;
uint16_t CAN_MoniCount;
CAN_MEMORY canMem[AddrMax+2];
//HSE --> PLL倍频到72M --> APB1 2分频到36M
void uf_CAN1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
//Config CAN pin : RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Config CAN pin : TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//NVIC
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //CAN通信 优先级:1,0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//彻底复位CAN外设:先禁用,再重新使能,确保完全复位
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, DISABLE);
delay_ms(1);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
//控制器配置
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
CAN_InitStructure.CAN_TTCM = DISABLE; //时间触发通信模式
CAN_InitStructure.CAN_ABOM = DISABLE; //自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //自动唤醒
CAN_InitStructure.CAN_NART = ENABLE; //自动重传 改启用
CAN_InitStructure.CAN_RFLM = DISABLE; //FIFO锁定
CAN_InitStructure.CAN_TXFP = DISABLE; //发送FIFO优先级
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //普通模式
//波特率配置
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
if(protocol == 7) //MUST协议的波特率是100k
{
CAN_InitStructure.CAN_Prescaler = 40; //36MHz/40/(1+6+2)=100kbs
}
else //其他协议是500k
{
CAN_InitStructure.CAN_Prescaler = 8; //36MHz/8/(1+6+2)=500kbs
}
CAN_Init(CAN1, &CAN_InitStructure);
//过滤器配置
CAN_FilterInitStructure.CAN_FilterNumber = 0; //选择过滤器0
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //标识符屏蔽模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //过滤器位宽32位
CAN_FilterInitStructure.CAN_FilterIdHigh = 0;
CAN_FilterInitStructure.CAN_FilterIdLow = 0;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FilterFIFO0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
//中断配置
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0中有消息允许中断
//清除所有可能的错误标志
CAN1->ESR = 0; // 清除错误状态寄存器
CAN1->MSR &= ~(CAN_MSR_ERRI); // 清除错误中断标志
//更新倒计时数
CAN_MoniCount = CAN_MON_CNT;
}
void USB_LP_CAN1_RX0_IRQHandler(void)
{
//处理接收中断
if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)
{
//接收报文
CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);
if(RxMessage.StdId == 0x305)
{
RxMessage.StdId = 0;
CAN_MoniCount = CAN_MON_CNT;
if(sleep_flag == 1)
{
//CAN网口收到数据,退出休眠且更新计时起点
sleep_flag = 0;
SLEEP_Refresh();
SLEEP2_Refresh();
}
}
}
}
void CAN_TIM_Moni(void)
{
CAN_MoniCount--;
if(CAN_MoniCount == 0)
{
uf_CAN1_Init();
}
}
/*
1."Sol-Ark", 2."GoodWe", 30."Megarevo", 12."Pylon",
11."Deye", 7."MUST", 37."solis", 3."Growatt",
4."Aiswei", 35."Afore", 27."Victron", 6."Sorotec",
5."SMA", 39."Sunways", 23."Luxpower", 24."Schneider",
40."AlpSolarr", 13."SRNE", 14."Voltronic", 32."COSUPER",
17."SMK", 31."SAKO", 18."SNADI", 21."invt",
*/
void CAN_UpdateData(void)
{
//第1页:
if(protocol == 1) CAN_Protocol_SolArk();
// else if(protocol == 2) CAN_Protocol_GoodWe();
// else if(protocol == 30) CAN_Protocol_Megarevo();
else if(protocol == 12) CAN_Protocol_Pylon();
else if(protocol == 11) CAN_Protocol_Deye();
// else if(protocol == 7) CAN_Protocol_MUST();
else if(protocol == 37) CAN_Protocol_solis();
else if(protocol == 3) CAN_Protocol_Growatt();
// else if(protocol == 4) CAN_Protocol_Aiswei();
// else if(protocol == 35) CAN_Protocol_Afore();
// else if(protocol == 27) CAN_Protocol_Victron();
// else if(protocol == 6) CAN_Protocol_Sorotec();
//第2页:
// else if(protocol == 5) CAN_Protocol_SMA();
// else if(protocol == 39) CAN_Protocol_Sunways();
// else if(protocol == 23) CAN_Protocol_Luxpower();
// else if(protocol == 24) CAN_Protocol_Schneider();
// else if(protocol == 40) CAN_Protocol_AlpSolarr();
}
//CAN报文发送函数
void CAN1_SendData(uint32_t Id, uint8_t *data)
{
uint16_t can_timeout;
can_timeout = CAN_TIMEOUT_COUNT;
if(Id < 0x1000)
{
TxMessage[CAN_SendCount].StdId = Id; //ID
TxMessage[CAN_SendCount].IDE = CAN_ID_STD; //标准ID
}
else
{
TxMessage[CAN_SendCount].ExtId = Id; //ID
TxMessage[CAN_SendCount].IDE = CAN_ID_EXT; //扩展ID
}
TxMessage[CAN_SendCount].RTR = CAN_RTR_DATA; //数据帧
TxMessage[CAN_SendCount].DLC = 8;
TxMessage[CAN_SendCount].Data[0] = data[0];
TxMessage[CAN_SendCount].Data[1] = data[1];
TxMessage[CAN_SendCount].Data[2] = data[2];
TxMessage[CAN_SendCount].Data[3] = data[3];
TxMessage[CAN_SendCount].Data[4] = data[4];
TxMessage[CAN_SendCount].Data[5] = data[5];
TxMessage[CAN_SendCount].Data[6] = data[6];
TxMessage[CAN_SendCount].Data[7] = data[7];
TxMailBox[CAN_SendCount] = CAN_Transmit(CAN1, &TxMessage[CAN_SendCount]); //发送,返回当前邮箱号
while(CAN_TransmitStatus(CAN1,TxMailBox[CAN_SendCount]) != CANTXOK) //等待发送完成
{
if((can_timeout--) == 0) return;
}
CAN_SendCount++;
}
+1222
View File
File diff suppressed because it is too large Load Diff
+1053
View File
File diff suppressed because it is too large Load Diff
+531
View File
@@ -0,0 +1,531 @@
/**
******************************************************************************
* @file tim.c
* @author Jerry
* @version V2.1
* @date 22-April-2022
* @brief tim program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
#include "string.h"
#include "sys.h"
#include "soe.h"
//64-byte page write buffer
//1,000,000 program/erase cycles
//100 year data retention
//AT24C256 32K Bytes = 128 * 256 bytes
//0x0000 不可初始化数据
//0x0200 厂内可初始化数据
//0x0400 升级初始化数据
//0x0800 报警记录数据
#define I2C_EEPROM I2C1
#define I2C_AFE I2C1
#define DEVICE_ID_EEPROM 0xA0
#define DEVICE_ID_AFE 0x34
#define I2C_TIMEOUT_COUNT 10000
#define FLASH_PAGE_ADDR 0x0800FC00 //要擦除的FLASH页地址
#define JUMP_TO_USER 0X20230612 //用户固件更新标记
#define JUMP_BUTNULL 0XFFFFFFFF //无更新标记
uint8_t IAP_Run; //执行程序时是否正常的标志
uint8_t DL_Index; //跳转位置的标识
uint32_t DL_Addr; //根据标识计算出的位置
uint32_t DL_Jump;
void uf_I2C1_Init(void)
{
uint8_t tmp[8];
uint32_t ee_index;
uint16_t ee_pc;
uint16_t ee_num;
/*初始化IIC*/
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
/* Configure I2C1 pins: PB6->SCL and PB7->SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C1);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 80000;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
I2C_AcknowledgeConfig(I2C1, ENABLE);
/*IAP标志*/
IAP_Run = 0x55;
EEPROM_WrMulByte(EE_IAP_NEW1,&IAP_Run);
delay_ms(10);
EEPROM_WrMulByte(EE_IAP_NEW2,&IAP_Run);
delay_ms(10);
/*为兼容此前底层,写入标识*/
EEPROM_RdMulByte(0,1,1,&DL_Index);
if((DL_Index != 0) && (DL_Index != 1)) //之前未刷过程序
{
DL_Index = 1; //现在一般是1
}
//读出检查,确认是这个位置并且无用户数据/跳转标志,才进行写入
DL_Addr = FLASH_PAGE_ADDR + 0x10000 * DL_Index;
FLASH_RdWord(DL_Addr, &DL_Jump, 1);
if(DL_Jump == JUMP_BUTNULL)
{
DL_Jump = JUMP_TO_USER;
FLASH_WrData(DL_Addr,(uint16_t *)&DL_Jump,8);
}
//若该位置有值,且不是跳转标志,跳转到另一个位置检查并写入
else if(DL_Jump != JUMP_TO_USER)
{
DL_Index = (DL_Index==0) ? 1:0; //取另一个地址查询,若仍然不对,那该用户程序不依靠IAP底层
DL_Addr = FLASH_PAGE_ADDR + 0x10000 * DL_Index;
FLASH_RdWord(DL_Addr, &DL_Jump, 1);
if(DL_Jump == JUMP_BUTNULL)
{
DL_Jump = JUMP_TO_USER;
FLASH_WrData(DL_Addr,(uint16_t *)&DL_Jump,8);
}
}
#if LTE_Conn
//上电读OTA升级回复标志
EEPROM_RdMulByte(EE_OTA_FINE,&tmp[0]);
if((tmp[0] == 0xAA) || (tmp[0] == 0xBB))
{
LTE_OTA_fineFlag = tmp[0];
}
else
{
LTE_OTA_fineFlag = 0;
}
#endif
/*EEPROM无值,赋默认值,但不主动写入EEPROM*/
//上电读485地址(先暂时获得一个值,之后根据paraMem参数来决定是否改变)
EEPROM_RdMulByte(EE_ADDR,&tmp[0]);
if((tmp[0]>=1) && (tmp[0]<=AddrMax))
{
bmsMem.E2_485Addr = tmp[0];
}
else
{
bmsMem.E2_485Addr = 2;
}
// //上电读屏幕语言
// EEPROM_RdMulByte(EE_LANG,&tmp[0]);
// if((tmp[0]==0) || (tmp[0]==1)) //0对应英文,1对应中文
// {
// language = tmp[0];
// }
// else
// {
// language = 0; //默认英文
// }
#if Addr_SetAuto
uint16_t random;
//上电读自动分配地址的随机队列标志
EEPROM_RdMulByte(EE_ASSIGN,&tmp[0]);
random = tmp[0]<<8 | tmp[1];
if((random>AddrMax) && (random<0xffff)) //AddrMax+1~65534
{
bmsMem.can_ArrayIndex = random;
}
else
{
bmsMem.can_ArrayIndex = 0;
}
#endif
//上电读是否需要充电校准总容量
EEPROM_RdMulByte(EE_FCC_TIME,&tmp[0]);
fcc_Calitimecount = tmp[0]<<24 | tmp[1]<<16 | tmp[2]<<8 | tmp[3];
if(fcc_Calitimecount <= timecount) //存的数据不算异常
{
fcc_CaliStartFlag = 1; //记录了起始时间,说明正在计时等满充
}
#if LTE_Conn
//上电读取消绑定标志
EEPROM_RdMulByte(EE_UNSUB,&tmp[0]);
if(tmp[0] <= 1)
{
LTE_UNSUB_Flag = tmp[0];
}
else
{
LTE_UNSUB_Flag = 0;
}
#endif
/*上电读取记录相关信息*/
EEPROM_RdMulByte(EE_SOE_INF,tmp);
ee_index = tmp[0]<<24 | tmp[1]<<16 | tmp[2]<<8 | tmp[3];
ee_pc = tmp[4]<<8 | tmp[5];
ee_num = tmp[6]<<8 | tmp[7];
//当前地址=0或0XFFFF或不为64倍数,初始化地址和记录序号
if((ee_pc < 0x1000) || (ee_pc > 0x2940) || (ee_pc == 0xffff) || (ee_pc%64 !=0))
{
soe.pc = RECORD_START_ADDR;
soe.index = 0;
soe.num = 0;
}
else
{
soe.index = ee_index;
soe.pc = ee_pc;
soe.num = ee_num;
}
}
//EEPROM写多字节,注意写入时不要跨page
uint8_t EEPROM_WrMulByte(uint8_t addrH, uint8_t addrL, uint8_t lenth, uint8_t *data)
{
uint8_t i;
uint16_t i2c_timeout;
//I2C总线BUSY
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_GetFlagStatus(I2C_EEPROM,I2C_FLAG_BUSY) == SET)
{
if((i2c_timeout--) == 0) return 9;
}
/*起始位*/
I2C_GenerateSTART(I2C_EEPROM, ENABLE);
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS) //EV5
{
if((i2c_timeout--) == 0) return 1;
}
/*EV5事件检测到,发送Device ID(写)*/
I2C_Send7bitAddress(I2C_EEPROM, DEVICE_ID_EEPROM, I2C_Direction_Transmitter);
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS) //EV6
{
if((i2c_timeout--) == 0) return 2;
}
/*EV6事件检测到,发送EEPROM 存储单元地址*/
//检测EV8,表示发送寄存器空了就可以继续填数据了,无需等待移位寄存器空
I2C_SendData(I2C_EEPROM, addrH);
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS)
{
if((i2c_timeout--) == 0) return 3;
}
/*发送EEPROM 存储单元地址*/
I2C_SendData(I2C_EEPROM, addrL);
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS)
{
if((i2c_timeout--) == 0) return 4;
}
/*发送写入EERPOM数据*/
for(i=0;i<lenth-1;i++)
{
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_SendData(I2C_EEPROM, *data++);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS)
{
if((i2c_timeout--) == 0) return 5;
}
}
//最后一个数据EV8_2
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_SendData(I2C_EEPROM, *data++);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 5;
}
/*停止位*/
I2C_GenerateSTOP(I2C_EEPROM, ENABLE);
return 0;
}
//EEPROM随机读多字节
uint8_t EEPROM_RdMulByte(uint8_t addrH, uint8_t addrL, uint8_t lenth, uint8_t *data)
{
uint8_t i;
uint16_t i2c_timeout;
//I2C总线BUSY
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_GetFlagStatus(I2C_EEPROM,I2C_FLAG_BUSY) == SET)
{
if((i2c_timeout--) == 0) return 9;
}
//ACK
I2C_AcknowledgeConfig(I2C_EEPROM, ENABLE);
/*起始位*/
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_GenerateSTART(I2C_EEPROM, ENABLE);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS) //EV5
{
if((i2c_timeout--) == 0) return 1;
}
/*发送Device ID(写)*/
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_Send7bitAddress(I2C_EEPROM, DEVICE_ID_EEPROM, I2C_Direction_Transmitter);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 2;
}
/*发送EEPROM 存储单元地址*/
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_SendData(I2C_EEPROM, addrH);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 3;
}
/*发送EEPROM 存储单元地址*/
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_SendData(I2C_EEPROM, addrL);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 4;
}
/*起始位*/
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_GenerateSTART(I2C_EEPROM, ENABLE);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_MODE_SELECT)!= SUCCESS)
{
if((i2c_timeout--) == 0) return 5;
}
/*发送DEVICE(读)*/
i2c_timeout = I2C_TIMEOUT_COUNT;
I2C_Send7bitAddress(I2C_EEPROM, DEVICE_ID_EEPROM, I2C_Direction_Receiver);
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 6;
}
/*读lenth长度数据*/
for(i=0;i<lenth-1;i++)
{
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 7;
}
*data++ = I2C_ReceiveData(I2C_EEPROM);
}
//NACK
I2C_AcknowledgeConfig(I2C_EEPROM, DISABLE);
//我认为ACK disable因该放在接收数据之前,这样接到数据后可以马上发NACK信号
i2c_timeout = I2C_TIMEOUT_COUNT;
while(I2C_CheckEvent(I2C_EEPROM, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
{
if((i2c_timeout--) == 0) return 8;
}
*data++ = I2C_ReceiveData(I2C_EEPROM);
/*停止位*/
I2C_GenerateSTOP(I2C_EEPROM, ENABLE);
return 0;
}
//AFE通信函数(AFE_WriteOneByte/AFE_ReadMulByte)已移至BSP/spi.c,使用SPI2与SH3673520通信
//write zero cali data to eeprom
//read from eeprom and check
uint8_t EEPROM_CALI_WrZero(int16_t data)
{
uint8_t tempW[4];
uint8_t tempR[4];
uint8_t i;
tempW[0] = (data >>8) & 0xff;
tempW[1] = data & 0xff;
tempW[2] = tempW[0] ^ 0xff;
tempW[3] = tempW[1] ^ 0xff;
if(EEPROM_WrMulByte(EE_CALI_ZERO,tempW) !=0)
{
return 1; //iic write error
}
delay_ms(20); //are there?
if(EEPROM_RdMulByte(EE_CALI_ZERO,tempR) !=0)
{
return 2; //iic read error
}
for(i=0;i<4;i++)
{
if(tempR[i] != tempW[i])
{
return 3; //check error
}
}
return 0;
}
//write gain cali data to eeprom
uint8_t EEPROM_CALI_WrGain(int16_t data)
{
uint8_t tempW[4];
uint8_t tempR[4];
uint8_t i;
tempW[0] = (data >>8) & 0xff;
tempW[1] = data & 0xff;
tempW[2] = tempW[0] ^ 0xff;
tempW[3] = tempW[1] ^ 0xff;
if(EEPROM_WrMulByte(EE_CALI_GAIN,tempW) !=0)
{
return 1;
}
delay_ms(20);
if(EEPROM_RdMulByte(EE_CALI_GAIN,tempR) !=0)
{
return 2; //iic read error
}
for(i=0;i<4;i++)
{
if(tempR[i] != tempW[i])
{
return 3; //check error
}
}
return 0;
}
int16_t EEPROM_CALI_RdZero(void)
{
uint8_t i;
uint8_t tempR[4];
int16_t result;
EEPROM_RdMulByte(EE_CALI_ZERO,tempR);
if(((tempR[0] ^ 0xff) == tempR[2]) && ((tempR[1] ^ 0xff) == tempR[3]))
{
result = tempR[0] << 8 | tempR[1];
return result;
}
else
{
EEPROM_RdMulByte(2,0,4,tempR); //读取旧地址的数据
if(((tempR[0] ^ 0xff) == tempR[2]) && ((tempR[1] ^ 0xff) == tempR[3]))
{
//符合存储格式,说明之前校准值保存在旧地址,赋值到新地址,并清除
EEPROM_WrMulByte(EE_CALI_ZERO,tempR);
delay_ms(5);
result = tempR[0] << 8 | tempR[1];
//为了不影响现在在旧地址的数据,将这部分清空
for(i=0;i<4;i++)
{
tempR[i] = 0xff;
}
EEPROM_WrMulByte(2,0,4,tempR);
delay_ms(5);
EEPROM_WrMulByte(2,4,4,tempR);
delay_ms(5);
}
else
{
result = 0;
}
return result;
}
}
int16_t EEPROM_CALI_RdGain(void)
{
uint8_t i;
uint8_t tempR[4];
int16_t result;
EEPROM_RdMulByte(EE_CALI_GAIN,tempR);
if(((tempR[0] ^ 0xff) == tempR[2]) && ((tempR[1] ^ 0xff) == tempR[3]))
{
result = tempR[0] << 8 | tempR[1];
return result;
}
else
{
EEPROM_RdMulByte(3,0,4,tempR); //读取旧地址的数据
if(((tempR[0] ^ 0xff) == tempR[2]) && ((tempR[1] ^ 0xff) == tempR[3]))
{
//符合存储格式,说明之前校准值保存在旧地址,赋值到新地址,并清除
EEPROM_WrMulByte(EE_CALI_GAIN,tempR);
delay_ms(5);
result = tempR[0] << 8 | tempR[1];
//为了不影响现在在旧地址的数据,将这部分清空
for(i=0;i<4;i++)
{
tempR[i] = 0xff;
}
EEPROM_WrMulByte(3,0,4,tempR);
delay_ms(5);
EEPROM_WrMulByte(3,4,4,tempR);
delay_ms(5);
}
else
{
result = 10000;
}
return result;
}
}
+269
View File
@@ -0,0 +1,269 @@
/**
******************************************************************************
* @file pwm.c
* @author
* @version
* @date
* @brief
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
//GPIOA
#define PIN_CHG_LIMIT_PON GPIO_Pin_8 //限流 电源控制管脚
//GPIOB
#define PIN_CHG_LIMIT_PWM GPIO_Pin_9 //限流 电流值控制
#define BIAS_VOLTAGE (200 * bmsMem.ucCellNum/16) //基准偏差电压值,单位1mV
#define BASE_VOLTAGE (60000 * bmsMem.ucCellNum/16) //基准电压值,单位1mV
#define BASE_CURRENT 10000 //基准/目标电流值10A,单位1mA
#define MAX_DUTY 99.00f //最大占空比(百分比)
#define MIN_DUTY 50.00f //最小占空比(百分比)
#define PWM_ARR 3599 //定时器自动重装值(对应0~100%占空比)
#define PWM_PSC 0 //预分频系数(PWM频率=72M/(0+1)/(3599+1) = 20kHz
uint8_t curLimit_ctrlFlag; //执行限流开/关的标志 0:关限流 1:开限流
float base_duty; //初始电流值偏小最好,对应假设充电器电压是最大值60V
float old_duty; //有效的上一次调整的占空比
float duty_cycle; //实时基准占空比
//开限流
void CHG_LIMIT_On(void)
{
if(curLimit_ctrlFlag != 1)
{
curLimit_ctrlFlag = 1;
delay_ms(10); //关充电MOS后,再延时开限流
//计算初始占空比
base_duty = (float)(bmsMem.packVoltage + BIAS_VOLTAGE) / BASE_VOLTAGE * 100;
base_duty = (int)(base_duty * 100 + 0.5) / 100.0f;
if(base_duty < MIN_DUTY)
{
base_duty = MIN_DUTY;
}
else if(base_duty > MAX_DUTY)
{
base_duty = MAX_DUTY;
}
//限流功能开启
GPIO_SetBits(GPIOA, PIN_CHG_LIMIT_PON);
//PWM占空比为默认值
duty_cycle = base_duty;
old_duty = duty_cycle;
PWM_Set_Duty_Percent(duty_cycle);
}
}
//关限流
void CHG_LIMIT_Off(void)
{
if(curLimit_ctrlFlag != 0)
{
curLimit_ctrlFlag = 0;
//限流功能关闭
GPIO_ResetBits(GPIOA, PIN_CHG_LIMIT_PON);
//PWM占空比为0%
duty_cycle = 0.00f;
old_duty = duty_cycle;
PWM_Set_Duty_Percent(duty_cycle);
delay_ms(10); //关限流后,延时开充电MOS
}
}
//限流控制脚和PWM脚的初始化
void CHG_LIMIT_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//限流充电控制
GPIO_InitStructure.GPIO_Pin = PIN_CHG_LIMIT_PON;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, PIN_CHG_LIMIT_PON); //限流控制脚,默认关闭状态
//限流PWM波初始化
TIM4_PWM_Init(PWM_ARR, PWM_PSC);
}
/**************************************************************************
** 初始化TIM4_CH1PA8)的PWM输出
** arr: 定时器自动重装值
** psc: 定时器预分频系数
***************************************************************************/
void TIM4_PWM_Init(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 开启外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); // TIM4+GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); // 复用功能时钟(必要)
// 配置PB9为复用推挽输出(PWM必须用复用模式)
GPIO_InitStructure.GPIO_Pin = PIN_CHG_LIMIT_PWM;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置TIM4时基参数
TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割(无分频)
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
// 配置TIM4_CH1的PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1CNT<CCR时输出高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能通道输出
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比0CCR值)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 有效电平为高
TIM_OC4Init(TIM4, &TIM_OCInitStructure); // 初始化通道4
// 使能预装载寄存器(保证参数修改生效)
TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); // 通道4预装载使能
TIM_ARRPreloadConfig(TIM4, ENABLE); // TIM4自动重装预装载使能
// 开启主输出(否则无PWM波形)
TIM_CtrlPWMOutputs(TIM4, ENABLE);
// 启动TIM4
TIM_Cmd(TIM4, ENABLE);
}
/*************************************************************************************************
* 函数名: PWM_Set_Duty_Percent
* 参 数: 无
* 返回值: 无
* 描 述:
*************************************************************************************************/
void PWM_Set_Duty_Percent(float duty_per)
{
uint16_t ccr_val;
//限制百分比范围
if (duty_per < 0.00f) duty_per = 0.00f;
if (duty_per > 100.00f) duty_per = 100.00f;
//转换为定时器CCR值(四舍五入,提升精度)
ccr_val = (u16)(duty_per / 100.00f * PWM_ARR + 0.50f);
//设置CCR值
TIM_SetCompare4(TIM4, ccr_val);
}
/*************************************************************************************************
* 函数名: CHG_LIMIT_PWM_Adjust
* 参 数: 无
* 返回值: 无
* 描 述: pwm限流,根据电流来计算占空比,53.4V 10A时占空比为93.0
*************************************************************************************************/
void CHG_LIMIT_PWM_Adjust(void)
{
int16_t cur_diff = bmsMem.packCurrent - BASE_CURRENT;
//根据电流差值调整占空比,变化越大电流波动越大
if(cur_diff >= 9000)
{
//电流超过目标9A以上,快速减小占空比(步长5.00,因为大电流有风险)
duty_cycle -= 5.00f;
}
else if(cur_diff >= 5000)
{
//电流超过目标5A以上,快速减小占空比(步长1.50,因为大电流有风险)
duty_cycle -= 1.50f;
}
else if(cur_diff >= 2000)
{
//电流超过目标2A以上,中速减小占空比(步长0.30)
duty_cycle -= 0.30f;
}
else if(cur_diff >= 1000)
{
//电流超过目标1A以上,中速减小占空比(步长0.20)
duty_cycle -= 0.20f;
}
else if(cur_diff >= 500)
{
//电流超过目标0.5A以上,慢速减小占空比(步长0.05)
duty_cycle -= 0.05f;
}
else if(cur_diff >= 100)
{
//电流超过目标0.5A以上,慢速减小占空比(步长0.01)
duty_cycle -= 0.01f;
}
else if(cur_diff <= -9000)
{
//电流低于目标9A以上,快速增大占空比(步长2.00)
duty_cycle += 2.00f;
}
else if(cur_diff <= -5000)
{
//电流低于目标5A以上,快速增大占空比(步长1.00)
duty_cycle += 1.00f;
}
else if(cur_diff <= -2000)
{
//电流低于目标3A以上,中速增大占空比(步长0.30)
duty_cycle += 0.30f;
}
else if(cur_diff <= -1000)
{
//电流低于目标1A以上,中速增大占空比(步长0.20)
duty_cycle += 0.20f;
}
else if(cur_diff <= -500)
{
//电流低于目标0.5A以上,慢速增大占空比(步长0.05)
duty_cycle += 0.05f;
}
else if(cur_diff <= -100)
{
//电流低于目标0.5A以上,慢速增大占空比(步长0.01)
duty_cycle += 0.01f;
}
else
{
//电流差在±500mA(0.5A)以内,占空比保持不变
duty_cycle = old_duty;
}
//限制占空比范围
if(duty_cycle < MIN_DUTY)
{
duty_cycle = MIN_DUTY;
}
else if(duty_cycle > MAX_DUTY)
{
duty_cycle = MAX_DUTY;
}
old_duty = duty_cycle;
PWM_Set_Duty_Percent(duty_cycle);
}
+793
View File
@@ -0,0 +1,793 @@
/**
******************************************************************************
* @file gpio.c
* @author Jerry Cai
* @version V2.1
* @date 19-April-2022
* @brief gpio program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "rtc.h"
#include "global.h"
#define sleepEnd 60*(paraMem.sleep_min_disable&0x7FFF) //休眠1等待时长min,单位1s
#define sleep2End 60*(paraMem.sleep2_min_disable&0x7FFF) //休眠2等待时长min,单位1s
#define sleep2Vol paraMem.sleep2_vol //休眠方案2对应休眠电压,单位1mV
#define uvoffEnd 300 //强制欠压复位等待时长5min,单位1s
#define ocvEnd 60*(paraMem.ocv_min_disable&0x7FFF) //开路电压校准等待时长min,单位1s
#define fcccaliEnd 60*(paraMem.cali_min_disable&0x7FFF) //校准满充容量等待时长min,单位1s
_calendar_obj calendar; //时钟结构体
_calendar_obj calendar_WRITE;
_calendar_obj calendar_BACKUP;
uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //平年的月修正数据表(基准是1900年1月1日),闰年其他相同只是1.2月要-1
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31}; //平年的月份日期表.闰年的2月有29天
uint32_t timecount; //当前计时s
uint32_t oldtimecnt; //上一秒的计时s
uint8_t LSEErrFlag; //外部低频晶振有问题的标志,需要让屏幕的时间不再显示
uint8_t LSEErrCount;//时间不走的计数,满足了会把时间清零,标志置1
uint8_t sleep_flag; //休眠执行标志位
uint8_t sleep_enableflag;//开启休眠功能标志位
uint32_t sleeptimecount; //休眠1的计时起点s
uint32_t sleeptime; //休眠启动的倒计时数
uint32_t sleep2timecount; //休眠2的计时起点s
uint32_t sleep2time; //休眠2启动的倒计时数
uint32_t uvofftimecount; //关闭欠压功能后的计时起点
uint16_t uvofftime; //关闭欠压功能的倒计时数(会在屏幕显示故只设uint16)
uint32_t ocvtimecount; //开路电压法的计时起点
uint32_t ocvtime; //开路电压法的倒计时数
uint32_t fcc_Calitimecount; //校准满充容量的计时起点
uint32_t fcc_Calitime; //校准满充容量的倒计时数,超过则不可更新
uint8_t RTC_UpdateFlag; //起始点刷新标志位,用以刷新休眠和欠压强制复位的时间
/*
* 函数名:Is_Leap_Year
* 描述 :判断是否为闰年
月份 1 2 3 4 5 6 7 8 9 10 11 12
闰年 31 29 31 30 31 30 31 31 30 31 30 31
非闰年 31 28 31 30 31 30 31 31 30 31 30 31
* 输入 :年份
* 输出 :该年份是不是闰年.1,是.0,不是
* 调用
*/
uint8_t Is_Leap_Year(uint8_t yed)
{
uint8_t year;
year=2000+yed;
if(year%4==0) //必须能被4整除
{
if(year%100==0)
{
if(year%400==0)
return 1;//如果以00结尾,还要能被400整除
else
return 0;
}
else
{
return 1;
}
}
else
{
return 0;
}
}
/*
* 函数名:RTC_Get_Week
* 描述 :输入公历日期得到星期
* 输入 :公历年月日
* 输出 :星期号
* 调用
*/
uint8_t RTC_Get_Week(uint8_t ye, uint8_t month, uint8_t day)
{
uint16_t temp2;
uint16_t year;
uint8_t yearH,yearL;
ye=ye/16*10+ye%16; //十六进制转十进制
month=month/16*10+month%16;
day=day/16*10+day%16;
year=2000+ye; //(输入格式决定了必定是21世纪)
yearH=year/100;//用于判断是21世纪
yearL=year%100;//用于计算多的年数
//计算自1900年1月1日以来积累的多出来的天数
temp2=yearL+yearL/4; //平年365%7=1 闰年366%7=2 (自动把可以略去的7的倍数略掉了)(不用计算/100和/400)
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
// 21世纪需要加6(因为1900年1月1日是星期一,2000年1月1日是星期六)
if(yearH == 20)
{
temp2+=6;
}
//若日期是闰年的1.2月,星期修正要-1
if(yearL%4==0&&month<3)
{
temp2--;
}
return(temp2%7!=0)?temp2%7:7; //周一到周日=1~7
}
/*
* 函数名:RTC_Get
* 描述 :根据RTC计算器值计算当前年/月/日/时/分/秒/星期放入calendar结构体
* 输入 :无
* 输出 :0,成功;其他:错误代码
* 调用
*/
uint8_t RTC_Get(void)
{
static uint16_t daycnt=0;
uint32_t temp=0;
uint16_t temp1=0;
uint16_t tempppy;
timecount=RTC_GetCounter();
/*时分秒的计算*/
#if LTE_Conn
temp=(timecount+28800)/86400; //得到(总秒钟数对应的)天数 //在计算年月日时,中国时区-8h
#else
temp=timecount/86400; //得到(总秒钟数对应的)天数
#endif
if(daycnt!=temp)//超过一天了
{
daycnt=temp;
temp1=1970; //计算年份,从1970年开始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//闰年-366
{
if(temp>=366)temp-=366;
else {temp1++;break;}
}
else temp-=365; //平年-365
temp1++;
}
tempppy=temp1;
temp1=0; //计算月份,最后的temp就是日期
while(temp>=28)
{
if(Is_Leap_Year(tempppy)&&temp1==1)//闰年且是2月份-29
{
if(temp>=29)temp-=29;
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//其他都按表上来
else break;
}
temp1++;
}
tempppy=tempppy-2000;//得到年份
calendar.w_year =(tempppy/10)*16+(tempppy%10);
temp1=temp1+1; //得到月份
calendar.w_month=(temp1/10)*16+(temp1%10);
temp=temp+1; //得到日期
calendar.w_date=(temp/10)*16+(temp%10);
}
#if LTE_Conn
temp=(timecount+28800)%86400; //得到(去掉天数后 当天的)秒钟数 //在计算年月日时,中国时区-8h
#else
temp=timecount%86400; //得到(去掉天数后 当天的)秒钟数
#endif
tempppy=temp/3600; //得到小时
calendar.hour=(tempppy/10)*16+(tempppy%10);
tempppy=(temp%3600)/60;//得到分钟
calendar.min=(tempppy/10)*16+(tempppy%10);
tempppy=(temp%3600)%60;//得到秒钟
calendar.sec=(tempppy/10)*16+(tempppy%10);
calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期
/*判断合法*/
//在初始化后的第一次调用时,oldtimecnt还未赋值,此时将合法的当前时间写入EEPROM
if(oldtimecnt==0)
{
oldtimecnt = timecount;
if(calendar.w_month != 0)
{
uint8_t tmpRd[6];
tmpRd[0] = calendar.w_year;
tmpRd[1] = calendar.w_month;
tmpRd[2] = calendar.w_date;
tmpRd[3] = calendar.hour;
tmpRd[4] = calendar.min;
tmpRd[5] = calendar.sec;
EEPROM_WrMulByte(EE_TIME_BACKUP,tmpRd);
delay_ms(5);
}
}
//当时钟一直不走,判定有问题
if(timecount==oldtimecnt)
{
LSEErrCount++;
if(LSEErrCount>10)
{
LSEErrFlag=1;
sleep_Moni_Count=SLEEP_MON_CNT;
sleep2_Moni_Count = SLEEP2_MON_CNT;
calendar.w_year = 0; //结构体清零,方便此时的报警记录计入的时间为全0无效值
calendar.w_month = 0;
calendar.w_date = 0;
calendar.week = 0;
calendar.hour = 0;
calendar.min = 0;
calendar.sec = 0;
return 1; //数据异常,返回1
}
}
else
{
LSEErrCount=0;
oldtimecnt = timecount;
}
//休眠功能-RTC,比较当前秒与开始秒的差别来定时
//方案1
if((sleep_flag == 0) && ((sleep_enableflag & 0x01) != 0)) //未进入休眠+启用休眠方案1
{
if(sleeptimecount != 0)
{
if(RTC_UpdateFlag == 1)
{
sleeptimecount = timecount - (sleepEnd - sleeptime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
sleeptime = sleepEnd - (timecount - sleeptimecount); //更新剩余时间
}
if((sleeptime == 0) || (sleeptime > sleepEnd))
{
sleep_flag = 1;
#if LTE_Conn
pre_sleep_flag = 2;
pre_sleep_waitCnt = 0;
sleepOn_time=timecount;
#endif
}
}
else
{
sleeptimecount = timecount;
}
}
//方案2
if((sleep_flag == 0) && ((sleep_enableflag & 0x02) != 0)) //未进入休眠+启用休眠方案2
{
//满足休眠电压,无充电电流
if((cellVoltageMin <= sleep2Vol) && (bmsMem.packCurrent < 500))
{
if(sleep2timecount != 0)
{
if(RTC_UpdateFlag == 1)
{
sleep2timecount = timecount - (sleep2End - sleep2time); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
sleep2time = sleep2End - (timecount - sleep2timecount); //更新剩余时间
}
if((sleep2time == 0) || (sleep2time > sleep2End))
{
#if Key_PressLong
power_old = 4; //写入关机
power_state = 0;
#else
sleep_flag = 1;
#if LTE_Conn
pre_sleep_flag = 2;
pre_sleep_waitCnt = 0;
sleepOn_time=timecount;
#endif
#endif
}
}
else
{
sleep2timecount = timecount;
}
}
//否则停止计时
else
{
sleep2timecount = 0;
}
}
//不启用任何休眠方案
if(sleep_enableflag == 0)
{
sleep_flag = 0;
}
//屏幕手动关欠压启动了
if((bmsMem.balanceStatus & 0x20) != 0)
{
if(RTC_UpdateFlag == 1)
{
uvofftimecount = timecount - (uvoffEnd - uvofftime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
uvofftime = uvoffEnd - (timecount - uvofftimecount);
}
if((uvofftime == 0) || (uvofftime > uvoffEnd)) //防止反向溢出
{
bmsMem.balanceStatus &= 0xffdf;
}
}
//开路电压法校准SOC启动了
if(OCV_Wait_flag == 1)
{
if(RTC_UpdateFlag == 1)
{
ocvtimecount = timecount - (ocvEnd - ocvtime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
ocvtime = ocvEnd - (timecount - ocvtimecount);
}
if((ocvtime == 0) || (ocvtime > ocvEnd)) //防止反向溢出
{
OCV_CaliSOC_flag = 1;
}
}
//充电校准满充容量的倒计时启动了
if(fcc_CaliStartFlag == 1)
{
if(RTC_UpdateFlag == 1)
{
fcc_Calitimecount = timecount - (fcccaliEnd - fcc_Calitime); //更新起始点=当前时间-已消耗时间,此时剩余时间保持原值
}
else
{
fcc_Calitime = fcccaliEnd - (timecount - fcc_Calitimecount);
}
if((fcc_Calitime == 0) || (fcc_Calitime > fcccaliEnd)) //防止反向溢出
{
fcc_CaliStartFlag = 0;
EEPROM_WrMulByte(EE_FCC_TIME,ClearEE);
delay_ms(5);
}
}
//该刷新的都刷新后,标志置0
if(RTC_UpdateFlag != 0)
{
RTC_UpdateFlag = 0;
}
return 0;
}
/*
* 函数名:RTC_Set
* 描述 :把输入的年/月/日/时/分/秒转换为秒钟 写入RTC计数器
以1970年1月1日为基准
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
uint8_t RTC_Set(uint8_t ear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec)
{
uint16_t t,syear;
uint32_t seccount=0;
ear=(ear/16)*10+ear%16;
smon=(smon/16)*10+smon%16;
sday=(sday/16)*10+sday%16;
hour=(hour/16)*10+hour%16;
min=(min/16)*10+min%16;
sec=(sec/16)*10+sec%16;
syear=2000+ear;
if(syear<1970||syear>2099) //1970~2099年为合法年份
{
return 1;
}
for(t=1970;t<syear;t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
else seccount+=31536000; //平年的秒钟数
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒钟数相加
{
seccount+=(u32)mon_table[t]*86400; //各月份的秒钟数
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
}
seccount+=(u32)(sday-1)*86400; //把前面日期的秒钟数相加
seccount+=(u32)hour*3600;//小时的秒钟数
seccount+=(u32)min*60; //分钟的秒钟数
seccount+=sec; //最后的秒钟加上去
#if LTE_Conn
//4G的timecount是世界时间,中国对应显示需+8h,存储就要比电脑写入的少8h=28800s
seccount -= 28800;
#endif
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
RTC_SetCounter(seccount); //设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
//RTC_Get();
return 0;
}
/*
* 函数名:RTC_GetSynchro
* 描述 :由函数RTC_WaitForSynchro()改得,增加超时判断说明晶振出现问题
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
uint8_t RTC_GetSynchro(void)
{
uint16_t temp=0;
/* Clear RSF flag */
RTC->CRL &= (uint16_t)~RTC_FLAG_RSF;
/* Loop until RSF flag is set */
while ((RTC->CRL & RTC_FLAG_RSF) == (uint16_t)RESET)
{
temp++;
if(temp>=2000) return 1; //初始化时钟失败,晶振有问题
delay_ms(1); //1ms延时,总共2s
}
return 0;
}
/*
* 函数名:uf_RTC_Init
* 描述 :RTC初始化配置.第一次配置时会写入2023-6-25 14:00:00.
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
//开机执行一次
//第一次配置写入2023-6-25 14:00:00,此后正常读RTC后备寄存器的存值。若RTC值意外丢失则读EEPROM存值使用
uint8_t uf_RTC_Init(void)
{
uint16_t temp=0;
uint8_t tmpRd[6];
//上电读备份的时间信息
EEPROM_RdMulByte(EE_TIME_BACKUP,tmpRd);
if((tmpRd[0]!=0xff) && (tmpRd[1]!=0xff) && (tmpRd[2]!=0xff) && (tmpRd[3]!=0xff) && (tmpRd[4]!=0xff) && (tmpRd[5]!=0xff)) //时分秒格式存储,不可能存在0xff
{
calendar_BACKUP.w_year = tmpRd[0];
calendar_BACKUP.w_month = tmpRd[1];
calendar_BACKUP.w_date = tmpRd[2];
calendar_BACKUP.hour = tmpRd[3];
calendar_BACKUP.min = tmpRd[4];
calendar_BACKUP.sec = tmpRd[5];
}
else
{
//0x23,0x06,0x25,0x14,0x00,0x00
calendar_BACKUP.w_year = 0x23;
calendar_BACKUP.w_month = 0x06;
calendar_BACKUP.w_date = 0x25;
calendar_BACKUP.hour = 0x14;
calendar_BACKUP.min = 0x00;
calendar_BACKUP.sec = 0x00;
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
if(BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //检查是不是第一次配置时钟
{
// /*使用内部低速晶振*/
// BKP_DeInit(); //复位备份区域
// RCC_LSICmd(ENABLE); //使能LSI时钟
// while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) //等待LSI晶振就绪
// {
// temp++;
// if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
// delay_ms(1); // 1ms延时
// }
// RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //设置RTC时钟(RTCCLK),选择LSI作为RTC的时钟源
// RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// RTC_WaitForSynchro(); //等待RTC寄存器同步
// RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// RTC_EnterConfigMode(); //允许配置
// RTC_SetPrescaler(40000 - 1); //设置RTC预分频的值40kHz
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// /*使用内部低速晶振*/
/*使用外部低速晶振*/
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
delay_ms(1); // 1ms延时
}
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode(); // 允许配置
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
/*使用外部低速晶振*/
//RTC_Set(0x23,0x06,0x25,0x14,0x00,0x00);
RTC_Set(calendar_BACKUP.w_year,calendar_BACKUP.w_month,calendar_BACKUP.w_date,calendar_BACKUP.hour,calendar_BACKUP.min,calendar_BACKUP.sec);
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0x5050); //向指定的后备寄存器中写入指定数据
}
else//系统继续计时
{
// /*使用内部低速晶振*/
// RCC_LSICmd(ENABLE); //使能LSI时钟
// while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) //等待LSI晶振就绪
// {
// temp++;
// if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
// delay_ms(1); // 1ms延时
// }
// RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
// /*使用内部低速晶振*/
if(RTC_GetSynchro() == 1)return 1; //等待RTC寄存器同步 //10.8 晶振坏了这里会卡死,所以增加超时失败退出
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
/**更多功能**/
//更新当前时间
timecount=RTC_GetCounter();
//若启用休眠1,则以当前秒数为起始点
if((paraMem.sleep_min_disable & 0x8000) == 0) //0代表启用休眠
{
sleep_enableflag |= 0x01;
sleeptimecount=timecount;
}
else
{
sleep_enableflag &= 0xfe;
}
//若启用休眠2,则判断此时最低电压是否满足条件,满足则以当前秒数为起始点
if((paraMem.sleep2_min_disable & 0x8000) == 0) //0代表启用休眠
{
sleep_enableflag |= 0x02;
//满足休眠电压,无均衡,无电流
if((cellVoltageMin <= sleep2Vol) && ((bmsMem.balanceStatus & 0x0001) == 0) && (bmsMem.packCurrent > (-200)) && (bmsMem.packCurrent < 200))
{
//无除过压以外的保护
if(((bmsMem.bStatus1 & 0x067e) == 0) && ((bmsMem.bStatus2 & 0x00ff) == 0) && ((bmsMem.bStatus3 & 0x0008) == 0) && ((bmsMem.temperaStatus & 0x0f7f) == 0))
{
sleep2timecount=timecount;
}
}
}
else
{
sleep_enableflag &= 0xfd;
}
return 0; //ok
}
/*
* 函数名:uf_RTC_Update
* 描述 :将calendar_WRITE结构体的值写入RTC时间
* 输入 :无
* 输出 :0,成功;1:错误代码
* 调用
*/
uint8_t uf_RTC_Update(void)
{
if(calendar_WRITE.w_month != 0) //有写入的数组
{
uint16_t temp=0;
uint8_t Wrtime[6];
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
// /*使用内部低速晶振*/
// BKP_DeInit(); //复位备份区域
// RCC_LSICmd(ENABLE); //使能LSI时钟
// while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) //等待LSI晶振就绪
// {
// temp++;
// if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
// delay_ms(1); // 1ms延时
// }
// RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //设置RTC时钟(RTCCLK),选择LSI作为RTC的时钟源
// RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// RTC_WaitForSynchro(); //等待RTC寄存器同步
// RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// RTC_EnterConfigMode(); //允许配置
// RTC_SetPrescaler(40000 - 1); //设置RTC预分频的值40kHz
// RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
// /*使用内部低速晶振*/
/*使用外部低速晶振*/
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
if(temp>=5000) return 1; //持续5s无法起振,初始化时钟失败,晶振有问题
delay_ms(1); // 1ms延时
}
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode(); // 允许配置
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
/*使用外部低速晶振*/
RTC_Set(calendar_WRITE.w_year,calendar_WRITE.w_month,calendar_WRITE.w_date,calendar_WRITE.hour,calendar_WRITE.min,calendar_WRITE.sec);
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0x5050); //向指定的后备寄存器中写入用户程序数据
//刷新休眠和欠压复位的时间
RTC_UpdateFlag = 1;
//存储时间备份
Wrtime[0] = calendar_WRITE.w_year;
Wrtime[1] = calendar_WRITE.w_month;
Wrtime[2] = calendar_WRITE.w_date;
Wrtime[3] = calendar_WRITE.hour;
Wrtime[4] = calendar_WRITE.min;
Wrtime[5] = calendar_WRITE.sec;
EEPROM_WrMulByte(EE_TIME_BACKUP,Wrtime);
delay_ms(5);
//清零
calendar_WRITE.w_year = 0;
calendar_WRITE.w_month = 0;
calendar_WRITE.w_date = 0;
calendar_WRITE.week = 0;
calendar_WRITE.hour = 0;
calendar_WRITE.min = 0;
calendar_WRITE.sec = 0;
}
return 0; //ok
}
//用于处理因各种原因需要的时间备份
int32_t oldCur;
void RTC_BackUp(void)
{
uint8_t WrFlag=0;
//当正在充放电,开始时记录一次,之后每5分钟记录一次
if(bCHGING == 1)
{
if(oldCur <= 0) //原来在放电或待机
{
WrFlag = 1;
oldCur = bmsMem.packCurrent;
}
if((calendar.min%5 == 0x00) && (calendar.sec == 0x00))
{
WrFlag = 1;
}
}
else if(bDSGING == 1)
{
if(oldCur >= 0) //原来在充电或待机
{
WrFlag = 1;
oldCur = bmsMem.packCurrent;
}
if((calendar.min%5 == 0x00) && (calendar.sec == 0x00))
{
WrFlag = 1;
}
}
//在待机状态,每1h记录一次
else
{
oldCur = 0;
if((calendar.min == 0x00) && (calendar.sec == 0x00))
{
WrFlag = 1;
}
}
//需要写入备份时间,执行
if(WrFlag == 1)
{
if(calendar.w_month != 0)
{
uint8_t tmpRd[6];
tmpRd[0] = calendar.w_year;
tmpRd[1] = calendar.w_month;
tmpRd[2] = calendar.w_date;
tmpRd[3] = calendar.hour;
tmpRd[4] = calendar.min;
tmpRd[5] = calendar.sec;
EEPROM_WrMulByte(EE_TIME_BACKUP,tmpRd);
delay_ms(5);
}
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef __RTC_H
#define __RTC_H
#include "stm32f10x.h"
//时间结构体
typedef struct
{
vu8 sec;
vu8 min;
vu8 hour;
vu8 week;
vu8 w_date;
vu8 w_month;
vu8 w_year;
}_calendar_obj;
extern _calendar_obj calendar; //日历结构体
extern _calendar_obj calendar_WRITE;
extern _calendar_obj calendar_BACKUP;
extern uint8_t uf_RTC_Init(void);
extern uint8_t uf_RTC_Update(void); //用于上位机修改时间
extern uint8_t RTC_Set(uint8_t ear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec);
extern uint8_t RTC_Get(void);
extern void RTC_BackUp(void);
#endif
+234
View File
@@ -0,0 +1,234 @@
/**
******************************************************************************
* @file spi.c
* @author
* @version
* @date
* @brief SPI通信程序,用于与AFE芯片(SH3673520)通信
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
//SH3673517 SPI通信
//PB12 = SPI1_CS (GPIO推挽输出,软件控制片选)
//PB13 = SPI1_SCK (AF1) - 时钟
//PB14 = SPI1_MISO(AF1) - 主机输入从机输出
//PB15 = SPI1_MOSI(AF0) - 主机输出从机输入
#define PIN_SPI_NSS GPIO_Pin_12
#define PIN_SPI_SCK GPIO_Pin_13
#define PIN_SPI_MISO GPIO_Pin_14
#define PIN_SPI_MOSI GPIO_Pin_15
#define SPI_Enable() GPIO_ResetBits(GPIOB, PIN_SPI_NSS) //使能通信
#define SPI_Disable() GPIO_SetBits(GPIOB, PIN_SPI_NSS) //关闭通信
/*
SPI_MOSI PB15 主设备输出,从设备输入
SPI_MISO PB14 主设备输入,从设备输出
SPI_SCK PB13 时钟
SPI_CS PB12 片选信号
*/
void uf_SPI2_Init(void)
{
/*定义SPI参数*/
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能SPI2和GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// PB13 (SCK), PB15 (MOSI)
GPIO_InitStructure.GPIO_Pin = PIN_SPI_SCK | PIN_SPI_MOSI;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// PB14 (MISO)
GPIO_InitStructure.GPIO_Pin = PIN_SPI_MISO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// PB12 (CS)
GPIO_InitStructure.GPIO_Pin = PIN_SPI_NSS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, PIN_SPI_NSS); //初始化拉高CS,关闭SPI通信
/*配置SPI模式*/
SPI_I2S_DeInit(SPI2);
// 配置SPI2与AFE芯片的SPI3通信方式:4线通信,全双工,主模式,数据同边传输,MSB在前
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工模式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据帧大小为 8 位
// 配置SPI3极性参数CPOL和CPHA
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟极性为1,空闲时SCK电平状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //时钟相位为1,在第二个跳变沿开始采样数据(第一个跳变沿,失效)
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件控制 NSS 信号(PB12)
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;//波特率预分频系数为 64 //36MHz/64 = 0.5625MHz < 1MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值的生成多项式=x^8+x^2+x+1,省略高位,可为任意值1,只用到低8位,生成二进制编码为0000111=7
SPI_Init(SPI2, &SPI_InitStructure);
// 使能SPI2
SPI_Cmd(SPI2, ENABLE);
}
//通信失败时,复位SPI
void SPI2_Error(void)
{
//写也无法确有效,因为需要时序
//这里重新初始化SPI
uf_SPI2_Init();
}
//AFE只支持单字节写操作
//可写地址 40H~59H
//rtnval 0-true; other-false
uint8_t AFE_WriteOneByte(uint8_t addr, uint8_t *data)
{
uint8_t tx_buffer[5], rx_buffer[5];
uint8_t i;
uint8_t response;
// 构造发送数据帧: [0x01][reg_addr][write_data][CRC][0x00]
tx_buffer[0] = 0x01; // 写命令
tx_buffer[1] = addr; // 寄存器地址
tx_buffer[2] = *data; // 写入数据
tx_buffer[3] = CRC8_Cal(tx_buffer, 3); // CRC8
tx_buffer[4] = 0x00; // 无效数据接收
// 拉低CS片选
SPI_Enable();
for (i = 0; i < 5; i++)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, tx_buffer[i]);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
rx_buffer[i] = SPI_I2S_ReceiveData(SPI2);
}
// 获取返回值
response = rx_buffer[4];
SPI_Disable();
delay_us(5);
return (response == 0xA5) ? 0 : 1;
}
//AFE可1次读取多个字节操作
//可读取地址 40H~99H
//rtnval 0-success, other-fail
uint8_t AFE_ReadMulByte(uint8_t addr, uint8_t lenth, uint8_t *data)
{
uint8_t tx_buffer[4]; // 发送缓冲区
uint8_t rx_buffer[40]; // 接收缓冲区,最大可读取24字节,留有余量
uint8_t crc_calculated, crc_received;
uint8_t i;
// 构造发送数据帧: [0x02][reg_addr][data_length][0x00]
tx_buffer[0] = 0x02; // 读命令
tx_buffer[1] = addr; // 寄存器地址
tx_buffer[2] = lenth; // 数据长度
tx_buffer[3] = 0x00;
// 拉低CS片选
SPI_Enable();
// 第一阶段:发送时钟和命令,发送(命令/地址/长度/0x00) 接收(0xFF/命令/地址/长度)
for( i=0; i<4; i++)
{
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)); // 等待发送缓冲区空
SPI_I2S_SendData(SPI2, tx_buffer[i]);
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)); // 等待接收完成
rx_buffer[i] = SPI_I2S_ReceiveData(SPI2);
}
// 第二阶段:接收有效数据(0x00)发送接收数据(需要提供接收时钟)
for( i=0; i<lenth; i++)
{
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI2, 0x00); // 发送无效数据维持时钟
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE));
rx_buffer[4+i] = SPI_I2S_ReceiveData(SPI2);
}
// 第三阶段:接收CRC
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, 0x00);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
rx_buffer[4 + lenth] = SPI_I2S_ReceiveData(SPI2);
// 拉高CS片选
SPI_Disable();
delay_us(5);
// 校验应头(发送时同时接收)
if(rx_buffer[0] != 0xFF || // 第一个字节应为0xFF
rx_buffer[1] != 0x02 || // 回显读命令
rx_buffer[2] != addr || // 回显寄存器地址
rx_buffer[3] != lenth) // 回显数据长度
{
return 0; // 头部校验失败
}
// 提取有效数据(跳过前4个应答字节)
for (i = 0; i < lenth; i++)
{
data[i] = rx_buffer[4 + i]; // 跳过0xFF、命令、地址、长度
}
crc_received = rx_buffer[4 + lenth]; // 最后1字节为CRC
// CRC计算范围:0xFF + 命令 + 地址 + 长度 + 数据,直到CRC前
crc_calculated = CRC8_Cal(&rx_buffer[0], 4 + lenth);
return (crc_received == crc_calculated) ? 0 : 1;
}
//软件复位
//rtnval 0-success, other-fail
uint8_t AFE_Reset(void)
{
uint8_t tx_buffer[5], rx_buffer[5];
uint8_t response;
uint8_t i = 0;
// 构造发送数据帧: [0x0B][0xBB][0xCC][CRC][0x00]
tx_buffer[0] = 0x0B; // 复位命令
tx_buffer[1] = 0xBB; // 固定参数1
tx_buffer[2] = 0xCC; // 固定参数2
tx_buffer[3] = CRC8_Cal(tx_buffer, 3); // CRC8
tx_buffer[4] = 0x00; // 无效数据接收
// 拉低CS片选
SPI_Enable();
for ( i = 0; i < 5; i++)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, tx_buffer[i]);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
rx_buffer[i] = SPI_I2S_ReceiveData(SPI2);
}
// 获取返回值
response = rx_buffer[4];
// 拉高CS片选
SPI_Disable();
return (response == 0xA5) ? 0 : 1;
}
+52
View File
@@ -0,0 +1,52 @@
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//确保n的值小于16!
//IO口操作,只对单一的IO口!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
#endif
+78
View File
@@ -0,0 +1,78 @@
/**
******************************************************************************
* @file tim.c
* @author Jerry
* @version V2.1
* @date 19-April-2022
* @brief tim program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
//相对精确的ms延时,不使用中断
//优化:防止和其他中断冲突,导致卡死
void delay_ms(uint16_t ms)
{
uint16_t i;
volatile uint32_t tempreg;
uint32_t timeout; //等待时间
for(i=0; i<ms; i++)
{
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
SysTick->LOAD = 72000 - 1;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
timeout = 72000;
do
{
tempreg = SysTick->CTRL;
timeout--;
if(timeout == 0)
{
break; //超时退出
}
}
while( (tempreg & SysTick_CTRL_COUNTFLAG_Msk) ==0);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0;
}
}
//相对精确的us延时,不使用中断
//(没调用过)
void delay_us(uint16_t us)
{
uint16_t i;
volatile uint32_t tempreg;
for(i=0; i<us; i++)
{
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
SysTick->LOAD = 72;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
do
{
tempreg = SysTick->CTRL;
}
while( (tempreg & SysTick_CTRL_COUNTFLAG_Msk) ==0);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0;
}
}
+231
View File
@@ -0,0 +1,231 @@
/**
******************************************************************************
* @file tim.c
* @author Jerry
* @version V2.1
* @date 19-April-2022
* @brief tim program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
uint32_t tmrSys = 0;
uint32_t tmrTemp[20];
//10MS
void uf_TIM3_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 10000-1;
TIM_TimeBaseStructure.TIM_Prescaler = 72-1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //主定时器 优先级:0,0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE );
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
//10ms中断
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx的中断待处理位:TIM 中断源
tmrSys++;
KEY_TIM_Moni();
Cali_SOC_Moni();
Screen_TIM_Moni(); //[陶晶驰]
CAN_TIM_Moni();
MODBUS_TIM_Moni();
MODBUS1_TIM_Moni();
// DI_TIM_Moni();
// DI_Ctrl();
OCC2_TIM_Moni();
OCC2_Ctrl();
#if Addr_SetAuto
//休眠时,地址变动暂停
if(sleep_flag == 0)
{
if(paraMem.addr_FREE_Flg == 0) ADDR_Rank_Moni();
if(paraMem.addr_FREE_Flg == 0) ADDR_Assign_Moni();
}
#endif
//只有RTC时钟有问题时,才会启用下列函数
if(LSEErrFlag==1)
{
SLEEP_TIM_Moni(); //休眠的倒计时
SLEEP2_TIM_Moni(); //休眠2的倒计时
UVOff_TIM_Moni(); //欠压强制复位的倒计时
FCCCali_TIM_Moni(); //容量校准要在12h内的倒计时
}
if(Screen_RevFlg == 1) //屏幕收到完整一帧去处理
{
Screen_RevCount++;
if(Screen_RevCount > 1)
{
Screen_RevFlg = 0;
Screen_RevCount = 0;
//Screen_IT_Update();
Screen_RevHandlerFlg = 1;
}
}
if(MODBUS_RevFlg == 1) //MODBUS收到完整一帧去处理
{
MODBUS_RevCount++;
if(MODBUS_RevCount > 1)
{
MODBUS_RevFlg = 0;
MODBUS_RevCount = 0;
MODBUS_IT_TIMUpdate();
}
}
if(MODBUS1_RevFlg == 1) //MODBUS1收到完整一帧去处理
{
MODBUS1_RevCount++;
if(MODBUS1_RevCount > 1)
{
MODBUS1_RevFlg = 0;
MODBUS1_RevCount = 0;
MODBUS1_IT_TIMUpdate();
}
}
/*三选一模块*/
#if BLE_Conn
BLE_TIM_Moni(); //BLE无通信定时初始化
if(BLE_RevFlg == 1) //BLE收到完整一帧去处理
{
BLE_RevCount++;
if(BLE_RevCount > 3)
{
BLE_RevFlg = 0;
BLE_RevCount = 0;
BLE_IT_Update();
}
}
#endif
#if WIFI_Conn
WIFI_TIM_Moni(); //WIFI无通信定时初始化
if(WIFI_RevFlg == 1) //WIFI收到完整一帧去处理
{
WIFI_RevCount++;
if(WIFI_RevCount > 3)
{
WIFI_RevFlg = 0;
WIFI_RevCount = 0;
WIFI_IT_Update();
}
}
#endif
#if LTE_Conn
LTE_TIM_Moni(); //LTE无通信定时初始化
if(LTE_RevFlg == 1) //LTE收到完整一帧去处理
{
LTE_RevCount++;
if(LTE_RevCount > 3)
{
LTE_RevFlg = 0;
LTE_RevCount = 0;
//有回复,清零等待倒计时
LTE_WaitRxFlg = 0;
LTE_WaitRxDelay = 0;
if(LTE_OTA_Flag == 0)
{
LTE_4G_IT_Update();
}
else
{
LTE_OTA_IT_Update();
}
}
}
#endif
}
}
/**************************************************************************
** 函数名: TIMER_Update
** 输 入: nothing
** 输 出: tmrSys value
** 备 注:tmrSys do ++ in the SysTick_Handler function
***************************************************************************/
uint32_t TIMER_Update(void)
{
return tmrSys;
}
/**************************************************************************
** 函数名:
** 输 入:
** 输 出:
** 备 注:
***************************************************************************/
uint32_t TIMER_IsOut(uint32_t cnt, uint32_t tmr)
{
uint32_t tmp = tmrSys;
tmp = cnt > tmp ? ( (uint32_t)(-1) - cnt + tmp ) : ( tmp-cnt );
if(tmp>=tmr)
return 1;
else
return 0;
}
/**************************************************************************
** 函数名:
** 输 入:
** 输 出:
** 备 注:
***************************************************************************/
uint32_t TIMER_IsOther(uint32_t cnt, uint32_t tmr)
{
uint32_t tmp = tmrSys;
uint32_t tmp1;
tmp = cnt > tmp ? ( (uint32_t)(-1) - cnt + tmp ) : ( tmp-cnt );
tmp1 = tmp>=tmr ? (tmp - tmr): (tmr-tmp);
return tmp1;
}
+316
View File
@@ -0,0 +1,316 @@
/**
******************************************************************************
* @file tim.c
* @author Jerry
* @version V2.1
* @date 19-April-2022
* @brief tim program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
uint8_t Screen_RevFlg;
uint8_t Screen_RevCount;
uint8_t Screen_RevHandlerFlg; //在主函数执行分析处理
uint8_t MODBUS_RevFlg;
uint8_t MODBUS_RevCount;
uint8_t MODBUS1_RevFlg;
uint8_t MODBUS1_RevCount;
/*三选一模块*/
#if BLE_Conn
uint8_t BLE_RevFlg;
uint8_t BLE_RevCount;
#endif
#if WIFI_Conn
uint8_t WIFI_RevFlg;
uint8_t WIFI_RevCount;
#endif
#if LTE_Conn
uint8_t LTE_RevFlg;
uint8_t LTE_RevCount;
#endif
/**RS485 网口3.4**/
//usart1 init
void uf_UART1_Init( u32 bound )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* Enable the USART1 Pins Software Remapping */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Configure USART1 Rx (PA.10) as input floating */
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable the USART1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //485通信主从机 优先级:3,0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
if(bmsMem.E2_485Addr == 1)
{
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); //设备做主机时关闭接收中断
}
else
{
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //设备做从机时打开接收中断
}
//USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
//send multiple bytes
void USART1_SendMulByte(uint8_t *p, uint8_t size)
{
uint8_t i;
for(i=0;i<size;i++)
{
USART_SendData(USART1, *p);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
p++;
}
}
//usart1 interrupt
void USART1_IRQHandler(void)
{
uint8_t tmp = tmp;
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
{
MODBUS_RevFlg = 1;
MODBUS_RevCount = 0;
MODBUS_IT_Receive();
}
}
/**屏幕**/
//usart2 init
void uf_UART2_Init( u32 bound )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* Enable the USART1 Pins Software Remapping */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
/* Configure USART1 Rx (PA.03) as input floating */
/* Configure USART1 Tx (PA.02) as alternate function push-pull */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable the USART1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //TTL通信屏幕 优先级:3,2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
//USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART2, ENABLE);
}
//usart2 interrupt
void USART2_IRQHandler(void)
{
uint8_t tmp = tmp;
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET)
{
Screen_RevFlg = 1;
Screen_RevCount = 0;
Screen_IT_Receive();
}
}
/**RS485 网口1**/
//usart3 init
void uf_UART3_Init( u32 bound )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* Enable the USART3 Pins Software Remapping */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
/* Configure USART3 Rx (PB.11) as input floating */
/* Configure USART3 Tx (PB.10) as alternate function push-pull */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Enable the USART3 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //485通信上位机 优先级:3,1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); //设备做从机时打开接收中断
//USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART3, ENABLE);
}
//send multiple bytes
void USART3_SendMulByte(uint8_t *p, uint8_t size)
{
uint8_t i;
for(i=0;i<size;i++)
{
USART_SendData(USART3, *p);
while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET);
p++;
}
}
//usart3 interrupt
void USART3_IRQHandler(void)
{
uint8_t tmp = tmp;
if(USART_GetITStatus(USART3,USART_IT_RXNE) != RESET)
{
MODBUS1_RevFlg = 1;
MODBUS1_RevCount = 0;
MODBUS1_IT_Receive();
}
}
/**蓝牙/WIFI/4G模块**/
//uart4 init
void uf_UART4_Init( u32 bound )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* Enable the UART4 Pins Software Remapping */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(UART4, &USART_InitStructure);
/* Configure UART4 Rx (PC.11) as input floating */
/* Configure UART4 Tx (PC.10) as alternate function push-pull */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Enable the UART4 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //蓝牙通信 优先级:1,1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE); //打开接收中断
//USART_ITConfig(UART4, USART_IT_IDLE, ENABLE);
USART_Cmd(UART4, ENABLE);
}
//uart4 interrupt
void UART4_IRQHandler(void)
{
if(USART_GetITStatus(UART4,USART_IT_RXNE) != RESET)
{
/*三选一模块*/
#if BLE_Conn
BLE_RevFlg = 1;
BLE_RevCount = 0;
BLE_IT_Receive();
#endif
#if WIFI_Conn
WIFI_RevFlg = 1;
WIFI_RevCount = 0;
WIFI_IT_Receive();
#endif
#if LTE_Conn
LTE_RevFlg = 1;
LTE_RevCount = 0;
LTE_4G_IT_Receive();
#endif
}
}
+43
View File
@@ -0,0 +1,43 @@
/**
******************************************************************************
* @file wdg.c
* @author
* @version
* @date
* @brief
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
//初始化独立看门狗
//prer:分频数:0~7(只有低3位有效!)
//分频因子=4*2^prer.但最大值只能是256!
//rlr:重装载寄存器值:低11位有效.
//时间计算(大概):Tout=((4*2^prer)*rlr)/40 (ms).
void uf_IWDG_Init(u8 prer,u16 rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能对寄存器IWDG_PR和IWDG_RLR的写操作
IWDG_SetPrescaler(prer); //设置IWDG预分频值:设置IWDG预分频值为64
IWDG_SetReload(rlr); //设置IWDG重装载值
IWDG_ReloadCounter(); //按照IWDG重装载寄存器的值重装载IWDG计数器
IWDG_Enable(); //使能IWDG
}
//喂独立看门狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter();//reload
}