声明:本人水平有限,博客可能存在部分错误的地方,请广大读者谅解并向本人反馈错误。
首先祝大家新年快乐,因为我也快放假了,驱动板只能是开学之后再去测试了,本篇博客应该是本专栏年前的最后一篇了
一、 硬件部分
1.1 原理图
废话就先不多说了,原理图如下:
原理图和上一版的原理图最大的改变是修改了MCU芯片,由103改为了405;并且继续保留了CAN芯片和FLASH芯片,对后续的开发肯定有用。
1.2 PCB
今天刚刚焊接起来,测试时出现了一点问题,就是在使用keil下载时会出现"Cannot load flash programming algorithm"问题,这个问题困扰了我一下午。尝试了许多方法,最后把原本下载的F4xx固件2.17.1版本卸载,安装了2.17.0版本,并且在keil下载按如下配置:
其实也没有做什么特殊的设置,然后就好了…
二、软件部分
2.1 代码
具体的代码我会在整体都做完之后进行开源的,下面我就说一下整体的代码功能:
- 使用ESP32的BLE功能,接收手机发送的指令
- ESP32会将接收的指令转化为通信协议发送给STM32
- STM32解析指令,并根据不同的指令进行不同的功能(目前实现的功能是切换OLED的显示界面)
2.2 部分效果展示
手机发送"speed"指令:
ESP32会将"speed"指令在OLED上显示,并且发送指令给STM32:
STM32接收到指令会显示速度的波形(图片看不出来,所以波形不是很明显)
还有其他指令,比如:
- focfb->STM32显示反馈的数据,比如电流、转速、角度、转矩
- Acurrent->显示A相采集的电流值
- Bcurrent->显示B相采集的电流值
- setpid->设置PID的参数(目前还不成熟,后续开发)
下面再放几个截图:
2.3 部分代码
在这先放上main函数和中断等部分函数:
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_SPI1_Init();
MX_TIM1_Init();
MX_TIM6_Init();
MX_ADC1_Init();
MX_TIM3_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
// OLED 初始化
OLED_GPIO_Init();
OLED_Init();
OLED_Display_On();
OLED_Set_Pos(0,0);
OLED_Clear();
HAL_UART_Receive_IT(&huart2, rxBuffer,RX_CMD_LEN); // 中断方式接收RX_CMD_LEN个字符
// 初始化定时器
HAL_TIM_Base_Start_IT(&htim6); // 中断方式启动tim2
HAL_Delay(100);
// 开启PWM输出
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); // 输出PWM波形
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_3);
HAL_Delay(100);
// 显示菜单
Mune(Open); // 显示菜单
HAL_Delay(200);
// 逆时针旋转
// COUNTER_CLOCKWISE_ROTE;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(defaultOledPage) Mune(defaultOledPage); // 显示默认画面
else Mune(selectionOledPage);
}
/* USER CODE END 3 */
}
timer中断:
void TIM6_DAC_IRQHandler(void)
{
/* USER CODE BEGIN TIM6_DAC_IRQn 0 */
/* USER CODE END TIM6_DAC_IRQn 0 */
HAL_TIM_IRQHandler(&htim6);
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */
htim1.Instance->CCR1 = 100;
htim1.Instance->CCR2 = 50;
htim1.Instance->CCR3 = 80;
Programe_Run();// 获取角度
/* USER CODE END TIM6_DAC_IRQn 1 */
}
解析角度与速度:
void Programe_Run(void)
{
uint8_t dect= 0;
float preAngel = 0.0;
float nowAngel = 0.0;
float dT = 0.01;
preAngel = degress;
dect = detectMagnet();
rawdata = getRawAngle();
degress = convertRawAngleToDegrees(rawdata);
velocity = Velocity(preAngel,degress,dT,counterclockwise);
}
解析通信协议指令:
void on_UART_IDLE(UART_HandleTypeDef *huart) //检测IDLE中断事件并处理
{
//注意,这里不能使用函数 __HAL_UART_GET_FLAG(),因为上位机连续发5个字节,串口接收到1个字节后虽然打开了IDLE中断,
// 但是因为后续连续发送数据,所以IDLE中断挂起标志位并不会被置位
if(__HAL_UART_GET_IT_SOURCE(huart,UART_IT_IDLE) == RESET) //判断IDLE中断是否被开启
return;
__HAL_UART_CLEAR_IDLEFLAG(huart); //清除IDLE标志
__HAL_UART_DISABLE_IT(huart, UART_IT_IDLE); //禁止IDLE中断
if (rxCompleted) //接收到了1个字符
{
uint8_t ch=rxBuffer[0];
if (ch==0x6A) //起始符
rxBufPos=0; //存储位置复位
if (rxBufPos<PRO_CMD_LEN) //PRO_CMD_LEN=5
{
proBuffer[rxBufPos]=ch; //存储到处理指令缓存区
rxBufPos++; //存储位置移动
if (ch==0x70) //结束符
{
parseCMD(); // 解析数据
// HAL_UART_Transmit(huart,proBuffer,strlen(proBuffer),200); //上传接收到的指令
// HAL_Delay(10); //需适当延时,否则updateRTCTime()函数处理可能出错
}
}
rxCompleted = RESET;
HAL_UART_Receive_IT(huart, rxBuffer,RX_CMD_LEN); //再次接收
}
}
/*数据格式:<0x6A><0x61><0x6D><总数据字节数><指令序列><对应指令><...><...><0x70>*/
void parseCMD(void)
{
int i=0;
// 将数据指令保存起来
dateSumNumber = proBuffer[1]; // 数据长度
// rxCmdBuf数组从序列号开始保存
for(i=0;i<(dateSumNumber-3);i++) rxCmdBuf[i] = proBuffer[i+2]; // 指令解析
cmdSequence = rxCmdBuf[0]; // 序列号
// 根据序列号选择不同的指令
switch(cmdSequence){
case 0x01: { // FOC反馈显示
defaultOledPage = 0; //不再显示默认界面
selectionOledPage = rxCmdBuf[1]; // 根据指令修改OLED界面
};break;
case 0x02: { // 电流页面显示
defaultOledPage = 0; //不再显示默认界面
selectionOledPage = rxCmdBuf[1]; // 根据指令修改OLED界面
};break;
case 0x03:{ // 速度页面显示
defaultOledPage = 0; //不再显示默认界面
selectionOledPage = rxCmdBuf[1]; // 根据指令修改OLED界面
};break;
case 0x04:{ // 修改PID参数
defaultOledPage = 0; //不再显示默认界面
selectionOledPage = rxCmdBuf[1]; // 根据指令修改OLED界面
};break;
}
}
OLED切换界面:
void Mune(int Page)
{
// int i = 0;
switch(Page){
case 0: { // 开机页面
oledClearMulPage(Page);
OLEDDisBinBMP(60,128,gImage_Jam,Middle,Normal_Color); //
HAL_Delay(1000); // 延时3s
OLED_Clear();
OLEDDisBinBMP(63,64,gImage_Laugh_HLL,Middle,Inverse_Color);
HAL_Delay(1000); // 延时3s
OLED_Clear();
oled_print_chinese(0,0,16,UPC[0],8,Inverse_Color);
oled_print_chinese(0,2,16,KongZhiXueYUan[0],7,Normal_Color);
oled_print_chinese(0,4,16,AI[0],4,Inverse_Color);
oled_print_chinese(0,6,16,Name[0],3,Normal_Color);
HAL_Delay(1500); // 延时3s
OLED_Clear();
};break;
case 1:{ // 显示FOC的反馈值
// 清OLED
oledClearMulPage(Page);
oled_print_chinese(0,0,16,Speed[0],2,Normal_Color);
oled_print_chinese(0,2,16,Current[0],2,Inverse_Color);
oled_print_chinese(0,4,16,Angle[0],2,Normal_Color);
oled_print_chinese(0,6,16,Torque[0],2,Inverse_Color);
// 测试使用
OLED_ShowNum(36,0,velocity,4,16);
OLED_ShowNum(36,2,aFinalValue,4,16);
OLED_ShowNum(36,4,degress,4,16);
OLED_ShowNum(36,6,cFinalValue,4,16);
// OLED_ShowNum(36,6,cmdSequence,4,16); // 接收序列号
OLEDDisBinBMP(58,58,gImage_Kong_Long,Right,Normal_Color); //
if(count<1000) count += 50;
else count = 0;
HAL_Delay(10);
};break;
case 2:{ // 显示电流采集值
oledClearMulPage(Page);
if(rxCmdBuf[2]==0x0A)
{
// 坐标
OLEDDisBinBMP(58,128,gImage_Current_A,Middle,Inverse_Color);
for(x=10;x<118;x=(x+1)%128)//若测高频,改为x=(x+8)号128,注意由于没进行信号发生器验证可能出现bug
{
float count=0.015295*aFinalValue+8;
OLED_DrawWave(x,count);
}
}
if(rxCmdBuf[2]==0x0B)
{
// 坐标
OLEDDisBinBMP(58,128,gImage_Current_B,Middle,Inverse_Color);
for(x=10;x<118;x=(x+1)%128)//若测高频,改为x=(x+8)号128,注意由于没进行信号发生器验证可能出现bug
{
float count=0.015295*bFinalValue+8;
OLED_DrawWave(x,count);
}
}
};break;
case 3: { // 显示速度返回值
oledClearMulPage(Page);
OLEDDisBinBMP(59,128,gImage_Velocity,Middle,Inverse_Color);
for(x=10;x<118;x=(x+1)%128)//若测高频,改为x=(x+8)号128,注意由于没进行信号发生器验证可能出现bug
{
float count = velocity + 8;
OLED_DrawWave(x,count);
}
};break;
case 4:{ // 显示解析出来的数据
oledClearMulPage(Page);
// if(clearOLEDFlag==0) { OLED_Clear();clearOLEDFlag=1;}
oled_print_chinese(0,0,16,Byte[0],2,Normal_Color);
oled_print_chinese(0,2,16,Sequence[0],2,Inverse_Color);
oled_print_chinese(0,4,16,Parameter[0],2,Normal_Color);
oled_print_chinese(0,6,16,Parameter[0],2,Inverse_Color);
oled_print_chinese(64,0,16,Parameter[0],2,Normal_Color);
oled_print_chinese(64,2,16,Parameter[0],2,Inverse_Color);
oled_print_chinese(64,4,16,Parameter[0],2,Normal_Color);
oled_print_chinese(64,6,16,Parameter[0],2,Inverse_Color);
// 测试使用
OLED_ShowNum(34,0,dateSumNumber,3,16);
OLED_ShowNum(34,2,cmdSequence,3,16);
OLED_ShowNum(34,4,rxCmdBuf[1],3,16);
OLED_ShowNum(34,6,rxCmdBuf[2],3,16);
OLED_ShowNum(98,0,rxCmdBuf[3],3,16);
OLED_ShowNum(98,2,rxCmdBuf[4],3,16);
OLED_ShowNum(98,4,rxCmdBuf[5],3,16);
OLED_ShowNum(98,6,rxCmdBuf[6],3,16); // 接收序列号
};break;
}
}
三、往期回顾
FOC系列(一)----DRV8301芯片的学习
FOC系列(二)----继续学习DRV8301芯片
FOC系列(三)----AS5600磁编码器
FOC系列(四)----重新绘制DRV8301驱动板