FOC系列(五)----STM32F405RGT6控制板焊接与初步编写代码

   声明:本人水平有限,博客可能存在部分错误的地方,请广大读者谅解并向本人反馈错误。
   首先祝大家新年快乐,因为我也快放假了,驱动板只能是开学之后再去测试了,本篇博客应该是本专栏年前的最后一篇了

一、 硬件部分

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 代码

  具体的代码我会在整体都做完之后进行开源的,下面我就说一下整体的代码功能:

  1. 使用ESP32的BLE功能,接收手机发送的指令
  2. ESP32会将接收的指令转化为通信协议发送给STM32
  3. 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驱动板

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计网·湖科大·思科】实验三 总线型以太网的特性、集线器和交换机的区别、交换机的自学习算法

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

字符串相关的函数和内存块相关函数

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

[学习笔记] ONNX 基础知识

1. ONNX 简介 1.1 什么是 ONNX 开放神经网络交换 ONNX&#xff08;Open Neural Network Exchange&#xff09;是一套表示深度神经网络模型的开放格式&#xff0c;由微软和 Facebook 于 2017 推出&#xff0c;然后迅速得到了各大厂商和框架的支持。通过短短几年的发展&#xf…

【JavaEE进阶】 #{}和${}

文章目录 &#x1f343;前言&#x1f333;#{}和${}使⽤&#x1f6a9;Interger类型的参数&#xff08;基础数据类型&#xff09;&#x1f388;使用#{}&#x1f388;使用${} &#x1f6a9;String类型的参数使用&#x1f388;#{}使用&#x1f388;${} &#x1f38d;#{}和${}区别&a…

林浩然与极限的“无穷”约会

林浩然与极限的“无穷”约会 Lin Haoran’s Encounter with the Mathematical “Infinity” 在数学王国里&#xff0c;有一位名叫林浩然的大侠&#xff0c;他的江湖就是高等数学的殿堂。而他要挑战的终极Boss&#xff0c;便是那个既神秘又顽皮的“极限”。 In the kingdom of …

《golang设计模式》第三部分·行为型模式-10-模板方法(Template Method)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 模板方法&#xff08;Template Method&#xff09;用来定义算法的框架&#xff0c;将算法中的可变步骤定义为抽象方法&#xff0c;指定子类实现或重写。 1.1 角色 AbstractClass&#xff08;…

字符串相关函数【超详细】(strcpy,strstr等string.h中的函数)

文章目录 strlen库中函数定义函数作用函数大概“工作”流程函数使用注意&#xff08;要求&#xff09;函数使用例举 strcpy库中函数定义函数作用函数使用注意&#xff08;要求&#xff09;函数大概“工作”流程函数使用例举 strcat库中函数定义函数作用函数使用注意&#xff08…

Go 的命令行解析 flag 包如何扩展新类型呢?

上篇文章 说到&#xff0c;除布尔类型 Flag&#xff0c;flag 支持的还有整型&#xff08;int、int64、uint、uint64&#xff09;、浮点型&#xff08;float64&#xff09;、字符串&#xff08;string&#xff09;和时长&#xff08;duration&#xff09;。 flag 内置支持能满足…

transformer和vit学习笔记

以下记录自己对transformer的学习笔记&#xff0c;可能自己看得懂【久了自己也忘了看不懂】&#xff0c;别人看起来有点乱。以后再优化文档~ 小伙伴请直接去看学习资源&#xff1a; Transformer的理解T-1_哔哩哔哩_bilibili 首先&#xff0c;时序处理&#xff1a;一些模型的出…

Two-factor authentication (2FA) is required for your GitHub account解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【基于电商履约场景的 DDD 实战】DDD业务建模第二部分:履约的战术设计(梳理整个战术设计流程图)

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

Java笔记 --- 二、Stream流

二、Stream流 结合Lambda表达式&#xff0c;简化集合、数组的操作 获取Stream流对象 单列集合获取Stream流 双列集合获取Stream流 数组获取Stream流 一堆零散的数据获取Stream流 Stream流的静态方法of的形参是一个可变参数&#xff0c;可以传递零散数据&#xff0c;也可以传递…

【Python】02快速上手爬虫案例二:搞定验证码

文章目录 前言1、不要相信什么验证码的库2、以古诗文网为例&#xff0c;获取验证码1&#xff09;code_result.py2&#xff09;gsw.py 前言 提示&#xff1a;以古诗文网为例&#xff0c;获取验证码&#xff1a; 登录&#xff1a;https://so.gushiwen.cn/user/login.aspx 1、不…

【C++】类与对象(一)

前言 类与对象&#xff08;一&#xff09; 文章目录 一、面向对象和面向过程的对比二、类的引入2.1 C中的结构体2.2 类2.3 类定义方法2.4 修饰限定符2.5 封装2.6 类的实例化2.7 类对象的大小 三、this指针3.1 this 指针的使用 一、面向对象和面向过程的对比 面向过程编程是将程…

【Docker】nacos集群搭建Nginx负载均衡

目录 一、mysql安装与基操 1.1 数据准备 1.2 创建mysql与数据表 二、Nacos集群部署 2.1 创建nacos及配置 2.2 创建Nginx容器 一、mysql安装与基操 1.1 数据准备 拉取mysql docker pull mysql:5.7(版本) 定义挂载目录 mkdir -p /mysql/{conf,data,script} 配置my.c…

【排序4】探秘归并排序:提高程序效率的必备技巧

&#x1f60a;归并排序 &#x1f38a;1、基本思想&#x1f38a;2、代码示例&#x1f38a;3、非递归实现&#x1f38a;4、归并排序的性能分析&#x1f38a;5、归并排序的优缺点&#x1f38a;6、归并排序的应用场景&#x1f38a;7、总结 &#x1f38a;1、基本思想 归并排序&…

(笔记总结)C/C++语言的常用库函数(持续记录,积累量变)

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

【vue3源码】vue源码探索之旅:项目介绍

简言 记录下我眼中的vue源码项目。 gitHubvue3项目仓库 项目要求: vue版本 3.4.15nodeV18.12.0以上使用pnpm包管理器vitest测试框架Vue3 vue3是渐进式JavaScript框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 Vue 是一个框架,也是一个生态。其功能覆盖了大部分…

Spark运行架构以及容错机制

Spark运行架构以及容错机制 1. Spark的角色区分1.1 Driver1.2 Excuter 2. Spark-Cluster模式的任务提交流程2.1 Spark On Yarn的任务提交流程2.1.1 yarn相关概念2.1.2 任务提交流程 2.2 Spark On K8S的任务提交流程2.2.1 k8s相关概念2.2.2 任务提交流程 3. Spark-Cluster模式的…

HBase入门:运行机制

文章目录 HBase 系统架构客户端ZooKeeper 服务器Master 主服务器Region 服务器 Region 服务器工作原理用户读写数据的过程缓存的刷新StoreFile合并 Store 的工作原理HLog 的工作原理 HBase 系统架构 HBase 的系统架构包括客户端、ZooKeeper 服务器、Master 主服务器、Region服…