0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

无刷电机小车开发记录03——PWM信号输入捕获驱动

RTThread物联网操作系统 来源:未知 2023-10-20 02:00 次阅读

前言


  好吧,转眼一看,距离上一篇文章已经过去了两个月了。只能说工作和家庭的各种事情确实太多了,人到中年属于自己的时间确实越来越少了,再不过来调一下这个小车估计都要生锈了。所以今天过来接着做下一步工作。之前是完成了BSP的移植和导入,接下来就要尝试移植FOC算法了,开源的FOC算法也比较多,我这里打算利用SimpleFOC进行移植。本身的SimpleFOC是基于C++的,这里要移植成C代码。另外,SimpleFOC的SDK其中已经适配了很多种类的传感器驱动器以及无刷电机。如果硬件使用的是它已经适配的方案,则只需要简单配置一下就可以驱动了。而我这里是要在RTThread下移植FOC,更倾向于使用RTThread的框架,所以各种传感器和驱动器的适配计划加到RTThread的驱动这边来做。FOC那边只移植SimpleFOC的核心算法即可。所以在正式移植FOC算法之前,还需要先搭建用到的底层驱动。今天就先整理一下读取磁编码器PWM信号的输入捕获驱动的移植记录。其实某些适配更好的BSP内的RTThread驱动库里面已经有了输入捕获驱动,但只是捕获了输入脉宽的时间,而我这里需要的是捕获PWM信号的占空比,也就对应了磁编码器探测到的电机位置。但大体功能类似,所以随便找一个类似的底层驱动进行一下修改和移植即可。

磁编码器简介


  我这里用的是赛卓电子的国产磁编码器芯片SC60228,详情请看其数据手册,主要特性如下:

wKgaomUxb86Ad0frAADRKt9ULqs978.jpg

移植RTT驱动


  这个比较简单,因为RTT驱动库内已经有了“rt_inputcapture.c”的驱动文件,在SDK的“rt-thread/components/drivers/misc”目录下。只不过大多数的BSP没有做对应的适配而已。那先不管BSP那边的适配问题,先把这个C文件和对应的头文件拷贝一份,比如我重命名为“PWM_input_capture.c”和“PWM_input_capture.h”。

wKgaomUxb86AJ7ovAABMOSbJPxk665.png

然后代码内容改动不大,主要改的是返回的数据除了脉宽时间还有一个周期时间,这样就可以计算输入PWM信号的占空比了。另外,原有的驱动上使用的是ringbuffer做了一个数据缓存,这样数据处理可以异步话,什么时候需要什么时候把缓存内的数据全部读走即可。但各人考虑,我应用的场合是用这个信号来驱动无刷电机,这个PWM信号的输入频率也才1Khz,市面上大多数的无刷电机驱动,底层控制频率基本都达到了10Khz以上。所以我这里肯定不需要异步处理的,会直接用这个信号触发底层控制。而且控制效果还需要测试,如果转速上不去或者抖动厉害的话,可能还需要想办法插值细化或者改用SPI读取编码器数据(这也是为什么硬件上做了两种接口的原因,就是想去测试探索一些好玩的东西)。所以我这里是直接去掉了ringbuffer,加入了信号量。到时候上层开一个线程去等待这个信号量去跑FOC算法。头文件修改如下:


		

1structpwm_inputcapture_data 2{ 3rt_uint32_tpulsewidth_us;//脉宽 4rt_uint32_tpulsecycle_us;//周期 5}; 6structpwm_inputcapture_device 7{ 8structrt_deviceparent; 9conststructpwm_inputcapture_ops*ops; 10rt_sem_t*sem; 11structpwm_inputcapture_datapulse_param; 12}; 13/** 14*captureoperators 15*/ 16structpwm_inputcapture_ops 17{ 18rt_err_t(*init)(structpwm_inputcapture_device*inputcapture); 19rt_err_t(*open)(structpwm_inputcapture_device*inputcapture); 20rt_err_t(*close)(structpwm_inputcapture_device*inputcapture); 21}; 22voidpwm_hw_inputcapture_isr(structpwm_inputcapture_device*inputcapture); 23rt_err_trt_device_pwm_inputcapture_register(structpwm_inputcapture_device*inputcapture, 24constchar*name, 25void*data);

C文件主要修改的是回调函数,把之前的数据加入ringbuffer的操作改成了释放信号量,其它地方的修改都是一些简单的适配,由于C代码较多,我这里就不都贴出来了,相信大家肯定会自己完成适配,甚至比我的还要适配的好。而我的代码,等我第一期的功能开发完了,会整体开源出来。C代码主要修改的回调函数如下:


		

1voidrt_hw_pwm_inputcapture_isr(structpwm_inputcapture_device*inputcapture) 2{ 3rt_sem_release(inputcapture->sem); 4if(inputcapture->parent.rx_indicate!=RT_NULL) 5inputcapture->parent.rx_indicate(&inputcapture->parent,1); 6}

适配BSP驱动


  BSP驱动的适配稍微麻烦一点,如果大家能找到其它类似BSP内的相似驱动可以进行移植,那我这里简单找了一下并没有找到,所以仿照RTT的驱动适配方式,自己适配了一下。主要实现要点就是开启每个Timer的CH0和CH1双通道对CI0或者CI1输入的PWM信号进行采样,一个捕获脉宽,一个捕获周期,从而得到占空比。剩下的就是一些向下调用GD32的驱动库API,向上适配RTT的驱动接口。同样,下面只给出主要的初始化代码和中断处理代码,其它的可自行实现或者关注我后续开源的代码。


		

1rt_err_tpwm_inputcap_init(structpwm_inputcapture_device*pwm_incap) 2{ 3uint32_tsys_clk_freq; 4uint32_ttimer_prescaler=1; 5uint32_ttrigger_ch; 6timer_parameter_structTimerConfig; 7timer_ic_parameter_structTimerICConfig; 8structgd32_pwm_inputcapture_device*pwm_incap_device; 9pwm_incap_device=(structgd32_pwm_inputcapture_device*)pwm_incap; 10rcu_periph_clock_enable(pwm_incap_device->timer_rcu); 11rcu_periph_clock_enable(pwm_incap_device->GPIO_rcu); 12gpio_init(pwm_incap_device->GPIOx,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,pwm_incap_device->PINx); 13sys_clk_freq=rcu_clock_freq_get(CK_SYS); 14LOG_I("systemclockfrequency:%d",sys_clk_freq); 15TimerConfig.alignedmode=TIMER_COUNTER_EDGE; 16TimerConfig.clockdivision=TIMER_CKDIV_DIV1; 17TimerConfig.counterdirection=TIMER_COUNTER_UP; 18TimerConfig.period=65535U; 19do{ 20if(sys_clk_freq/timer_prescaler/TimerConfig.period< pwm_incap_device->input_freq_min) 21break; 22if(timer_prescaler==65536) 23{ 24rt_kprintf("cannotconfiguretheprescalerforinputsignalfrequency:%dhz ",pwm_incap_device->input_freq_min); 25returnRT_ERROR; 26} 27timer_prescaler++; 28}while(1); 29TimerConfig.prescaler=timer_prescaler-1; 30TimerConfig.repetitioncounter=0; 31timer_init(pwm_incap_device->timerx,&TimerConfig); 32LOG_I("%stimerprescaler:%d",pwm_incap_device->name,timer_prescaler); 33TimerICConfig.icfilter=10; 34TimerICConfig.icpolarity=TIMER_IC_POLARITY_RISING; 35TimerICConfig.icprescaler=TIMER_IC_PSC_DIV1; 36TimerICConfig.icselection=TIMER_IC_SELECTION_DIRECTTI; 37timer_input_pwm_capture_config(pwm_incap_device->timerx,pwm_incap_device->input_ch,&TimerICConfig); 38timer_interrupt_flag_clear(pwm_incap_device->timerx,TIMER_INT_FLAG_UP); 39timer_interrupt_enable(pwm_incap_device->timerx,TIMER_INT_UP); 40trigger_ch=((pwm_incap_device->input_ch==TIMER_CH_0)?TIMER_SMCFG_TRGSEL_CI0FE0:TIMER_SMCFG_TRGSEL_CI1FE1); 41timer_input_trigger_source_select(pwm_incap_device->timerx,trigger_ch); 42timer_slave_mode_select(pwm_incap_device->timerx,TIMER_SLAVE_MODE_RESTART); 43timer_external_trigger_config(pwm_incap_device->timerx,TIMER_EXT_TRI_PSC_OFF,TIMER_ETP_RISING,10); 44NVIC_SetPriority(pwm_incap_device->timerx_irqn,3); 45NVIC_EnableIRQ(pwm_incap_device->timerx_irqn); 46timer_enable(pwm_incap_device->timerx); 47returnRT_EOK; 48} 49voidpwm_inputcapture_update_isr(structgd32_pwm_inputcapture_device*device) 50{ 51uint32_twidth_ch; 52/*TIMUpdateevent*/ 53if(timer_interrupt_flag_get(device->timerx,TIMER_INT_FLAG_UP)!=RESET) 54{ 55timer_interrupt_flag_clear(device->timerx,TIMER_INT_FLAG_UP); 56device->pwm_inputcap.pulse_param.pulsecycle_us=timer_channel_capture_value_register_read(device->timerx,device->input_ch); 57width_ch=((device->input_ch==TIMER_CH_0)?(TIMER_CH_1):(TIMER_CH_0)); 58device->pwm_inputcap.pulse_param.pulsewidth_us=timer_channel_capture_value_register_read(device->timerx,width_ch); 59rt_hw_pwm_inputcapture_isr(device); 60} 61}

修改工程构建文件


修改相关SConscript文件

  在“libraries/gd32_drivers/SConscript”文件内的适当位置加入如下代码:


		

1ifGetDepend(['RT_USING_PWM_INPUT_CAPTURE']): 2src+=['drv_pwm_inputcapture.c']

  在“rt-thread/components/drivers/misc/SConscript”文件内的适当位置加入如下代码:


		

1ifGetDepend(['RT_USING_INPUT_CAPTURE']): 2src=src+['rt_inputcapture.c']

  意思很简单,就是当rtconfig.h内定义了”RT_USING_PWM_INPUT_CAPTURE”宏,则把“drv_pwm_inputcapture.c”和“rt_inputcapture.c”驱动文件加入工程,更准确的是加入编译。

修改相关Kconfig文件

  在“rt-thread/components/drivers/Kconfig”文件内的适当位置加入如下代码:


		

1configRT_USING_INPUT_CAPTURE 2bool"UsingINPUTCAPTUREdevicedrivers" 3defaultn

  管理BSP驱动代码的Kconfig文件不再librares目录下,而是在board目录下。于是在“board/Kconfig”文件内的适当位置,仿照其它驱动加入如下代码:


		

1menuconfigBSP_USING_PWM_INPUTCAPTURE 2bool"Enablepwminputcapture" 3defaultn 4selectRT_USING_PWM_INPUT_CAPTURE 5ifBSP_USING_PWM_INPUTCAPTURE 6configBSP_USING_PWM_INPUTCAPTURE1 7bool"Enablepwminputcapture1" 8defaultn 9configBSP_USING_PWM_INPUTCAPTURE2 10bool"Enablepwminputcapture2" 11defaultn 12configBSP_USING_PWM_INPUTCAPTURE3 13bool"Enablepwminputcapture3" 14defaultn 15configBSP_USING_PWM_INPUTCAPTURE4 16bool"Enablepwminputcapture4" 17defaultn 18configBSP_USING_PWM_INPUTCAPTURE5 19bool"Enablepwminputcapture5" 20defaultn 21configBSP_USING_PWM_INPUTCAPTURE6 22bool"Enablepwminputcapture6" 23defaultn 24endif

  意思也比较简单,我这里适配了6个PWM的输入捕获驱动,并且利用“select”语句,在BSP的驱动管理里面自动开启了RTT驱动里面的“RT_USING_PWM_INPUT_CAPTURE”选项。修改完上述代码后,就可以用menuconfig命令或者RTThreadIDE的RT-Thread Settings图形配置界面内进行配置了:

wKgaomUxb86AcVJtAAC__4hZpLo688.png

测试


  驱动有了,再在顶层逻辑内创建并实现两个测试线程:


		

1intmain(void) 2{ 3... 4rt_thread_tMotorL_encoder_thread; 5MotorL_encoder_thread=rt_thread_create("MotorLEncoder",MotorLEncoder_thread_entry,RT_NULL,1024,4,20); 6rt_thread_startup(MotorL_encoder_thread); 7rt_thread_tMotorR_encoder_thread; 8MotorR_encoder_thread=rt_thread_create("MotorREncoder",MotorREncoder_thread_entry,RT_NULL,1024,4,20); 9rt_thread_startup(MotorR_encoder_thread); 10... 11} 12voidMotorLEncoder_thread_entry(void*parameter) 13{ 14rt_device_tLpwm_input_dev; 15rt_uint16_twait_i; 16structpwm_inputcapture_device*inputcap_dev; 17Lpwm_input_dev=rt_device_find("pwm_inputcap1"); 18if(Lpwm_input_dev==RT_NULL) 19return; 20inputcap_dev=(structpwm_inputcapture_device*)Lpwm_input_dev; 21rt_device_open(Lpwm_input_dev,RT_DEVICE_OFLAG_RDONLY); 22while(1) 23{ 24if(inputcap_dev->sem!=RT_NULL) 25{ 26rt_sem_take(inputcap_dev->sem,RT_WAITING_FOREVER); 27if(wait_i++>=1000) 28{ 29rt_kprintf("MotorLencoder: %d ",inputcap_dev->pulse_param.pulsewidth_us*10000/inputcap_dev->pulse_param.pulsecycle_us); 30wait_i=0; 31} 32} 33} 34} 35voidMotorREncoder_thread_entry(void*parameter) 36{ 37rt_device_tRpwm_input_dev; 38rt_uint16_twait_i; 39structpwm_inputcapture_device*inputcap_dev; 40Rpwm_input_dev=rt_device_find("pwm_inputcap3"); 41if(Rpwm_input_dev==RT_NULL) 42return; 43inputcap_dev=(structpwm_inputcapture_device*)Rpwm_input_dev; 44rt_device_open(Rpwm_input_dev,RT_DEVICE_OFLAG_RDONLY); 45while(1) 46{ 47if(inputcap_dev->sem!=RT_NULL) 48{ 49rt_sem_take(inputcap_dev->sem,RT_WAITING_FOREVER); 50if(wait_i++>=1000) 51{ 52rt_kprintf("MotorRencoder:%d ",inputcap_dev->pulse_param.pulsewidth_us*10000/inputcap_dev->pulse_param.pulsecycle_us); 53wait_i=0; 54} 55} 56} 57}

  目前只是实现了大概1S钟打印一次编码器位置,一圈的机械角度范围扩大到了0~10000(我用的是12位磁编码器,分辨率是4096,我这里统一归一化到了10000),终端输出如下:

wKgaomUxb86AWvABAADp-6S53R4467.png

  可以看到,慢慢向一个方向推动小车,两个编码器的变化规律是相反的,和实际的两个电机对向安装相匹配,实际使用的时候按照其中一个为基准,把另外一个编码器数据反向即可。

  只看其中一个轮子,输出频率改为原有的1Khz,输出值转换为浮点的角度值,可得到如下的测试曲线:

wKgaomUxb8-ASU3AAABtDl44Qdk079.jpg

静止不动,暂时也没有驱动电机,也就没有电机的电磁干扰,在次条件下测得的静态数据如下,静态稳定度在0.2度左右,比12位的最小测量精度0.088度大了二倍多一点:

wKgaomUxb8-ASXSrAACu0yvNQTo492.jpg

相关链接


本系列首篇文章链接:

https://club.rt-thread.org/ask/article/5c0c4ba7eb4ab1ad.html

———————End——————

wKgaomUxb9CAVirTAHiX-BnG6Ho919.gif

点击阅读原文进入官网


原文标题:无刷电机小车开发记录03——PWM信号输入捕获驱动

文章出处:【微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • RT-Thread
    +关注

    关注

    31

    文章

    1273

    浏览量

    39926

原文标题:无刷电机小车开发记录03——PWM信号输入捕获驱动

文章出处:【微信号:RTThread,微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    浅谈无刷电机的工作流程

    上一期的芝识课堂,我们跟大家一起分析了无刷电机的四个功能单元,并详细分析了PWM和逆变器单元的工作情况,今天我们继续来熟悉无刷电机工作流程中另外两个重要的部分——转子位置检测和波形驱动
    的头像 发表于 11-12 13:46 358次阅读
    浅谈<b class='flag-5'>无刷电机</b>的工作流程

    无刷电机驱动方案的应用

    : 有刷电机:有刷电机通过碳刷和换向器(或称为整流子)来传递电流。 无刷电机无刷电机没有碳刷和换向器,而是通过电子换向器(通常是霍尔传感器和电子
    发表于 10-21 16:00

    其利天下技术·无刷电机中开环控制和闭环控制的区别·无刷电机驱动方案

    风扇驱动方案开环控制的原理开环控制是指系统的输出不影响输入控制信号的策略。在无刷电机中,控制信号(如PW
    的头像 发表于 10-16 10:30 340次阅读
    其利天下技术·<b class='flag-5'>无刷电机</b>中开环控制和闭环控制的区别·<b class='flag-5'>无刷电机</b><b class='flag-5'>驱动</b>方案

    其利天下技术·无刷电机寿命有多长?软件能改善吗?无刷电机驱动

    作为无刷电机驱动方案开发商,经常跟客户都会讨论到一个问题——无刷电机的寿命受什么因素影响?现在无刷电机的制作工艺对比以前已经有了极大的提升,
    的头像 发表于 09-23 16:01 258次阅读
    其利天下技术·<b class='flag-5'>无刷电机</b>寿命有多长?软件能改善吗?<b class='flag-5'>无刷电机</b><b class='flag-5'>驱动</b>

    其利天下技术·低速无刷电机如何更好的控制?无刷电机驱动方案开发

    需要低速运转的无刷电机经常运用在电动车、机器人关节、医疗设备等场景。最近有客户找到我们开发无刷电机驱动方案,他的需求是低转速,而且需要精准控制电流。经常
    的头像 发表于 09-09 11:50 401次阅读
    其利天下技术·低速<b class='flag-5'>无刷电机</b>如何更好的控制?<b class='flag-5'>无刷电机</b><b class='flag-5'>驱动</b>方案<b class='flag-5'>开发</b>

    其利天下技术开发·无刷电机常见的5种启动方式·无刷电机驱动方案

    作为经常跟无刷电机打交道的驱动方案商,能够根据不同的无刷电机选择匹配的启动方式,是基本的技术要求。今天分享一下,无刷电机常见的启动方式有哪些?分别又有什么优缺点呢?
    的头像 发表于 08-21 10:59 482次阅读
    其利天下技术<b class='flag-5'>开发</b>·<b class='flag-5'>无刷电机</b>常见的5种启动方式·<b class='flag-5'>无刷电机</b><b class='flag-5'>驱动</b>方案

    PWM驱动电机发热的原因

    PWM技术被广泛应用于电机的调速和控制。 1.2 PWM驱动电机的工作原理 PWM
    的头像 发表于 08-08 15:25 796次阅读

    其利天下技术·无刷电机常见的5种启动方式及优缺点·无刷电机驱动方案设计开发

    作为经常跟无刷电机打交道的驱动方案商,能够根据不同的无刷电机选择匹配的启动方式,是基本的技术要求。今天分享一下,无刷电机常见的启动方式有哪些?分别又有什么优缺点呢?   其利天下·
    的头像 发表于 08-07 10:26 633次阅读
    其利天下技术·<b class='flag-5'>无刷电机</b>常见的5种启动方式及优缺点·<b class='flag-5'>无刷电机</b><b class='flag-5'>驱动</b>方案设计<b class='flag-5'>开发</b>

    无刷电机驱动原理 直流有刷电机无刷电机的差异

    在上期芝识课堂中,我们一起认识了东芝无刷电机的基本知识,对于生活中无处不在的无刷电机,了解它的更多知识才能上手开发哦~今天芝子就带你一起来学习无刷电机
    的头像 发表于 07-19 14:29 1279次阅读
    <b class='flag-5'>无刷电机</b>的<b class='flag-5'>驱动</b>原理 直流有刷<b class='flag-5'>电机</b>和<b class='flag-5'>无刷电机</b>的差异

    无刷电机驱动器的工作原理和结构组成

    无刷电机驱动器,也被称为无刷电调或无刷电机电子调速器,是一种能有效控制电机转速、扭矩和位置的新型电机驱动
    的头像 发表于 06-12 16:54 2221次阅读

    CS32F03X生成多路PWM控制无刷电机换相

    本技术文档旨在帮助用户快速配置 MCU CS32F03X 输出多路经过调制的 PWM 波,用于控制直流无刷电机(BLDC)换相。HPWM-LON 是直流无刷电机较为常用的
    发表于 05-16 10:41

    无刷电机是什么意思 无刷电机是交流还是直流

    无刷电机(Brushless Motor)是一种将电能转换为机械能的驱动装置,也被称为无刷直流电机(BLDC Motor)。与传统的有刷电机相比,
    的头像 发表于 02-01 13:57 1.2w次阅读

    一个通道如何捕获PWM的频率和占空比?

    捕获。虽然也实现了捕获PWM的功能,但是代价也太大了,且很难同时捕获多个PWM,那有没有更好的方法呢?本文介绍了另一种
    的头像 发表于 12-30 08:00 2759次阅读
    一个通道如何<b class='flag-5'>捕获</b><b class='flag-5'>PWM</b>的频率和占空比?

    无刷电机驱动板怎么接线

    驱动板的接线方法,包括电源接线、电机接线和传感器接线等方面,以帮助读者更好地理解和应用无刷电机驱动板。 电源接线 电源输入
    的头像 发表于 12-14 11:22 4819次阅读

    pwm是怎么驱动电机的?

    pwm是怎么驱动电机的? 本文将详尽探讨脉宽调制(PWM)技术及其在电机驱动中的应用。首先,我们
    的头像 发表于 12-07 15:48 1774次阅读