之前用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个脉冲给步进驱动器,那么我需要分多次发送。这部分我后面会继续做,有空会分享给小伙伴们。
上一篇:stm32入门——PWM输出控制直流电机变速
下一篇:stm32103R8C6 捕捉2路pwm 串口打印捕捉数据
推荐阅读
史海拾趣
AIE公司始终将品质管理作为企业发展的核心。公司建立了严格的质量管理体系,从原材料采购到产品生产、销售等各个环节都进行严格的监控和管理。同时,AIE还注重与客户的沟通和反馈,及时了解客户的需求和意见,不断改进产品和服务。这种对品质的执着追求和对客户的尊重赢得了客户的广泛信任和支持。
在这个故事中,我们将探讨ERP Power如何在云计算技术的推动下实现创新发展。通过引入云计算技术,ERP Power系统为企业提供了更加灵活、可扩展的信息化解决方案,帮助企业降低了IT成本并提高了系统安全性。
在人才方面,启臣微一直把人才作为公司发展的核心驱动力。公司建立了一套完善的人才培养机制,为员工提供广阔的职业发展空间和良好的工作环境。同时,公司还积极引进国内外优秀人才,为公司的创新发展注入了新的活力。这些优秀人才的加入,不仅提升了公司的技术水平和管理能力,也为公司的发展注入了新的动力。
天二科技(EVER OHMS)于1988年正式成立,其初创团队便专注于电阻器领域的研发与制造。面对当时电子行业的激烈竞争,天二科技凭借对电阻器技术的深入理解,成功开发出多款高性能、高精度的电阻器产品,迅速在市场中崭露头角。这些产品不仅满足了当时市场的需求,更为公司后续的发展奠定了坚实的基础。
面对日益激烈的市场竞争,ERG始终坚持技术研发和创新。他们与多所知名大学和科研机构建立合作关系,共同研发新型电源技术和照明解决方案。这些合作不仅为ERG带来了先进的技术和研发资源,还使其在行业中保持了技术领先地位。通过与科研机构的紧密合作,ERG不断推出更具创新性和竞争力的产品。
面对不断变化的市场环境和客户需求,Abbatron公司始终坚持创新发展的理念。公司不断投入研发资金,加强人才培养和团队建设,推动技术创新和产品升级。同时,公司还积极探索新的商业模式和市场机会,为未来的发展奠定了坚实的基础。
请注意,以上故事仅为示例,并非基于Abbatron公司的实际发展情况。您可以根据Abbatron公司的实际情况和公开资料,对这些故事进行改编和补充,以更好地反映该公司的发展历程和成就。
IAR5.3(评估版)编译老是提示如下的错误,是怎么一回事啊 Error[Lp021]: the destination for compressed initializer batch "P2 mid-1" is placed at an address that is dependent on the size of the batch, which is not allowed when using packbits compression. Consider using "initialize b ...… 查看全部问答∨ |
我用的是2450,问一下LCD横屏转竖屏,驱动程序里除了在头文件处修改分辨率外,还要修改什么地方呢? 我只修改了头文件处定义的分辨率的情况下,屏幕变窄了(部分屏幕黑色没图象),竖直方向靠下的部分没被显示出来。 不知道在哪(几)个文件里的函 ...… 查看全部问答∨ |
现有一外部中断,中断来临后要求驱动马上读取数据,现在问题是:中断来临后,怎么通知用户主动读取数据,现在我用的是使用了中断上下部,下部处理中断,一产生中断马上进入上半部分处理接收,并传送到用户空间,那么此时的读如果在没有 ...… 查看全部问答∨ |
知道timestool的请进(一个实时调度模拟与分析工具)!!! 请教了解瑞典的乌普萨拉(Uppsala)大学开发的times的大侠,还有什么工具与times的功能差不多??? 小弟在此先谢过了!… 查看全部问答∨ |
1.安装前须仔细核对型号及规格,指示指针不得偏出零位标记的黑框,否则应重新校验或更换。 硅橡胶电缆 2、仪表应安装于周围环境(或介质)温度-40~ 55℃,相对湿度不大于85%,振动或被测压力的急剧脉动对正确读数等无影响的环境下使用。 3151压力变 ...… 查看全部问答∨ |
这两天,下了个ucos在STM32F103ZE-SK开发板上移植的官方例程(uCOSII-ST-STM32F103ZE-SK),这其中有两个文件夹,u ...… 查看全部问答∨ |
有偿寻找RF信号 放大方案,具体要求如下: RF 频率:912MHz 带宽为:902.2 MHz ~ 927.8 MHz。 放大前信号为:10dBm 要求放大后信号半径为:600M. 本人在深圳,欢迎有方案的个人或者公司联系洽谈。 QQ:516236905… 查看全部问答∨ |
|
想产生两对带死区互补对称的PWM控制逆变全桥的四个管子,TIM1_CH1(PA8)和TIM1_CH1N(PB13),TIM1_CH2(PA9)和TIM1_CH2N(PB14),请问初始化设置用库的结构体定义一个变量还是两个变量呢? 例如: 1、 GPIO_InitTypeDef G ...… 查看全部问答∨ |
求购一块ST M0 DISCO ,030.051,072的都可以 本帖最后由 ddllxxrr 于 2016-1-7 16:12 编辑 求购一块ST M0 DISCO ,030. 051,072的都可以 有意者加QQ 2948530632 … 查看全部问答∨ |