#include "stm32f1xx_hal.h"
/* Private define ------------------------------------------------------------*/
#define CELL_NUM 10 // 10串锂电池
#define ADC_BUF_SIZE 11 // 10路电压 + 1路温度
#define V_REF 3.3f // 参考电压
#define ADC_RES 4096.0f // 12位ADC分辨率
#define VOLTAGE_DIVIDER 11.0f// 分压比(10k+1k电阻)
#define OVER_VOLTAGE 4.25f // 单体过压阈值
#define UNDER_VOLTAGE 2.8f // 单体欠压阈值
#define OVER_TEMP 60.0f // 过温阈值
// 引脚定义
#define BEEP_PIN GPIO_PIN_3
#define BEEP_PORT GPIOB
#define RUN_LED_PIN GPIO_PIN_5
#define RUN_LED_PORT GPIOB
#define FAULT_LED_PIN GPIO_PIN_6
#define FAULT_LED_PORT GPIOB
#define CHARGE_RELAY_PIN GPIO_PIN_12
#define CHARGE_RELAY_PORT GPIOB
#define DISCHARGE_RELAY_PIN GPIO_PIN_13
#define DISCHARGE_RELAY_PORT GPIOB
#define PRECHARGE_RELAY_PIN GPIO_PIN_14
#define PRECHARGE_RELAY_PORT GPIOB
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
I2C_HandleTypeDef hi2c2;
UART_HandleTypeDef huart1;
uint16_t adc_buf[ADC_BUF_SIZE];
float cell_voltages[CELL_NUM];
float total_voltage;
float temperature;
uint8_t fault_code = 0;
char uart_tx_buf[512];
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_I2C2_Init(void);
static void MX_USART1_UART_Init(void);
void BMS_Init(void);
void BMS_ReadAllData(void);
float NTC_CalculateTemp(uint16_t adc_val);
void BMS_ProtectionLogic(void);
void Relay_AllControl(uint8_t state);
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowString(uint8_t x, uint8_t y, char *str);
void OLED_UpdateDisplay(void);
void UART_SendString(char *str);
void UART_SendDebugData(void);
/* Private user code ---------------------------------------------------------*/
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_I2C2_Init();
MX_USART1_UART_Init();
BMS_Init();
OLED_Init();
UART_SendString("BMS System Start!\r\n");
while (1)
{
BMS_ReadAllData();
BMS_ProtectionLogic();
OLED_UpdateDisplay();
UART_SendDebugData();
HAL_GPIO_TogglePin(RUN_LED_PORT, RUN_LED_PIN);
HAL_Delay(100);
}
}
void BMS_Init(void)
{
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, ADC_BUF_SIZE);
Relay_AllControl(0);
HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(FAULT_LED_PORT, FAULT_LED_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_RESET);
}
void BMS_ReadAllData(void)
{
total_voltage = 0.0f;
for(int i=0; i<CELL_NUM; i++)
{
cell_voltages[i] = (float)adc_buf[i] * V_REF / ADC_RES * VOLTAGE_DIVIDER;
total_voltage += cell_voltages[i];
}
temperature = NTC_CalculateTemp(adc_buf[10]);
}
float NTC_CalculateTemp(uint16_t adc_val)
{
const float R0 = 10000.0f;
const float B = 3950.0f;
const float T0 = 298.15f;
float R = (4095.0f / (float)adc_val - 1.0f) * R0;
float inv_T = 1.0f / T0 + log(R / R0) / B;
return (1.0f / inv_T - 273.15f);
}
void BMS_ProtectionLogic(void)
{
fault_code = 0;
// 单体过压检查
for(int i=0; i<CELL_NUM; i++)
{
if(cell_voltages[i] > OVER_VOLTAGE) fault_code |= (1 << i);
}
// 单体欠压检查
for(int i=0; i<CELL_NUM; i++)
{
if(cell_voltages[i] < UNDER_VOLTAGE) fault_code |= (1 << (i + 16));
}
// 过温检查
if(temperature > OVER_TEMP) fault_code |= (1 << 31);
if(fault_code != 0)
{
Relay_AllControl(0);
HAL_GPIO_TogglePin(FAULT_LED_PORT, FAULT_LED_PIN);
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_SET);
}
else
{
Relay_AllControl(1);
HAL_GPIO_WritePin(FAULT_LED_PORT, FAULT_LED_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_RESET);
}
}
void Relay_AllControl(uint8_t state)
{
if(state == 1)
{
HAL_GPIO_WritePin(PRECHARGE_RELAY_PORT, PRECHARGE_RELAY_PIN, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(CHARGE_RELAY_PORT, CHARGE_RELAY_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(DISCHARGE_RELAY_PORT, DISCHARGE_RELAY_PIN, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(CHARGE_RELAY_PORT, CHARGE_RELAY_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(DISCHARGE_RELAY_PORT, DISCHARGE_RELAY_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(PRECHARGE_RELAY_PORT, PRECHARGE_RELAY_PIN, GPIO_PIN_SET);
}
}
// OLED SSD1306 I2C驱动
#define OLED_ADDR 0x78
void OLED_WriteCmd(uint8_t cmd)
{
uint8_t buf[2] = {0x00, cmd};
HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0x00, 1, buf, 2, 100);
}
void OLED_WriteData(uint8_t data)
{
uint8_t buf[2] = {0x40, data};
HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0x40, 1, buf, 2, 100);
}
void OLED_Init(void)
{
HAL_Delay(100);
OLED_WriteCmd(0xAE);
OLED_WriteCmd(0xD5); OLED_WriteCmd(0x80);
OLED_WriteCmd(0xA8); OLED_WriteCmd(0x3F);
OLED_WriteCmd(0xD3); OLED_WriteCmd(0x00);
OLED_WriteCmd(0x40);
OLED_WriteCmd(0x8D); OLED_WriteCmd(0x14);
OLED_WriteCmd(0x20); OLED_WriteCmd(0x00);
OLED_WriteCmd(0xC8);
OLED_WriteCmd(0xDA); OLED_WriteCmd(0x12);
OLED_WriteCmd(0x81); OLED_WriteCmd(0xCF);
OLED_WriteCmd(0xD9); OLED_WriteCmd(0xF1);
OLED_WriteCmd(0xDB); OLED_WriteCmd(0x40);
OLED_WriteCmd(0xA4);
OLED_WriteCmd(0xA6);
OLED_WriteCmd(0xAF);
OLED_Clear();
}
void OLED_SetPos(uint8_t x, uint8_t y)
{
OLED_WriteCmd(0xB0 + y);
OLED_WriteCmd(((x & 0xF0) >> 4) | 0x10);
OLED_WriteCmd(x & 0x0F);
}
void OLED_Clear(void)
{
for(uint8_t y=0; y<8; y++)
{
OLED_SetPos(0, y);
for(uint8_t x=0; x<128; x++) OLED_WriteData(0x00);
}
}
const uint8_t oled_font[][16] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 空格
{0x00,0x00,0x7E,0x81,0x81,0x81,0x7E,0x00,0x00,0x00,0x7E,0x81,0x81,0x81,0x7E,0x00}, // 0
{0x00,0x00,0x02,0x04,0x08,0x10,0x7F,0x00,0x00,0x00,0x7F,0x01,0x02,0x04,0x08,0x00}, // 1
{0x00,0x00,0x7E,0x01,0x7E,0x80,0x7E,0x00,0x00,0x00,0x7E,0x81,0x7E,0x01,0x7E,0x00}, // 2
{0x00,0x00,0x7E,0x01,0x41,0x01,0x7E,0x00,0x00,0x00,0x7E,0x01,0x41,0x01,0x7E,0x00}, // 3
{0x00,0x00,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,0x00,0x1F,0x10,0x10,0x10,0x7F,0x00}, // 4
{0x00,0x00,0x7E,0x80,0x7E,0x01,0x7E,0x00,0x00,0x00,0x7E,0x80,0x7E,0x01,0x7E,0x00}, // 5
{0x00,0x00,0x7E,0x80,0x7E,0x81,0x7E,0x00,0x00,0x00,0x7E,0x80,0x7E,0x81,0x7E,0x00}, // 6
{0x00,0x00,0x7E,0x01,0x02,0x04,0x08,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x7F,0x00}, // 7
{0x00,0x00,0x7E,0x81,0x7E,0x81,0x7E,0x00,0x00,0x00,0x7E,0x81,0x7E,0x81,0x7E,0x00}, // 8
{0x00,0x00,0x7E,0x81,0x7F,0x01,0x7E,0x00,0x00,0x00,0x7E,0x81,0x7F,0x01,0x7E,0x00}, // 9
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x00,0x00,0x00}, // .
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x78,0x00,0x00,0x00}, // :
{0x00,0x00,0x7E,0x81,0x91,0x91,0x7E,0x00,0x00,0x00,0x7E,0x81,0x91,0x91,0x7E,0x00}, // B
{0x00,0x00,0x7F,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x7F,0x00}, // E
{0x00,0x00,0x7F,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x7F,0x00}, // F
{0x00,0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00,0x00,0x7F,0x08,0x08,0x08,0x7F,0x00}, // H
{0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00}, // L
{0x00,0x00,0x7E,0x81,0x81,0x81,0x7E,0x00,0x00,0x00,0x7E,0x81,0x81,0x81,0x7E,0x00}, // O
{0x00,0x00,0x7F,0x81,0x81,0x81,0x70,0x00,0x00,0x00,0x70,0x81,0x81,0x81,0x7F,0x00}, // P
{0x00,0x00,0x7E,0x80,0x7E,0x01,0x7E,0x00,0x00,0x00,0x7E,0x01,0x7E,0x80,0x7E,0x00}, // S
{0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00}, // T
{0x00,0x00,0x7F,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,0x7F,0x80,0x80,0x80,0x7F,0x00}, // U
};
void OLED_ShowChar(uint8_t x, uint8_t y, char ch)
{
uint8_t c;
if(ch >= '0' && ch <= '9') c = ch - '0' + 1;
else if(ch == '.') c = 10;
else if(ch == ':') c = 11;
else if(ch >= 'A' && ch <= 'Z') c = ch - 'A' + 12;
else c = 0;
OLED_SetPos(x, y);
for(uint8_t i=0; i<8; i++) OLED_WriteData(oled_font[c][i]);
OLED_SetPos(x, y+1);
for(uint8_t i=0; i<8; i++) OLED_WriteData(oled_font[c][i+8]);
}
void OLED_ShowString(uint8_t x, uint8_t y, char *str)
{
while(*str)
{
OLED_ShowChar(x, y, *str++);
x += 8;
if(x > 120)
{
x = 0;
y += 2;
}
}
}
void OLED_UpdateDisplay(void)
{
OLED_Clear();
char buf[32];
OLED_ShowString(0, 0, "BMS F103 SIM");
sprintf(buf, "TOTAL:%.2fV", total_voltage);
OLED_ShowString(0, 2, buf);
sprintf(buf, "TEMP:%.1fC", temperature);
OLED_ShowString(0, 4, buf);
sprintf(buf, "FAULT:%08X", fault_code);
OLED_ShowString(0, 6, buf);
}
void UART_SendDebugData(void)
{
int len = sprintf(uart_tx_buf, "\r\n===== BMS Data =====\r\n");
len += sprintf(uart_tx_buf+len, "Total Voltage: %.2f V\r\n", total_voltage);
len += sprintf(uart_tx_buf+len, "Temperature: %.1f C\r\n", temperature);
len += sprintf(uart_tx_buf+len, "Fault Code: 0x%08X\r\n", fault_code);
for(int i=0; i<CELL_NUM; i++)
{
len += sprintf(uart_tx_buf+len, "Cell %d: %.3f V\r\n", i+1, cell_voltages[i]);
}
UART_SendString(uart_tx_buf);
}
void UART_SendString(char *str)
{
HAL_UART_Transmit(&huart1, (uint8_t*)str, strlen(str), 100);
}
/* CubeMX自动生成的初始化函数 */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 11;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = ADC_REGULAR_RANK_4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = ADC_REGULAR_RANK_6;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = ADC_REGULAR_RANK_7;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = ADC_REGULAR_RANK_8;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_REGULAR_RANK_9;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_9;
sConfig.Rank = ADC_REGULAR_RANK_10;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_9;
sConfig.Rank = ADC_REGULAR_RANK_11;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}
static void MX_I2C2_Init(void)
{
hi2c2.Instance = I2C2;
hi2c2.Init.ClockSpeed = 100000;
hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c2.Init.OwnAddress1 = 0;
hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c2.Init.OwnAddress2 = 0;
hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_DMA_Init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(&hadc1,DMA_Handle,hdma_adc1);
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOB, BEEP_PIN|RUN_LED_PIN|FAULT_LED_PIN|CHARGE_RELAY_PIN
|DISCHARGE_RELAY_PIN|PRECHARGE_RELAY_PIN, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = BEEP_PIN|RUN_LED_PIN|FAULT_LED_PIN|CHARGE_RELAY_PIN
|DISCHARGE_RELAY_PIN|PRECHARGE_RELAY_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif