STM32电子实战项目(一)记录:BLDC kitchen prep centre

在这里插入图片描述

产品目的:

解决搅拌机食材粘壁问题。

产品功能及需求分析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

需求分析及实现可能性:

从项目需求看,该项目要实现的功能并不复杂,控制电机的正反转及对应LED显示即可,同时也没必要控制电机转速,不过由于是锂电产品,所以还要考虑充电和低功耗问题以及产品均需要注意的安全性问题。

1.锂电池:
从电机特性表中得到负载电流也不大,但仍需考虑启动时电流会稍大,所以选择18650锂电池时要对其输出电流能力留有一定余量,一般除了看电池容量外,还需要看其标准充放电,最大充放电参数。
在这里插入图片描述
在这里插入图片描述

2.充电设计:
TYPE-C充电口,本来应设计成兼容各类TYPE-C适配器,但由于项目进度较赶,若设计成TYPE-C标准的话除了需要加协议诱导芯片外,还要MCU对不同适配器所能输出的最大功率进行调整,所以经过商议后决定直接买现成的带TYPE-C口的15V/2A输出适配器;
三串锂电池满电电压约为12.6V,由于考虑使用DC-DC降压方案,所以电池管理芯片选择了如韵公司的CN3763芯片;
在这里插入图片描述

3.电机正反转:
可以使用分立元件搭H桥电路来实现,但是程序方面就要进行死区保护,防止上下管直通烧毁,也可以直接使用电机专用IC,其内部基本都会加入过热保护、过流保护、短路保护及死区保护等,该项目我选择了RZ7886对电机进行控制;
在这里插入图片描述

4.电量显示:
通过一个红蓝双色LED灯显示电量情况,锂电池充电情况可以通过电池管理芯片CN3763的CHRG和DONE脚状态来进行是否充满电的判断;而当产品进行工作时,需要通过MCU的ADC功能来检测电池电压判断电量情况,一般单节锂电池的电量判断低于3V时则为低电量,电池电量检测电路如下:
在这里插入图片描述
分别对3串,2串,单节锂电池的电量进行测量,通过相减便可以分别得出3节锂电池的电量,同时使用三极管在产品处于充电或工作状态时才打开电量检测,可以缩小产品待机休眠时的功耗。

5.锂电池产品的低功耗及安规设计
对于锂电池产品,我们公司的功耗要求为待机时小于20uA,实测产品的待机功耗为11uA:

在这里插入图片描述
低功耗的关键点在于硬件电路耗电的地方在满足正常工作条件的情况下电阻值尽量选大,或者使用三极管、MOS管对其进行控制;在MCU方面在休眠前将无关的IO引脚置为高阻输入。
锂电池产品一般要对电池充放电做双重保护,保护方案包括:电池充电管理芯片(BMS)、电池保护IC、过流保护保险丝、NTC监测锂电池温度等。

开发环境:

硬件电路绘制:立创eda(标准版)
软件程序:STM32CUBEMX、Keil-arm

开发过程:

1.使用立创EDA进行原理图绘制:

在这里插入图片描述

部分功能电路介绍:
在这里插入图片描述
这里的作用是当有充电插入时,通过该电路能实现对IO口过压保护的同时,消除部分触发干扰,也能将外部中断的触发边沿延长使单片机能稳定读取到外部中断源。

在这里插入图片描述
该电路位于电池充电管理芯片之前,用于在充电时若检测到电池异常情况(过热、过充)时,而充电管理芯片不起作用时,可以关断充电,实现双重充电保护的作用。

在这里插入图片描述
该电路用于在电池过放之后,充电时由于锂电池已经不够电量支持单片机工作时使用,此时接入充电,VBUS会直接到达LDO处稳压后供MCU工作,而又不至于直接倒灌入电池正极VBAT+。而在正常工作时,VBAT+又因为D1二极管的存在不会流入VBUS,而是正常的流向LDO稳压后供MCU工作。

在这里插入图片描述
此电路为采用NTC+MCU的ADC功能实现温度的监测,监测时将NTC紧贴锂电池才可测量出锂电池的真实温度,我一般设置在超过60℃则禁止其充放电。

在这里插入图片描述
此电路为使用RZ7886实现的电机正反转驱动电路,同时在芯片的接地端接入100mΩ电流采样电阻作为多一重的堵转过流保护。其原理为当电机工作电流为1A时,会有1A电流通过芯片流入采样电阻到地,此时在电阻上就会产生100mV的压降,通过MCU的ADC读取到100mV的电压则可以反推出来此时电机的工作电流为1A。

在这里插入图片描述
此部分作为调试或维修PCB板时断开锂电池连接使用,由于产品化后拆卸锂电池较为麻烦,而又要对电路进行断电,防止焊接时短路锂电池,一般锂电池产品的电路板上都会画为锯齿状,这样焊接时更容易直接用焊锡连接上跳线处,实物如下:
在这里插入图片描述

2.PCB绘制

PCB的尺寸和固定孔一般由公司的结构部门提供,我们只需让其提供对应的DXF格式文件,我们在将其导入到立创EDA的边框层即可,如下:
在这里插入图片描述
在这里插入图片描述
最后完成元器件的布局及走线等,标注好版本日期丝印等,就可完成PCB的绘制了:在这里插入图片描述
同时立创EDA也支持3D模型的查看,让我们看一下到时候的成品PCBA大致样子吧:
在这里插入图片描述
在这里插入图片描述
此时可以将该3D模型输出为obj格式(立创EDA专业版支持输出step格式)供结构部门进行板子尺寸及元器件位置的比对,看是否合适其制作的产品外壳。

3.PCB板打样下单及元器件购买

在结构部门确认好3D模型没问题之后,这边也就可以检查DRC后就开始PCB板的打样了;PCB打样下单成功后紧接着就要对着原理图查找是否有公司元件仓缺失的元件,有的话就要及时下单购买,防止板子回来之后却元器件影响项目进度。

4.使用STM32CUBEMX搭建程序框架

一般PCB板打样不加急的话时间大概为3天左右,在这段等待的时间内,也不能无所事事,可以先进行程序框架的搭建。

对着原理图将各部分外设配置好,配置好时钟树等等:
在这里插入图片描述

在生成工程的时候,记得勾选Set all free pins as analog,可以将没有配置功能的IO口置为高阻态降低功耗。
在这里插入图片描述

5.样板焊接

等PCB样板及元器件都送到后,就可以焊接样板了,这里没什么好写的,拿起烙铁直接动手就行,成品如下图:
在这里插入图片描述
在这里插入图片描述

6.程序编写

首先新建一个.h头文件,将所有输入输出IO口的操作进行重新定义,这样进行程序工作逻辑编写时才不容易乱:

#ifndef __centre_ctrl_H
#define __centre_ctrl_H#include "stm32f1xx.h"
#include "main.h"
#include "stdbool.h"enum mode
{stop=0,cw_work=1,ccw_work=2,cw=3,ccw=4,charing=5,re_stop=6
};enum CE
{off=0,on=1
};#define LED_CW_ON          HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET)  
#define LED_CW_OFF         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET)
#define LED_CCW_ON         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET)  
#define LED_CCW_OFF        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET)
#define LED_BAT_RED_ON     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,GPIO_PIN_RESET)  
#define LED_BAT_RED_OFF    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,GPIO_PIN_SET)
#define LED_BAT_BLUE_ON    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET)  
#define LED_BAT_BLUE_OFF   HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_SET)#define motor_BI_ON         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET)  
#define motor_BI_OFF        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET)
#define motor_FI_ON         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET)  
#define motor_FI_OFF        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET)#define charge_CE_ON        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET)  
#define charge_CE_OFF       HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET)#define NTC_VC_ON         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET)  
#define NTC_VC_OFF        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET)
#define VC3_ON        		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET)  
#define VC3_OFF       		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET)
#define VC2_ON        		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET)  
#define VC2_OFF       		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET)
#define VC1_ON        		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET)  
#define VC1_OFF       		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET)#define CW_key            HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13)
#define CCW_key           HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_12)#define charing_io           HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9)
#define CHRG              HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)
#define DONE              HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15)void motolr_LED_display(void);
void charing_read(void);//识别是否充电,用于在工作时接入充电,可用来执行充电时是否允许继续工作
void ADC_CE(bool CE);//使能ADC,会增大耗电量
void work_battery_indicator(void);
void charing_battery_indicator(uint16_t blink_time);
void ADC_analog(void);//进入STOP时将ADC脚置为高阻态,省电; 唤醒时需要初始化ADC#endif

定义好各种IO口操作后便可以创建C文件编写各种功能程序了,这里我主要将程序分开为3个C文件,一个进行电池电量和温度的ADC读取,一个做低功耗的各种操作,还有一个则进行按键和LED灯的操作:

centre_adc.c内容:

#include <stm32f1xx.h>
#include <centre_ctrl.h>
#include <centre_adc.h>
#include "stdio.h"
#include "centre_low_power.h"uint16_t ADC_time;//ADC采样时间间隔
uint8_t uart_printf_time;//串口输出时间间隔uint16_t ADC_SUM[100];//
uint16_t vol_NTC;float ADC_NTC,ADC_BAT3,ADC_BAT2,ADC_BAT1,ADC_OCP;
uint16_t D_ADC_NTC,D_ADC_BAT3,D_ADC_BAT2,D_ADC_BAT1,D_ADC_current;//ADC值取整,即ADC值*1000
uint16_t I_current;
uint8_t ocp_sum;//过流计数值,多次过流后再进行保护,防止ADC一次错误读取就保护
uint8_t ot_sum=5;//NTC过温计数值
uint8_t bat1_error_sum,bat2_error_sum,bat3_error_sum;//电池异常计数值extern ADC_HandleTypeDef hadc1;
extern DMA_HandleTypeDef hdma_adc1;
extern TIM_HandleTypeDef htim3;extern uint8_t machine_mode;uint8_t battery_level_read_sum;
uint8_t power_work_level_check;
uint8_t power_work_level=6;//工作时的电量档位数,用来避免电量档位过度时在两档之间不稳定显示的问题uint8_t charing_battery_level_read_sum;
uint8_t power_charing_level_check;
uint8_t power_charing_level=7;//充电时的电量档位数uint16_t NTC_voltage_celsius(uint8_t temp)//输入摄氏度,自动匹配出对应采集电压  线路为3.3V--100K--100K B3950 NTC--GND
{switch(temp){case 0:   vol_NTC=2530;break; //单位mVcase 10:	vol_NTC=2200;break;case 20:  vol_NTC=1833;break;case 30:	vol_NTC=1472;break;case 40:	vol_NTC=1143;break;case 50:	vol_NTC=874;break;case 60:	vol_NTC=660;break;case 70:	vol_NTC=479;break;case 80:	vol_NTC=367;break;case 90:	vol_NTC=272;break;case 100: vol_NTC=207;break;}return vol_NTC;
}void BAT_OT_protect(uint8_t temp)//形参为设置温度
{if(D_ADC_NTC<(NTC_voltage_celsius(temp)))//温度比temp高{ot_sum++;}if(D_ADC_NTC>=(NTC_voltage_celsius(temp)))//温度比temp低,未超温{ot_sum--;}if(ot_sum>=10)//连续检测到超温,禁止充电{ot_sum=5;charge_CE_OFF;//关闭}if(ot_sum==0)//连续检测到未超温,恢复充电{ot_sum=10;charge_CE_ON;//开启}
}void OC_protect(uint16_t sum)//电机工作过流保护,形参为过流保护值,单位:mA
{if(machine_mode==cw_work||machine_mode==ccw_work){if(I_current>=sum){ocp_sum++;}else if(I_current<sum){ocp_sum=0;}if(ocp_sum>=5)//检测到超过5次过流,就停止电机工作,根据要求直接STOP系统{ocp_sum=0;motor_BI_OFF;motor_FI_OFF;LED_CCW_OFF;LED_CW_OFF;LED_BAT_BLUE_OFF;LED_BAT_RED_ON;HAL_Delay(500);LED_BAT_RED_OFF;HAL_Delay(500);LED_BAT_RED_ON;HAL_Delay(500);LED_BAT_RED_OFF;HAL_Delay(500);LED_BAT_RED_ON;HAL_Delay(500);LED_BAT_RED_OFF;machine_mode=re_stop;//enter_stop_mode();}}
}void three_bat_check()//分别检测3颗电池的电量是否异常
{if(D_ADC_BAT1<2500||D_ADC_BAT1>4400)//第一节电池的电量如果低于2500mV或者超过4400mV,证明异常{bat1_error_sum++;}else bat1_error_sum=0;if(D_ADC_BAT2-D_ADC_BAT1<2500||D_ADC_BAT2-D_ADC_BAT1>4400)//第二节电池的电量如果低于2500mV或者超过4400mV,或者电池间电量偏差较大,证明异常{bat2_error_sum++;}else bat2_error_sum=0;if(D_ADC_BAT3-D_ADC_BAT2<2500||D_ADC_BAT3-D_ADC_BAT2>4400)//第三节电池的电量如果低于2500mV或者超过4400mV,或者电池间电量偏差较大,证明异常{bat3_error_sum++;}else bat3_error_sum=0;if(bat1_error_sum>5||bat2_error_sum>5||bat3_error_sum>5)//只要有一颗电池出现异常,则进入STOP{bat1_error_sum=0;bat2_error_sum=0;bat3_error_sum=0;machine_mode=re_stop;}
}void ADC_printf()
{printf("ADC_NTC=%1.3f\r\n",ADC_NTC);printf("D_ADC_BAT3=%d\r\n",D_ADC_BAT3);printf("D_ADC_BAT2=%d\r\n",D_ADC_BAT2);printf("D_ADC_BAT1=%d\r\n",D_ADC_BAT1);printf("I_current=%d\r\n",I_current);//mAprintf("\r\n");
}void ADC_rounding()//ADC值*1000
{D_ADC_NTC=ADC_NTC*1000;D_ADC_BAT3=ADC_BAT3*1000;D_ADC_BAT2=ADC_BAT2*1000;D_ADC_BAT1=ADC_BAT1*1000;D_ADC_current=ADC_OCP*1000;I_current=D_ADC_current*10;//采样电阻为0.1Ω,单位为mA
}void ADC_collect()//读出ADC的电压采样值
{uint8_t j;for(j=0;j<100;){ADC_NTC+=ADC_SUM[j++];ADC_BAT3+=ADC_SUM[j++];//参考电压3.3V,12位精度	ADC_BAT2+=ADC_SUM[j++];//参考电压3.3V,12位精度	ADC_BAT1+=ADC_SUM[j++];//参考电压3.3V,12位精度	ADC_OCP+=ADC_SUM[j++];//参考电压3.3V,12位精度}ADC_NTC/=20;	ADC_BAT3/=20;	ADC_BAT2/=20;	ADC_BAT1/=20;	ADC_OCP/=20;ADC_NTC=ADC_NTC*3.3f/4096;ADC_BAT3=(ADC_BAT3*3.3f/4096)*7.5;//BAT的值由680K和100K分压得到,则应乘以7.8才为实际电压ADC_BAT2=(ADC_BAT2*3.3f/4096)*7.5;//BAT的值由680K和100K分压得到,则应乘以7.8才为实际电压ADC_BAT1=(ADC_BAT1*3.3f/4096)*7.5;//BAT的值由680K和100K分压得到,则应乘以7.8才为实际电压ADC_OCP=ADC_OCP*3.3f/4096;ADC_rounding();//ADC值*1000//ADC_printf();}void ADC_work(uint16_t temp,uint8_t printf_time)//采样时间间隔,串口输出时间间隔
{//ADC_time++;if(ADC_time>=temp)//temp毫秒读取一次ADC数据{HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC_SUM,100);//开启ADC-DMAADC_collect();//HAL_ADC_Stop_DMA(&hadc1);//关闭ADC-DMAADC_time=0;if(machine_mode==cw_work||machine_mode==ccw_work){OC_protect(2500);//电机工作过流保护,形参为过流保护值,单位:mA}if(machine_mode==charing){BAT_OT_protect(50);//形参为设置温度}three_bat_check();//分别检测3颗电池的电量是否异常,异常则会进入STOPuart_printf_time++;if(uart_printf_time==printf_time){ADC_printf();uart_printf_time=0;}}			
}

centre_low_power.c内容:

#include <stm32f1xx.h>
#include <centre_low_power.h>
#include <centre_ctrl.h>
#include <stdbool.h>
#include "main.h"extern TIM_HandleTypeDef htim3;
extern ADC_HandleTypeDef hadc1;uint8_t machine_mode=0;uint16_t wakeup_read_time;//设定系统唤醒后一段时间内进行模式判断
uint16_t wakeup_CW_key_sum;
uint16_t wakeup_CCW_key_sum;
uint16_t wakeup_charge_sum;uint16_t key_stop_time;//检测从工作模式进入待机模式的消抖时间void enter_stop_mode(void)//进入stop模式
{HAL_TIM_Base_Stop_IT(&htim3);//1ms//关闭所有耗电控制motor_BI_OFF;motor_FI_OFF;LED_BAT_BLUE_OFF;LED_BAT_RED_OFF;LED_CCW_OFF;LED_CW_OFF;ADC_CE(off);//关闭ADC使能IOcharge_CE_OFF;//关闭所有耗电控制//置标志位machine_mode=stop;//将mode置为stop后下次唤醒后才可进行唤醒模式判别wakeup_read_time=0;wakeup_CCW_key_sum=0;wakeup_CW_key_sum=0;wakeup_charge_sum=0;HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);HAL_ADC_Stop_DMA(&hadc1);//关闭ADC-DMA//ADC_analog();//进入STOP时将ADC脚置为高阻态,省电; 唤醒时需要初始化ADC__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI);HAL_Delay(50);//MX_ADC1_Init();//HAL_TIM_Base_Start_IT(&htim3);//1ms
}void wakeup_mode_check(uint16_t time,uint16_t check_sum)//形参分别为总检测时间,判定为成功的检测次数
{ //只要系统被唤醒,就检测是按键唤醒,还是充电唤醒if(machine_mode==stop){if(wakeup_read_time<time){wakeup_read_time++;if(!CW_key&&CCW_key&&!charing_io)//如果为正转按键按下唤醒{wakeup_CW_key_sum++;wakeup_CCW_key_sum=0;wakeup_charge_sum=0;}if(CW_key&&!CCW_key&&!charing_io)//如果为反转按键按下唤醒{wakeup_CCW_key_sum++;wakeup_CW_key_sum=0;wakeup_charge_sum=0;}if(charing_io)//如果为充电唤醒{wakeup_charge_sum++;wakeup_CW_key_sum=0;wakeup_CCW_key_sum=0;}if(CW_key&&!charing_io&&CCW_key)//为按键或充电唤醒消抖{wakeup_CW_key_sum=0;wakeup_CCW_key_sum=0;wakeup_charge_sum=0;}}if(wakeup_read_time>=time)//检测时间到达后{if(wakeup_charge_sum<check_sum&&wakeup_CW_key_sum<check_sum&&wakeup_CCW_key_sum<check_sum)//如果判断为干扰误触发,则重新进入停机模式{machine_mode=re_stop;}if(wakeup_CW_key_sum>=check_sum&&wakeup_CCW_key_sum<check_sum&&wakeup_charge_sum<check_sum)//为正转按键唤醒{wakeup_CW_key_sum=0;machine_mode=cw;//HAL_TIM_Base_Stop_IT(&htim3);//1ms  在这里停止定时器是为了防止识别到按键按下后,在等待松手时定时器中断又执行wakeup_mode_check()导致的异常}if(wakeup_CCW_key_sum>=check_sum&&wakeup_CW_key_sum<check_sum&&wakeup_charge_sum<check_sum)//为反转按键唤醒{wakeup_CCW_key_sum=0;machine_mode=ccw;//HAL_TIM_Base_Stop_IT(&htim3);//1ms}if(wakeup_charge_sum>=check_sum&&wakeup_CCW_key_sum<check_sum&&wakeup_CW_key_sum<check_sum)//为充电唤醒{wakeup_charge_sum=0;machine_mode=charing;charge_CE_ON;}wakeup_read_time=0;}}if(machine_mode==charing)//如果进入充电模式,判断什么时候退出了充电模式,则进入STOP{if(!charing_io){wakeup_charge_sum++;}if(charing_io)//消抖{wakeup_charge_sum=0;}if(wakeup_charge_sum>=30)//判断为断开充电后进入待机模式{wakeup_charge_sum=0;machine_mode=re_stop;}}}void charing_stop()//拔掉充电后进入STOP
{if(machine_mode==charing)//如果进入充电模式,判断什么时候退出了充电模式,则进入STOP{if(!charing_io){wakeup_charge_sum++;}if(charing_io)//消抖{wakeup_charge_sum=0;}if(wakeup_charge_sum>=30)//判断为断开充电后进入待机模式{wakeup_charge_sum=0;machine_mode=re_stop;}}
}void  key_motor_work()//按下CW_key或CWW_key后,machine唤醒等待松手后电机开始运转
{if(machine_mode==cw){while(!CW_key);//HAL_TIM_Base_Start_IT(&htim3);//1msmotor_FI_OFF;motor_BI_ON;machine_mode=cw_work;}if(machine_mode==ccw){while(!CCW_key);//HAL_TIM_Base_Start_IT(&htim3);//1msmotor_BI_OFF;motor_FI_ON;machine_mode=ccw_work;}
}void key_stop()//在machine_mode处于cw或ccw状态下,按CW_key或CCW_key使其进入stop,这里要看需求是否CW状态下只能通过CW_key关机还是均可以关机
{if(machine_mode==cw_work||machine_mode==ccw_work){if(!CW_key||!CCW_key){key_stop_time++;}if(CW_key&&CCW_key){key_stop_time=0;}}if(key_stop_time>=20){while(!CW_key||!CCW_key);key_stop_time=0;machine_mode=re_stop;	}
}

centre_ctrl.c内容:

#include <stm32f1xx.h>
#include <centre_ctrl.h>
#include <stdbool.h>
#include "centre_low_power.h"
#include "centre_adc.h"extern uint8_t machine_mode;
extern uint16_t D_ADC_NTC,D_ADC_BAT3,D_ADC_BAT2,D_ADC_BAT1,D_ADC_current;//ADC值取整,即ADC值*1000uint8_t charing_read_sum;
uint16_t led_blink_time;uint16_t chrg_sum;
uint16_t done_sum;void motolr_LED_display()
{if(machine_mode==cw_work){LED_CCW_OFF;LED_CW_ON;}if(machine_mode==ccw_work){LED_CW_OFF;LED_CCW_ON;}
}void charing_read()//识别是否充电,用于在工作时接入充电,可用来执行充电时是否允许继续工作
{if(charing_io){charing_read_sum++;}if(!charing_io){charing_read_sum=0;}if(charing_read_sum>=10){machine_mode=charing;charing_read_sum=0;motor_BI_OFF;motor_FI_OFF;LED_CCW_OFF;LED_CW_OFF;}
}void ADC_CE(bool CE)//使能ADC读取,会增大耗电量
{if(CE==on){	NTC_VC_ON;VC3_ON;VC2_ON;VC1_ON;}if(CE==off){NTC_VC_OFF;VC3_OFF;VC2_OFF;VC1_OFF;}
}void work_battery_indicator()
{if(D_ADC_BAT3<=10000)//小于10V{LED_BAT_BLUE_OFF;LED_BAT_RED_ON;}if(D_ADC_BAT3>10000)//大于10V{LED_BAT_RED_OFF;LED_BAT_BLUE_ON;}
}void charing_battery_indicator(uint16_t blink_time)
{if(!CHRG&&DONE)//处于充电状态{//led_blink_time++;//LED_BAT_BLUE_OFF;chrg_sum++;done_sum=0;}if(!DONE&&CHRG)//充电完成{chrg_sum=0;done_sum++;led_blink_time=0;}if(chrg_sum>=50){chrg_sum=50;//防止其持续累加增大LED_BAT_BLUE_OFF;if(led_blink_time<=blink_time){LED_BAT_RED_ON;}if(led_blink_time>blink_time&&led_blink_time<=blink_time*2){LED_BAT_RED_OFF;}if(led_blink_time>blink_time*2){led_blink_time=0;}}if(done_sum>=50)//充电完成{done_sum=50;LED_BAT_RED_OFF;LED_BAT_BLUE_ON;led_blink_time=0;}
}

通过此种封装函数的方式进行程序编写的好处是不仅函数可以通过输入不同形参输入在不同模式下进行调用,也可以使主程序看起来简洁,主程序如下:

int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_ADC1_Init();MX_USART1_UART_Init();//MX_IWDG_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */HAL_Delay(500);HAL_ADCEx_Calibration_Start(&hadc1);//DMA自校准///HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC_SUM,100);//开启ADC-DMAHAL_TIM_Base_Start_IT(&htim3);//1ms/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_Delay(3);//HAL_IWDG_Refresh(&hiwdg);//printf("machine_mode=%d\r\n",machine_mode);if(machine_mode==re_stop){enter_stop_mode();}if(machine_mode==cw||machine_mode==ccw)//识别到为正转或者反转后,开启工作模式{key_motor_work();}if(machine_mode==cw_work||machine_mode==ccw_work)//处于工作模式中{ADC_CE(on);//使能ADC,会增大耗电量ADC_work(10,100);//采样时间间隔,串口输出时间间隔motolr_LED_display();//正反转指示灯work_battery_indicator();//电池电量指示灯key_stop();//按下按键,进入stop		charing_read();//识别是否充电}if(machine_mode==charing)//由于充电指示灯有闪烁时间要求,所以将其显示放定时器中断中执行{ADC_CE(on);//使能ADC,会增大耗电量ADC_work(30,33);//采样时间间隔,串口输出时间间隔charing_battery_indicator(1000);//charing_stop();}}/* USER CODE END 3 */
}

可以看到主程序内容浅显易懂,通过判断产品处于什么模式下,比如充电模式,工作模式等,则调用相应需执行或判断的函数进行操作即可。

7.硬件+软件联调

对于锂电产品,接入电池前要对电路的充电和保护功能进行测试,正常后方可接入锂电池:
在这里插入图片描述
可以采用DC稳压电源+电子负载的方式进行测试,输入DC15V电压模拟TYPE-C充电,电子负载调恒压模式,电压范围在三串锂电池并联的正常电压范围内,可以看到充电电流为1A,当将恒压电压调整到超过12.6V后电流变为0,证明充电管理芯片正常工作了,此时便可将电池接入电路了。电池接入前要保证3节电池的电压接近,若有偏差可以对电池分别进行充放电使3节电池电压相等再进行串联。

同时程序不可能一次性就编写无误,一般都是各个功能模块编完之后进行仿真、烧入MCU配合硬件联调、调用串口读取数据等;
此时掏出自制的隔离型ST-LINK V2.1对样板的数据进行串口读取(在未引入外部电源时,无须使用带隔离的模块,但在引入外部电源后,如接入电池,或者使用市电进行供电的产品,最好使用带隔离的模块,否则容易因为地电压不一致导致大电流流动烧毁电脑或样板)


在这里插入图片描述

串口线路连接好后,按下按键唤醒样板工作,看到比克大魔王手发红光,证明正常接到串口数据了,此时我们通过串口助手查看一下数据:

在这里插入图片描述
通过串口可以读出此时三串锂电池总电压为10.9V左右,而电机工作电流在370mA,都处于正常范围,证明数据是正确的。

8.最后装机交货

装机交货没什么好写的,讲讲为什么要写这么繁琐的文章吧。对实际项目开发过程的完整记录旨在让每一位刚入行电子或者对电子感兴趣的人可以了解到一个完整的项目开发过程。相信每一位入门电子行业的工程师一开始肯定都是很渴望自己能独立开发项目的,但并不是每个人都能一入行就得到公司信任获得开发项目的机会,像我刚毕业后在第一家公司虽然挂名电子工程师,但实际却一直做着打杂测试的工作,当时十分想了解一个完整的项目开发需要经历哪些过程但却一直没有机会 。一次完整的项目经验不仅可以令自己获得成就感,也可以考验你硬件软件各方面的能力,在项目中也会遇到各种问题,锻炼你分析问题原因的能力。

当然此篇文章还有很多写得不够严谨仔细的地方,后期如果有较为有趣的电子开发项目,我也会尽量更为仔细把开发过程记录下来,分享给大家。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/23103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何根据业务需求,轻松挑选SSL证书?

在当今数字化时代&#xff0c;网站的安全性愈发受到重视。SSL证书作为网站安全的“守门员”&#xff0c;不仅能保护用户数据不被窃取&#xff0c;还能提升网站的信任度。但面对市场上琳琅满目的SSL证书产品&#xff0c;如何根据业务需求挑选合适的证书呢&#xff1f;今天&#…

流量分析——一、蚁剑流量特征

君衍. 一、Webshell特征流量分析二、环境介绍三、使用Wireshark进行流量分析1、环境说明2、HTTP追踪流分析3、蚁剑请求体中代码块解读 四、使用BurpSurite进行流量分析1、环境配置2、抓包分析 六、总结 一、Webshell特征流量分析 对于重保、护网等攻防演练的防守方来说&#x…

wps:样式集的使用【笔记】

wps&#xff1a;样式集的使用【笔记】 前言版权推荐wps&#xff1a;样式集的使用1拿到一个内容模板2修改样式集3修改样式的详细说明4保存样式集5应用样式集 说明另外最后 前言 2024-6-5 23:36:20 以下内容源自《【笔记】》 仅供学习交流使用 版权 禁止其他平台发布时删除以…

LLM大语言模型(十六):最新开源 GLM4-9B 本地部署,带不动,根本带不动

目录 前言 本机环境 GLM4代码库下载 模型文件下载&#xff1a;文件很大 修改为从本地模型文件启动 启动模型cli对话demo 慢&#xff0c;巨慢&#xff0c;一个字一个字的蹦 GPU资源使用情况 GLM3资源使用情况对比 前言 GLM-4-9B 是智谱 AI 推出的最新一代预训练模型 …

Apache Superset:数据可视化的现代开源解决方案

Superset&#xff1a; 洞察数据&#xff0c;一目了然- 精选真开源&#xff0c;释放新价值。 概览 Apache Superset 是一个由 Apache 软件基金会支持的开源数据可视化和数据探索平台。它允许用户以直观的方式构建丰富的数据报告和仪表板&#xff0c;支持从多种数据源中提取数据…

2024版本---LabVIEW 软件安装及使用教程

目录 第1章 LabVIEW 软件安装及使用教程 1. 简介 2. 安装教程 2.1 下载 LabVIEW 2024 版本 2.2 安装 LabVIEW 3. 激活 LabVIEW 4. LabVIEW 基本使用教程 4.1 用户界面介绍 4.2 创建一个简单的 VI&#xff08;虚拟仪器&#xff09; 4.3 数据采集示例 5. 进阶功能介绍…

如何将华为Ascend手机的短信和联系人安全传输到电脑

华为Ascend系列手机以其流畅的使用体验、光滑的触感以及轻巧的设计赢得了市场的青睐。不仅如此&#xff0c;Ascend系列手机还以亲民的价格和出色的用户体验&#xff0c;搭载了众多先进功能&#xff0c;如Ascend P6的4.7英寸大屏、海思四核处理器、2GB RAM和800万像素摄像头等。…

66、API攻防——接口安全阿里云KEYPostmanDVWS

文章目录 一、工具使用——Postman自动化测试二、安全问题——Dvws泄露&鉴权&XXE三、安全问题——阿里KEY信息泄露利用 dvws-node 一、工具使用——Postman自动化测试 二、安全问题——Dvws泄露&鉴权&XXE 路径中出现/api/&#xff0c;一般都是接口。 请求包是…

宏集Panorama SCADA:个性化定制,满足多元角色需求

前言 在考虑不同人员在企业中的职能和职责时&#xff0c;他们对于SCADA系统的需求可能因其角色和工作职责的不同而有所差异。在SCADA系统的设计和实施过程中&#xff0c;必须充分考虑和解决这种差异性。 为了满足不同人员的需求, 宏集Panorama SCADA平台具备灵活的功能和定制…

Spring Boot 应用打 WAR 包后无法注册到 Nacos怎么办

你好&#xff0c;我是柳岸花开。 在微服务架构中&#xff0c;服务注册与发现是至关重要的一环。Nacos 作为阿里巴巴开源的注册中心&#xff0c;能够很好地满足这一需求。然而&#xff0c;在将 Spring Boot 应用打包成 WAR 部署到外部服务器时&#xff0c;可能会遇到服务无法注册…

基于R语言BIOMOD2 及机器学习方法的物种分布模拟

BIOMOD2是一个R软件包&#xff0c;用于构建和评估物种分布模型&#xff08;SDMs&#xff09;。它集成了多种统计和机器学习方法&#xff0c;如GLM、GAM、SVM等&#xff0c;允许用户预测和分析物种在不同环境条件下的地理分布。通过这种方式&#xff0c;BIOMOD帮助研究者评估气候…

AWS EMR Serverless

AWS概述 EMR Serverless 简介 在AWS概述一文中简单介绍过AWS EMR, 它是AWS提供的云端大数据平台。借助EMR可以设置集群以便在几分钟内使用大数据框架处理和分析数据。创建集群可参考官方文档&#xff1a;Amazon EMR 入门。但集群创建之后需要一直运行&#xff0c;用户需要管理…

Nginx+Tomcat负载均衡、动静分离集群

目录 1.Nginx负载均衡 1.1 负载均衡概念 1.2 负载均衡原理 1.3 Nginx配置反向代理 1.3.1 反向代理概念 1.3.2 反向代理主要参数 2.Nginx动静分离 2.1 动静分离的概念 2.2 Nginx 静态处理优势 2.3 动静分离原理 3. NginxTomcat动静分离的实验设计 3.1 准备三台虚拟机…

js 选择一个音频文件,绘制音频的波形,从右向左逐渐前进。

选择一个音频文件&#xff0c;绘制波形&#xff0c;从右向左逐渐前进。 完整代码&#xff1a; <template><div><input type"file" change"handleFileChange" accept"audio/*" /><button click"stopPlayback" :…

Typora编辑的markdown文档莫名其妙消失或未保存--解决方案【亲测可行】

由于误触键盘导致文件关闭&#xff0c;打开文件之后发现里面文字全没了~气死了&#xff01;&#xff01;&#xff01;&#xff01; 可以通过如下方法解决&#xff01; 一、打开typora 二、【文件】-【偏好设置】 三、点击恢复未保存的草稿&#xff0c;找到最近的文件复制粘贴…

django 内置 JSON 字段 使用场景

Django 内置的 JSON 字段&#xff08;JSONField&#xff09;是在 Django 3.1 版本中引入的&#xff0c;用于处理 JSON 格式的数据。JSONField 允许在数据库表中存储和查询 JSON 数据&#xff0c;并且在与 Python 代码交互时自动转换为合适的 Python 数据类型。以下是一些常见的…

2024050302-重学 Java 设计模式《实战享元模式》

重学 Java 设计模式&#xff1a;实战享元模式「基于Redis秒杀&#xff0c;提供活动与库存信息查询场景」 一、前言 程序员&#x1f468;‍&#x1f4bb;‍的上下文是什么&#xff1f; 很多时候一大部分编程开发的人员都只是关注于功能的实现&#xff0c;只要自己把这部分需求…

Facebook商城号怎么做?思路与操作分析

2016 年&#xff0c;Facebook打造了同名平台 Facebook Marketplace。通过利用 Facebook 现有的庞大客户群&#xff0c;该平台取得了立竿见影的成功&#xff0c;每月访问量将超过 10 亿。对于个人卖家和小企业来说&#xff0c;Facebook Marketplace是一个不错的销货渠道&#xf…

【二进制部署k8s-1.29.4】十一、metallb的安装部署

文章目录 简介 一.安装metallb二.配置metallb三.验证metallb 简介 本章节主要讲解安装metallb-v0.7.1的安装&#xff0c;metallb算是平民版的负载均衡&#xff0c;用于测试、访问量较小的情况还是比较不错的&#xff0c;但是对于请求量比较的时候&#xff0c;由于流量都集中在一…

猫熊超市管理系统

import java.util.Scanner;//增加商品类 //此类用来录入一个商品的所有属性&#xff0c;并作为结果对其返回 public class Add {public Goods add1() {Scanner scanner new Scanner(System.in);System.out.println("请输入商品名称");String name scanner.next();S…