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

文章介绍了如何在STM32平台上移植simpleFoc库的encoder模块,包括使用HAL_GetTick实现millis、micros和delay函数,修改encoder外中断接口,添加回调函数以及新增digitalRead函数。在设置编码器中断和初始化硬件后,通过示例代码验证了程序的正确性。
摘要由CSDN通过智能技术生成

文章目录


前言

今天移植的主要内容是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定时器章节,里面有编码器接口功能,后续有必要的情况下会对该部分以定时器接口进行实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值