手把手移植 simpleFOC (四):pwm 六相 篇

news2024/11/16 7:44:26

文章目录

  • 系列文章目录
  • 前言
  • 一、pandas是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.读入数据
  • 总结


前言

今天移植的内容,为定时器生在pwm,能按矢量数据控制电机到相应的位置


一、定时器的配置

通读了simpleFoc的代码,准备让定时器1生成的pwm波为20KHz,中心对齐模式3,三个通道均工作在模式1,极性为高电平,即TIMx_CNT < TIMx_CCRx时OCxREF信号为高,输出为高电平。

工作频率设定

预设:20KHz,由于是中心对齐模式,周期即是常规设定的2倍,那么ARR=f_timer/(2*f_pwm)

f_timer: 即是定时器工作主频,未分频,即为72MHz

f_pwm: 即是要设定的pwm的频,20KHz

那么ARR=72000000/(2*20000) = 1800

配置如下:

 三个通道均工作在模式1,极性为高电平

二、移植代码

1.添加代码

由于原理图设计为pwm三通道驱动,即将BLDCDriver3PWM.cpp,BLDCDriver3PWM.h添加到编译工程即可,并新增两个文件stm32_mcu.cpp,stm32_mcu.h,为BLDCDriver3PWM作底层接口

2.修改代码,嫁接定时器

 初始化部分:

  由于都是stm32cubeMX根据需要生成的驱动代码,原代码对管脚初始化部分即可删除或屏蔽。

// init hardware pins
int BLDCDriver3PWM::init() {
  // PWM pins
  // pinMode(pwmA, OUTPUT);
  // pinMode(pwmB, OUTPUT);
  // pinMode(pwmC, OUTPUT);
  // if( _isset(enableA_pin)) pinMode(enableA_pin, OUTPUT);
  // if( _isset(enableB_pin)) pinMode(enableB_pin, OUTPUT);
  // if( _isset(enableC_pin)) pinMode(enableC_pin, OUTPUT);


  // sanity check for the voltage limit configuration
  if(!_isset(voltage_limit) || voltage_limit > voltage_power_supply) voltage_limit =  voltage_power_supply;

  // Set the pwm frequency to the pins
  // hardware specific function - depending on driver and mcu
  params = _configure3PWM(pwm_frequency, pwmA, pwmB, pwmC);
  initialized = (params!=SIMPLEFOC_DRIVER_INIT_FAILED);
  return params!=SIMPLEFOC_DRIVER_INIT_FAILED;
}

L6234使能部分

由于原理图的该芯片的en管脚为mcu统一管控,会影响到BLDCDriver3PWM::setPhaseState该函数,并且该函数只对 Trapezoid_120 、Trapezoid_150两种模式调制调用,貌似方波输出(该部分不是重点,没有深入,暂时忽略),BTN8982版是分开控制,或L6234版后续再改一版。

// enable motor driver
void  BLDCDriver3PWM::enable(){
    // enable_pin the driver - if enable_pin pin available
    HAL_GPIO_WritePin(m0_en_GPIO_Port, m0_en_Pin, GPIO_PIN_SET);
    // set zero to PWM
    setPwm(0,0,0);
}

// disable motor driver
void BLDCDriver3PWM::disable()
{
  // set zero to PWM
  setPwm(0, 0, 0);
  // disable the driver - if enable_pin pin available
 HAL_GPIO_WritePin(m0_en_GPIO_Port, m0_en_Pin, GPIO_PIN_RESET);

}
// Set voltage to the pwm pin
void BLDCDriver3PWM::setPhaseState(PhaseState sa, PhaseState sb, PhaseState sc) {
  // disable if needed
  if( _isset(enableA_pin) &&  _isset(enableB_pin)  && _isset(enableC_pin) ){
    //该模式在 Trapezoid_120 Trapezoid_150里调用  由于三个引脚连在一起了,暂时屏蔽该功能
    // digitalWrite(enableA_pin, sa == PhaseState::PHASE_ON ? enable_active_high:!enable_active_high);
    // digitalWrite(enableB_pin, sb == PhaseState::PHASE_ON ? enable_active_high:!enable_active_high);
    // digitalWrite(enableC_pin, sc == PhaseState::PHASE_ON ? enable_active_high:!enable_active_high);
  }
}

驱动层接入

将第一章节定时器配置生成的代码MX_TIM1_Init赋予给

void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC),本例是将内容复制粘贴过来了 并全输出低电平,并且启动定时器

void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
  // if (numTimerPinsUsed+3 > SIMPLEFOC_STM32_MAX_PINTIMERSUSED) {
  //   SIMPLEFOC_DEBUG("STM32-DRV: ERR: too many pins used");
  //   return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
  // }
  if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
  else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
  // center-aligned frequency is uses two periods
  pwm_frequency *=2;

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
  htim1.Init.Period = SystemCoreClock/pwm_frequency;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = TIM_1_RCR;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSR_ENABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 10;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,0);
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,0);
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,0);
					
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);

  /* USER CODE END TIM1_Init 2 */
  HAL_TIM_MspPostInit(&htim1);
  return 0;
}

 修改void _writeDutyCycle3PWM(float dc_a,  float dc_b, float dc_c, void* params)函数。

该函数的主要功能是根据相电压对于mos管的工作电压dc_bus的比率,设定pwm通道的TIMx_CCRx值,即完成pwm输出高电平的占空比。

// function setting the pwm duty cycle to the hardware
// - BLDC motor - 3PWM setting
//- hardware speciffic
void _writeDutyCycle3PWM(float dc_a,  float dc_b, float dc_c, void* params){
  // transform duty cycle from [0,1] to [0,4095]
  //float pwm_range = SystemCoreClock/pwm_frequency;
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,_PWM_RANGE*dc_a);
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,_PWM_RANGE*dc_b);
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,_PWM_RANGE*dc_c);
 
}

 三、调试

1、设定三相电压值

电机转到相应的位置,即说明驱动正常了。

extern "C" {


// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);



void setup() {


  driver.voltage_power_supply = 24;
  // limit the maximal dc voltage the driver can set
  // as a protection measure for the low-resistance motors
  // this value is fixed on startup
  driver.voltage_limit = 0.8;
  driver.init();
  driver.enable();


  Serial.begin(115200);
  Serial.println("Motor ready!");
  Serial.println("Set target position [rad]");
  _delay(1000);
}

void loop() {
	
    driver.setPwm(3,6,5);
    
}

 driver.voltage_limit = 0.8   由于本人使用的是5008高功率电机,相电阻很小,这里的限制一定要小,不然L6234发烫,电机工作也不正常。该值可以从小到大慢慢调,如果电机转不动或抖动,慢慢往上加。

 2、六相驱动调试

改动loop代码,让六相循环驱动起来,电机就会转起来了。

void loop() {

    static int sixPhase_seq = 0;
 
    switch(sixPhase_seq%6)
    {
      case 0:{driver.setPwm(1,0,0);}break;  //100
      case 1:{driver.setPwm(1,1,0);}break;  //110
      case 2:{driver.setPwm(0,1,0);}break;  //010
      case 3:{driver.setPwm(0,1,1);}break;  //011
      case 4:{driver.setPwm(0,0,1);}break;  //001
      case 5:{driver.setPwm(1,0,1);}break;  //101
    }
   
    sixPhase_seq++;
  
    _delay(20);
    
}

这代码里的1和0,代表输出三相的电压值,也体现了六桥通断的逻辑,如果是云台电机可将driver.voltage_limit设高一点,并将这里的1,设定高一些数字。

simpleFoc 六相(移植成功)

总结

本章从定时器的设置到电机能按六相位转动起来,基本为后续的开环,闭环控制作好了硬件基础。

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

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

相关文章

【玩转python系列】【小白必看】使用Python爬虫技术获取代理IP并保存到文件中

文章目录 前言导入依赖库打开文件准备写入数据循环爬取多个页面完整代码运行效果结束语 前言 这篇文章介绍了如何使用 Python 爬虫技术获取代理IP并保存到文件中。通过使用第三方库 requests 发送HTTP请求&#xff0c;并使用 lxml 库解析HTML&#xff0c;我们可以从多个网页上…

要单片机和RTOS有必要学习嵌入式linux吗?

学习嵌入式 Linux 是否有必要&#xff0c;取决于你的项目需求和职业发展目标。以下是一些考虑因素&#xff1a; 项目需求&#xff1a;如果你的项目需要处理复杂的网络、文件系统、多任务管理等功能&#xff0c;嵌入式 Linux 可能是更适合的选择。Linux 提供了丰富的开源软件包和…

生成对抗网络DCGAN实践笔记

在AI内容生成领域&#xff0c;有三种常见的AI模型技术&#xff1a;GAN、VAE、Diffusion。其中&#xff0c;Diffusion是较新的技术&#xff0c;相关资料较为稀缺。VAE通常更多用于压缩任务&#xff0c;而GAN由于其问世较早&#xff0c;相关的开源项目和科普文章也更加全面&#…

华为OD机试真题2022Q4 A + 2023 B卷(Java)

大家好&#xff0c;我是哪吒。 五月份之前&#xff0c;如果你参加华为OD机试&#xff0c;收到的应该是2022Q4或2023Q1&#xff0c;这两个都是A卷题。 5月10日之后&#xff0c;很多小伙伴收到的是B卷&#xff0c;那么恭喜你看到本文了&#xff0c;抓紧刷题吧。B卷新题库正在更…

微服务 - Consul集群化 · 服务注册 · 健康检测 · 服务发现 · 负载均衡

一、Consul 概括 Consul 是由N多个节点(台机/虚机/容器)组成&#xff0c;每个节点中都有 Agent 运行着&#xff0c;各节点间用RPC通信&#xff0c;所有节点内相同的 Datacenter 名称为一个数据中心&#xff0c;节点又分三种角色 Client/Server/Leader&#xff1a; Agent&…

Python算法笔记(3)-树、二叉树、二叉堆、二叉搜索树

树和二叉树 什么是树 树是一种非线性的数据结构&#xff0c;由n个节点构成的有限集合&#xff0c;节点数0的树叫空树&#xff0c;在任意一棵树中&#xff0c;有且仅有一个特点的称为根节点&#xff0c;当N>1时&#xff0c;其余节点可分m为互不相交的有限集。 例如如下&…

子序列,回文串相关题目

class Solution { public:int dp[2510];int lengthOfLIS(vector<int>& nums) {//dp[i]表示以nums[i]为结尾的最长子序列的长度int nnums.size();for(int i0;i<n;i){dp[i]1;}for(int i1;i<n;i){for(int j0;j<i;j){if(nums[i]>nums[j]){dp[i]max(dp[i],dp[…

因子分解机介绍和PyTorch代码实现

因子分解机&#xff08;Factorization Machines&#xff0c;简称FM&#xff09;是一种用于解决推荐系统、回归和分类等机器学习任务的模型。它由Steffen Rendle于2010年提出&#xff0c;是一种基于线性模型的扩展方法&#xff0c;能够有效地处理高维稀疏数据&#xff0c;并且在…

用Blender做一个足球烯C60

文章目录 作图思路先做一个足球球棍模型平滑 Blender初学者入门&#xff1a;做一个魔方 作图思路 C 60 C_{60} C60​是由60个碳原子构成&#xff0c;形似足球&#xff0c;又名足球烯。而足球的顶点&#xff0c;可以通过正二十面体削去顶点得到&#xff0c;原理可参照这篇&…

基于数据驱动的多尺度表示的信号去噪统计方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

React组件进阶之children属性,props校验与默认值以及静态属性static

React组件进阶之children属性,props校验与默认值以及静态属性static 一、children属性二、props校验2.1 props说明2.2 prop-types的安装2.3 props校验规则2.4 props默认值 三、静态属性static 一、children属性 children 属性&#xff1a;表示该组件的子节点&#xff0c;只要组…

网站创建004:跟用户交互的标签

input 系列&#xff1a; <body><input type"text" /> <!--文本输入框--><input type"password" /> <!--密码输入框--><input type"checkbox" /> <!--复选框--><input type"checkbox"…

【MySQL】使用C语言连接

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《零基础入门MySQL》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录 &#x1f449;my…

用CSS和HTML写一个水果库存静态页面

HTML代码&#xff1a; <!DOCTYPE html> <html> <head><link rel"stylesheet" type"text/css" href"styles.css"> </head> <body><header><h1>水果库存</h1></header><table>…

函数指针及其使用

类比 数组的地址 函数的地址 数组指针 函数的指针 函数指针的运用 有趣的代码1

从0到1构建基于自身业务的前端工具库

前言 在实际项目开发中无论 M 端、PC 端&#xff0c;或多或少都有一个 utils 文件目录去管理项目中用到的一些常用的工具方法&#xff0c;比如&#xff1a;时间处理、价格处理、解析url参数、加载脚本等&#xff0c;其中很多是重复、基础、或基于某种业务场景的工具&#xff0…

链表(一) 单链表操作详解

文章目录 一、什么是链表二、链表的分类1、单向或者双向2、带头或不带头3、循环或不循环 三、无头单向不循环链表的实现SList.hSList.c动态申请一个节点单链表打印单链表尾插单链表头插单链表的尾删单链表头删单链表查找在pos位置前插入单链表在pos位置之后插入x删除pos位置单链…

自动驾驶下半场的“入场券”

交流群 | 进“传感器群/滑板底盘群/汽车基础软件群/域控制器群”请扫描文末二维码&#xff0c;添加九章小助手&#xff0c;务必备注交流群名称 真实姓名 公司 职位&#xff08;不备注无法通过好友验证&#xff09; 作者 | 张萌宇 自动驾驶战争的上半场拼的是硬件和算法&…

DTC介绍

DTC 一般由3个字节组成&#xff1a; 字节1&#xff1a;High Byte bit 7-6: 对应DTC属于哪一个系统&#xff0c;P: 00动力系统、C: 01底盘、B: 10车身和U: 11通信系统bit 5-4: 用来区分DTC是标准组织所定义还是制造商自定义 00: ISO/SAE01: 制造商10: ISO/SAE11: ISO/SAE bit 3…

【Rust教程 | 基础系列2 | Cargo工具】Cargo介绍及使用

文章目录 前言一&#xff0c;Cargo介绍1&#xff0c;Cargo安装2&#xff0c;创建Rust项目2&#xff0c;编译项目&#xff1a;3&#xff0c;运行项目&#xff1a;4&#xff0c;测试项目&#xff1a;5&#xff0c;更新项目的依赖&#xff1a;6&#xff0c;生成项目的文档&#xf…