历史上的今天
返回首页

历史上的今天

今天是:2024年08月22日(星期四)

正在发生

2019年08月22日 | STM32——使用PWM+DMA实现脉冲发送精确控制

2019-08-22 来源:eefocus

之前用stm32写过脉冲发送的代码,用来控制步进电机,但是缺点明显,之前是用定时器中断做的,所以一但控制的电机多起来,MCU资源占用就很大,这在大多数情况下是不可接受的,更不用说多轴联动了。

最近做的步进电机CAN总线控制系统,就想顺便重新写驱动。希望做到占用很少的MCU资源,实现脉冲发送的精确控制。既然是用来控制步进电机,那么脉冲的数量和频率一定要可控,要不然怎么实现电机的加减速曲线。于是就想到了DMA。


DMA (直接存储器访问)

DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。(资料来自百度百科)


在记忆里,STM32的数据手册中有提到PWM有DMA触发的模式。那么这一次终于有用武之地了。


show me your code !!!

main.c

#include "delay.h"

#include "key.h"

#include "sys.h"

#include "usart.h"

#include "dma.h"

#include "timer.h"

#define size 100


extern u16 DMA1_MEM_LEN;

extern DMA_InitTypeDef DMA_InitStructure;  

u16 send_buf[size];

 

int main(void)

{

int i;

int feedback;

delay_init();

uart_init(115200);

KEY_Init();

DMA_Config(DMA1_Channel6, (u32)&TIM3->ARR, (u32)send_buf, size);

TIM3_PWM_Init(599,7199);

for(i = 0; i < size; ++i)

{

if(i != size - 1)

send_buf[i] = 100 + 10 * i;

else

send_buf[i] = 0;

}

DMA_Enable(DMA1_Channel6);

while(1)

{

feedback = DMA_send_feedback(DMA1_Channel6);

if(feedback != 0)

{

printf("-> ");

printf("%drn", DMA_send_feedback(DMA1_Channel6));

}

if(KEY_Scan(0) == 1)

{

DMA_Enable(DMA1_Channel6);

}

}

}


main.c中的send_buf[size]是控制信息的来源,你想要如何发送脉冲,全靠这个buffer

这里写图片描述

这是send_buf的数据组成,size 决定了发送脉冲的数量information决定了脉冲的频率。至于脉冲的脉宽,可以在timer.c中的初始化函数中修改。


dma.c

#include "dma.h"


extern u16 send_buf[100];

extern TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

DMA_InitTypeDef DMA_InitStructure;

u16 DMA1_MEM_LEN; /* 保存DMA每次数据传送的长度 */


/*

 *DMA1的各通道配置

 *这里的传输形式是固定的,这点要根据不同的情况来修改

 *从存储器->外设模式/8位数据宽度/存储器增量模式

 *DMA_CHx:DMA通道CHx

 *cpar:外设地址

 *cmar:存储器地址

 *cndtr:数据传输量

 */

void DMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)

{

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* 使能DMA钟源 */

delay_ms(5);

  DMA_DeInit(DMA_CHx);   /* 将DMA的通道1寄存器重设为缺省值 */


DMA1_MEM_LEN=cndtr;

DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  /* DMA外设基地址 */

DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  /* DMA内存基地址 */

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  /* 数据传输方向,从内存读取发送到外设 */

DMA_InitStructure.DMA_BufferSize = cndtr;  /* DMA通道的DMA缓存的大小 */

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  /* 外设地址寄存器不变 */

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  /* 内存地址寄存器递增 */

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  /* 数据宽度为16位 */

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; /* 数据宽度为16位 */

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  /* 工作在正常模式 */

DMA_InitStructure.DMA_Priority = DMA_Priority_High; /* DMA通道 x拥有中优先级 */

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  /* DMA通道x没有设置为内存到内存传输 */

DMA_Init(DMA_CHx, &DMA_InitStructure); 


/* 开启一次DMA传输 */

void DMA_Enable(DMA_Channel_TypeDef*DMA_CHx)

{

DMA_Cmd(DMA_CHx, DISABLE );

TIM3->ARR = 2; /* 由于最后一项是0,所以在最后的时刻ARR会被清零,导致下一次启动无效。*/

  DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);

  DMA_Cmd(DMA_CHx, ENABLE);

TIM_Cmd(TIM3, ENABLE);  /* 使能TIM3 */

TIM3->EGR = 0x00000001; /* 由于最后一次ARR值为0,这是为了停止定时器对io口的操作,但是不要忽略了一点:CNT并没有停止计数,而且是不会再停下来,如果没有手动操作的话,所以需要在每次dma使能时加上一句,将EGR里的UG位置1,清零计数器 */

}   


/*

 *进度反馈,返回剩下的数据量

 */

u16 DMA_send_feedback(DMA_Channel_TypeDef* DMA_CHx)

{

return DMA_CHx->CNDTR;


dma.h

#ifndef __DMA_H

#define __DMA_H    

#include "sys.h"

#include "delay.h"

#include "dma.h"

#include "timer.h"

#include "usart.h"

#include "stm32f10x_dma.h"


void NVIC_Configuration(void);         


void DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);//配置DMA1_CHx


void DMA_Enable(DMA_Channel_TypeDef*DMA_CHx);//使能DMA1_CHx


u16 DMA_send_feedback(DMA_Channel_TypeDef* DMA_CHx);


void DMA1_Channel6_IRQHandler(void);

#endif


timer.c

#include "timer.h"

#include "led.h"

#include "usart.h"

 

 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

/*

 *TIM3 PWM部分初始化 

 *PWM输出初始化

 *arr:自动重装值

 *psc:时钟预分频数

 */

void TIM3_PWM_Init(u16 arr,u16 psc)

{  

GPIO_InitTypeDef GPIO_InitStructure;

TIM_OCInitTypeDef  TIM_OCInitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* 使能定时器3时钟 */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  /* 使能GPIO外设 */   

 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; /* TIM_CH1*/

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  /* 复用推挽输出 */

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);/* 初始化GPIO */

 

   /* 初始化TIM3 */

TIM_TimeBaseStructure.TIM_Period = arr; /* 设置在下一个更新事件装入活动的自动重装载寄存器周期的值 */

TIM_TimeBaseStructure.TIM_Prescaler =psc; /* 设置用来作为TIMx时钟频率除数的预分频值 */

TIM_TimeBaseStructure.TIM_ClockDivision = 0; /* 设置时钟分割:TDTS = Tck_tim */

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  /*TIM向上计数模式 */

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); /* 根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 */

 

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; /* 选择定时器模式:TIM脉冲宽度调制模式1 */

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; /* 比较输出使能 */

//TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  /* 使能TIM3在CCR1上的预装载寄存器*/

TIM_OCInitStructure.TIM_Pulse= 100;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; /* 输出极性:TIM输出比较极性高 */

TIM_OC1Init(TIM3, &TIM_OCInitStructure);  /* 根据T指定的参数初始化外设TIM3 OC1 */

//TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE); /* 如果是要调节占空比就把这行去掉注释,然后注释掉下面那行,再把DMA通道6改为DMA通道3 */

TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);

TIM_Cmd(TIM3, ENABLE);  /* 使能TIM3 */

}


timer.h

#ifndef __TIMER_H

#define __TIMER_H

#include "sys.h"


void TIM3_PWM_Init(u16 arr,u16 psc);


#endif


看看运行效果

这里写图片描述

垃圾示波器,玩玩就好。。。。

篇幅限制,结果是最终将会发送100个脉冲,频率慢慢改变。但是在这个有一点需要提醒小伙伴们注意一下,send_buf的size并不是要多大有多大的,比较STM32的RAM有限,没办法一下子给你分配那么多空间,如果你强行分配的话,那么在编译过程中一定会报错,我在测试的时候,分配到3W+的时候已经差不多是极限了,但是这仅仅只是一个demo程序,在实际应用的时候不可能把RAM都给send_buf,所以如果想要发送大量的脉冲的话,比如我想发409600个脉冲给步进驱动器,那么我需要分多次发送。这部分我后面会继续做,有空会分享给小伙伴们。

推荐阅读

史海拾趣

Advanced Energy公司的发展小趣事

AE公司一直将创新作为企业发展的核心动力。公司不断投入研发资源,推动电源技术的持续创新。同时,AE公司也积极与各大电子企业建立合作伙伴关系,共同推动电子行业的发展。通过与合作伙伴的紧密合作,AE公司不仅获得了更多的市场机会,也提升了自身的技术水平和创新能力。

Analogix Semiconductor公司的发展小趣事

在发展过程中,AE公司不断突破技术瓶颈,实现了多项重要技术的创新。这些技术突破不仅提升了公司产品的性能和质量,也推动了整个电子行业的发展。随着技术的不断进步,AE公司开始将业务拓展至全球市场。通过与各国客户的合作与交流,AE公司不断了解市场需求,优化产品设计,为全球客户提供更加优质的电源解决方案。

ELM Technology Corporation公司的发展小趣事

ELM Technology Corporation非常重视企业文化的建设。公司倡导“创新、协作、诚信、责任”的企业精神,鼓励员工积极创新、勇于担当。同时,公司还为员工提供了良好的工作环境和福利待遇,增强了员工的归属感和凝聚力。这种积极向上的企业文化为ELM的持续发展提供了强大的动力。

请注意,以上故事仅为概述,具体细节和事实需要根据ELM Technology Corporation的实际情况进行补充和完善。

上海超致公司的发展小趣事

上海超致半导体科技有限公司于2015年成立,在成立之初,公司就明确了专注于高端高压功率半导体(Super-Junction MOSFET、IGBT)的集成电路产品设计方向。公司拥有一支在功率半导体器件领域拥有丰富经验的设计与运营团队,这些成员大多具有超过15年的行业经验。在公司成立的早期,上海超致就成功研发出了多款具有竞争力的产品,为公司的后续发展奠定了坚实的基础。

Elite Semiconductor Products Inc公司的发展小趣事

随着技术的不断进步和市场的不断变化,Elite意识到单一市场已经无法满足公司的发展需求。因此,公司开始积极拓展国际市场,寻求更广阔的发展空间。在海外市场拓展过程中,Elite注重了解当地市场的需求和文化特点,制定针对性的市场策略。同时,公司还积极与当地企业建立合作关系,共同开拓市场。这些努力让Elite在国际市场上取得了不俗的成绩,也为公司的持续发展注入了新的动力。

艾为(AWINIC)公司的发展小趣事

艾为电子注重与产业链上下游企业的合作,通过协同创新和资源共享,实现共赢发展。例如,艾为与歌尔股份建立了长期合作伙伴关系,双方在声光电射手等领域展开了深度合作,共同推出了多款优秀产品。此外,艾为还与多家ODM厂商和智能硬件企业建立了紧密的合作关系,共同推动产业链的协同发展。

问答坊 | AI 解惑

透视中国电子工程社群:谁在拿高薪?

一谈到高薪,任职于深圳一家著名手机设计公司的小Z就跳了起来,“现在房子、车子、女朋友……哪一个不得花钱?薪水绝对是我找工作的第一因素。”或许是由于中国各大城市的生活成本不断上涨,在本次采访中,我们发现,几乎所有的工程师都和小Z持相同 ...…

查看全部问答>

酓ummy_isr error, interrupt num:5 ,INTMSK = 0xffffffda是什么意思啊

搞2440的外部中断,一按下键,超级终端就出现上面的东西,然后程序也不动了。 这个是什么意思呢?拜托各位大虾帮忙看看吧…

查看全部问答>

霹雳游侠扫描灯20模式带拖尾C程序

/****** 霹雳游侠扫描灯20模式带拖尾C程序,ME850调试正常*****2009/05/30*******/ #include //头文件 rzmzy(); //红色走马左移声明 rlszy(); //红色流水左移声明 rlsyy(); //红色流水右移声明 rzmyy(); //红色走马右移声明 rzmnwyd(); //红色走马 ...…

查看全部问答>

关于液晶显示

我之前使用的液晶是1602,采用51控制的。由于1602的屏幕过小,观看比较困难,有没有控制方式与1602相同的,屏幕比较大液晶显示屏,字符的或汉字的都可以,不用改动控制电路。可以的话给给些相关资料。谢谢!!!!!!…

查看全部问答>

华硕K40id 怎么把WIN7改为XP系统呢

华硕K40id  怎么把WIN7改为XP系统呢华硕K40id  怎么把WIN7改为XP系统呢华硕K40id  怎么把WIN7改为XP系统呢 …

查看全部问答>

eeworld怎么上传东西呀

   eeworld怎么上传东西呀,我找到一个很好用的工具。想给大家共享,怎么上传呀?   谢谢 …

查看全部问答>

在单片机系统里将文件保存为word格式??

单片机系统,外设CF卡存储器,驱动写好了,文件系统FAT16,能正常读写。 现在需要把采集的数据以报表的格式生成word文档(其中包括处理数据得到的曲线图,就是这点最可恨)保存进CF卡里。求帮忙指点。 word文档在磁盘里的存储格式是什么样的?特 ...…

查看全部问答>

请问如何在EVC停止Downloading files

a.exe - 0 error(s), 0 warning(s) 在这步就想停止了,不想进行下面的步骤 Downloading files The Microsoft ActiveSync connection server has failed. Please make sure Microsoft ActiveSync is running and retry the download by Rebuild A ...…

查看全部问答>

用32k外界晶振能产生38Khz的pwm不?

具体怎么实现呀??谢谢指教…

查看全部问答>