手把手移植 simpleFOC (三):编码器篇

news2024/11/15 15:59:19

文章目录

前言

一、延时函数

二、修改encoder外中断接口

1.中断调用接口

2.嫁接回调函数

3、新增digitalRead函数

三、添加编译项

四、编译,调试

 总结


前言

今天移植的主要内容是simpleFoc的encoder,目标是转到电机,读出对应的角度及角度率。

一、延时函数

阅读encoder.cpp后,发现里面需要几个延时函数。由于stm32cubeMX生成工程后,sysTick默认为产生1ms中断,并且HAL_GetTick()函数记录了从开机起到当前的ms数。利用该接口编写millis()、micros()、delay()供 simpleFoc使用

掏出arduino里wiring.c文件,进行修改。

#include "Arduino.h"
#include "stm32f1xx_hal.h"

static void __empty() {
	// Empty
}
void yield(void) __attribute__ ((weak, alias("__empty")));

#ifdef __cplusplus
extern "C" {
#endif

uint32_t millis(void)
{
// todo: ensure no interrupts
    return HAL_GetTick();
}

// Interrupt-compatible version of micros
// Theory: repeatedly take readings of SysTick counter, millis counter and SysTick interrupt pending flag.
// When it appears that millis counter and pending is stable and SysTick hasn't rolled over, use these 
// values to calculate micros. If there is a pending SysTick, add one to the millis counter in the calculation.
uint32_t micros(void)
{
    uint32_t ticks, ticks2;
    uint32_t pend, pend2;
    uint32_t count, count2;

    ticks2  = SysTick->VAL;
    pend2   = !!((SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)||((SCB->SHCSR & SCB_SHCSR_SYSTICKACT_Msk)))  ;
    count2  = HAL_GetTick();

    do {
        ticks=ticks2;
        pend=pend2;
        count=count2;
        ticks2  = SysTick->VAL;
        pend2   = !!((SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)||((SCB->SHCSR & SCB_SHCSR_SYSTICKACT_Msk)))  ;
        count2  = HAL_GetTick();
    } while ((pend != pend2) || (count != count2) || (ticks < ticks2));

    return ((count+pend) * 1000) + (((SysTick->LOAD  - ticks)*(1048576/(SystemCoreClock/1000000)))>>20) ; 
    // this is an optimization to turn a runtime division into two compile-time divisions and 
    // a runtime multiplication and shift, saving a few cycles
}

// original function:
// uint32_t micros( void )
// {
//     uint32_t ticks ;
//     uint32_t count ;
// 
//     SysTick->CTRL;
//     do {
//         ticks = SysTick->VAL;
//         count = GetTickCount();
//     } while (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk);
// 
//     return count * 1000 + (SysTick->LOAD + 1 - ticks) / (SystemCoreClock/1000000) ;
// }


void delay(uint32_t dwMs)
{
    if (dwMs == 0)
        return;
    uint32_t start = HAL_GetTick();
    do {
        yield();
    } while (HAL_GetTick() - start < dwMs);
}

#ifdef __cplusplus
}
#endif

二、修改encoder外中断接口

1.中断调用接口

一般编码器 doA doB会赋予的,所以在中断里尽量节约执行时间,并没有判断回调函数是否为空,而doIndex项,看例程里有时并没有赋予,所以在执行时判断了 if(functionZ) 再执行,否则stm32会异常,该处踩坑了。

typedef void (*pf_callbakck)(void);

static pf_callbakck functionA=nullptr;
static pf_callbakck functionB=nullptr;
static pf_callbakck functionZ=nullptr;


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  switch(GPIO_Pin)
  {
    case M0_ENC_A_Pin:{
      functionA();
    }break;
    case M0_ENC_B_Pin:{
      functionB();
    }break;
    case M0_ENC_Z_Pin:{
      if(functionZ)
        functionZ();
    }break;
    default:break;
  }
  
}

2.嫁接回调函数

代码如下:

void Encoder::enableInterrupts(void (*doA)(), void(*doB)(), void(*doIndex)())
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(doA != nullptr) functionA=doA;
  if(doB != nullptr) functionB=doB;

  GPIO_InitStruct.Pin = M0_ENC_A_Pin|M0_ENC_B_Pin;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  switch(quadrature){
    case Quadrature::ON:      
      /*Configure GPIO pins : PBPin PBPin */
      
      GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
      
      break;
    case Quadrature::OFF:
      // A callback and B callback

      GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;

      break;
  }
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = M0_ENC_Z_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(M0_ENC_Z_GPIO_Port, &GPIO_InitStruct);

  // if index used initialize the index interrupt
  if(hasIndex() && doIndex != nullptr) 
    functionZ=doIndex;

  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}

3、新增digitalRead函数

__inline__  bool digitalRead(int pin)
{
  bool tmp=0;
  switch(pin)
  {
    case M0_ENC_A_Pin:tmp=HAL_GPIO_ReadPin(M0_ENC_A_GPIO_Port, M0_ENC_A_Pin);break;
    case M0_ENC_B_Pin:tmp=HAL_GPIO_ReadPin(M0_ENC_B_GPIO_Port, M0_ENC_B_Pin);break;
    case M0_ENC_Z_Pin:tmp=HAL_GPIO_ReadPin(M0_ENC_Z_GPIO_Port, M0_ENC_Z_Pin);break;
    default:break;
  }  
  return tmp;
}

三、添加编译项

如图所示:

四、编译,调试

测试代码如下:

#define __MAIN_CPP__

#include "simpleFoc_main.h"
#include "Print.h"
#include "hwSerial.h"
#include <SimpleFOC.h>
#include "main.h"
extern HardwareSerial  Serial2;
#define Serial Serial2


extern "C" {


Encoder encoder = Encoder(M0_ENC_A_Pin, M0_ENC_B_Pin, 1000);
// interrupt routine intialisation
void doA(){encoder.handleA();}
void doB(){encoder.handleB();}

void setup() {  

  // enable/disable quadrature mode
  encoder.quadrature = Quadrature::ON;

  // check if you need internal pullups
  encoder.pullup = Pullup::USE_EXTERN;
  
  // initialise encoder hardware
  encoder.init();
  // hardware interrupt enable
  encoder.enableInterrupts(doA, doB);

  Serial.println("Encoder ready");
  _delay(1000);
}

void loop() {
  // iterative function updating the sensor internal variables
  // it is usually called in motor.loopFOC()
  // not doing much for the encoder though
  // encoder.update();
  // display the angle and the angular velocity to the terminal
  Serial.print(encoder.getAngle());
  Serial.print("\t");
  Serial.println(encoder.getVelocity());
  _delay(100);
}



}

编译下载,转动电机,运行结果如下:


 总结

 运行结果,编码器程序移植工作正常。只是simpleFoc采用的是外部中断方式进行采集,看了stm32定时器章节,里面有编码器接口功能,后续有必要的情况下会对该部分以定时器接口进行实现。

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

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

相关文章

Labview串口通信VISA实现串口收发

文章目录 前言一、什么是 VISA二、VISA 驱动下载及安装1、下载2、安装 三、VISA 实现串口收发1、打开虚拟串口2、前面板运行效果3、程序框图 前言 前面使用过调用 MSComm 控件的方式&#xff08;Labview串口通信MSComm实现串口收发&#xff09;&#xff0c;即利用 Windows 提供…

尚医通07:MongoDB+医院需求介绍

内容介绍 1、客户端工具 2、MongoDB常用操作 3、springboot集成MongoDB&#xff08;mongoTemplate&#xff09; 4、springboot集成MongoDB&#xff08;MongoRepository&#xff09; 5、医院需求介绍 6、部署医院模拟系统 7、开发平台接口-上传医院接口 客户端工具 1、…

【用IDEA基于Scala2.12.18开发Spark 3.4.1 项目】

目录 使用IDEA创建Spark项目设置sbt依赖创建Spark 项目结构新建Scala代码 使用IDEA创建Spark项目 打开IDEA后选址新建项目 选址sbt选项 配置JDK debug 解决方案 相关的依赖下载出问题多的话&#xff0c;可以关闭idea&#xff0c;重启再等等即可。 设置sbt依赖 将sbt…

Linux6.2 ansible 自动化运维工具(机器管理工具)

文章目录 计算机系统5G云计算第一章 LINUX ansible 自动化运维工具&#xff08;机器管理工具&#xff09;一、概述二、ansible 环境安装部署三、ansible 命令行模块1.command 模块2.shell 模块3.cron 模块4.user 模块5.group 模块6.copy 模块7.file 模块8.hostname 模块9.ping …

【Python入门系列】第十九篇:Python基于协同过滤推荐系统的实现

文章目录 前言一、协同过滤算法简介二、计算相似度三、Python实现简单的协同过滤推荐系统总结 前言 推荐系统是现代互联网平台中的重要组成部分&#xff0c;它可以根据用户的兴趣和行为&#xff0c;向其推荐个性化的内容。协同过滤是推荐系统中常用的一种方法&#xff0c;它基…

POI信息点的diPointX、diPointY转化成经纬度

需求&#xff1a;接口返回某个地点的数据&#xff08;diPointX、diPointY&#xff09;&#xff0c;前端需把该地点转化成经纬度形式在地图上进行Marker标记。 实现&#xff1a;&#xff08;查找百度地图开发文档&#xff09; 代码验证&#xff1a; console.log(new BMap.Merca…

性能测试问题之慢sql分析

我们在做性能测试的时候&#xff0c;慢sql也可以说是很常见问题&#xff0c;我的性能测试生涯几乎经常遇到慢sql&#xff0c;那么我们怎么来判断有没有慢sql呢&#xff0c;有慢sql后怎么来分析优化呢?如图&#xff1a; 通过上图看可以看到当存在慢sql的时候&#xff0c;这里会…

火爆全网,接口自动化测试-DDT数据驱动实战总结,一篇贯通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 DDT&#xff08;D…

信息安全:网络安全体系 与 网络安全模型.

信息安全&#xff1a;网络安全体系 与 网络安全模型. 网络安全保障是一项复杂的系统工程&#xff0c;是安全策略、多种技术、管理方法和人员安全素质的综合。一般而言&#xff0c;网络安全体系是网络安全保障系统的最高层概念抽象&#xff0c;是由各种网络安全单元按照一定的规…

python更换iterm2背景图片

背景 在看知乎的时候&#xff0c;突然看到了这样的一个视频教程&#xff0c;用python代码更换iterm2的背景。于是我细细的研究一下。视频地址 视频中提到的参考文章地址&#xff1a; iterm2官网官方仓库 实现过程 我直接把作者的代码粘贴如下&#xff0c;首先需要安装iter…

pycharm 使用远程服务器 jupyter (本地jupyter同理)

1. 远程服务器miniconda 环境中创建jupyter环境 # 1. 激活环境 conda activate envname#2. 在环境中安装jupyter pip install jupyter # 或者 conda install jupyter#3. 生成jupyter_notebook_config.py文件 jupyter notebook --generate-config#4. 设置密码 jupyter noteboo…

docker—springboot服务通信

文章目录 docker—springboot服务通信一、方式1、host 二、坑点末、参考资料 docker—springboot服务通信 一、方式 1、host 步骤&#xff1a; host文件增加域名解析&#xff1a; 127.0.0.1 rabbitmqapplication.yml&#xff1a; application.yml中&#xff0c;连接方式使用…

【HarmonyOS】API6使用storage实现轻量级数据存储

写在前面 本篇内容基于API6 JS语言进行开发&#xff0c;通过结合轻量级数据存储开发指导的文档&#xff0c;帮助大家完成一个实际的代码案例&#xff0c;通过这个小案例&#xff0c;可以实现简单数据的存储。 参考文档&#xff1a;文档中心 1、页面布局 首先我们编写一个简单…

C++容器——list的模拟实现

目录 一.list的基本结构 二. 接下来就是对list类构造函数的设计了&#xff1a; 三.链表数据的增加&#xff1a; 四.接下来就是迭代器的创建了&#xff1a; 四.简单函数的实现&#xff1a; 五.构造与析构 六.拷贝构造和赋值重载 传统写法: 现代写法&#xff1a; 七.迭…

Docker—— consul的容器服务更新与发现

Docker—— consul的容器服务更新与发现 一、Consul概述1.什么是服务注册与发现2.什么是consul 二、consul 部署1.consul服务器①. 建立 Consul 服务②. 查看集群信息③. 通过 http api 获取集群信息 2.registrator服务器①. 安装 Gliderlabs/Registrator②. 测试服务发现功能是…

别再被割韭菜了,小白几块钱就能打造专属AI知识库

随着AIGC各种项目的越发成熟&#xff0c;打造自己的知识库&#xff0c;对于企业和个人来说就变的门槛越来越低&#xff0c;自己的知识库&#xff0c;有许多的好处&#xff0c;上传自己的知识文档&#xff0c;能让对话变的更加垂直专业。 但是博主看到网站很多商家动辄几千的收…

使用docker-compose搭建lnmpr环境

源码gitee compose 使用的三个步骤&#xff1a; • 使用 Dockerfile 定义应用程序的环境。 • 使用 docker-compose.yml 定义构成应用程序的服务&#xff0c;这样它们可以在隔离环境中一起运行。 • 最后&#xff0c;执行 docker-compose up -d 命令来启动并运行整个应用程序…

Carla教程一:动力学模型到LQR

Carla教程一、动力学模型到LQR 从运动学模型和动力学模型到LQR 模型就是可以描述车辆运动规律的模型。车辆建模都是基于自行车模型的设定,也就是将四个轮子抽象为自行车一样的两个轮子来建模。 1、运动学模型 运动学模型是基于几何关系分析出来的,一般适用于低俗情况下,…

【西安交通大学】:融合传统与创新的学府之旅

【西安交通大学】&#xff1a;融合传统与创新的学府之旅 引言历史与发展学校特色学科优势院系专业校园环境与设施学生生活与社团活动校友荣誉与成就未来发展展望总结&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&…

【Linux】Http协议的学习

文章目录 前言一、了解HTTP协议是如何规定的总结 前言 HTTP协议&#xff08;超文本传输协议&#xff09;和我们上一篇写的网络版计算器中自己定制的协议一样&#xff0c;只不过Http协议是是一个非常好用的协议&#xff0c;所以我们可以直接用现成的不用自己再搞一套了。 一、了…