前言
该部分是pwm波的深入应用,如那个引脚输出何种形式的脉冲以及DMA的基础应用。
stm32的基本知识补充
没事的时候,可以看下stm32中文手册
pwm的基本配置
- 使能定时器以及相应的端口的时钟
RCC_APB2PeriphClockCmd
- 初始化I/O口为
推挽复用模式
,GPIO_Mode = GPIO_Mode_AF_PP
- 是否进行重映射配置
- 初始化定时器,ARR,PSC等,
TIM_TimeBaseInit
- 初始化输出比较参数
TIM_OC4PreloadConfig
- 使能预装载寄存器,
TIM_OC4PreloadConfig
,使能定时器TIM_Cmd
定时器输出形式
TIM_SetCompare4(TIMx,i); //TIMx的ch4输出,对应mini的PA11
个人理解
- 更换输出口的时候要查看芯片手册,找出某个引脚对应的
TIMx
以及CH1~4
,如果需要重映射可以参考GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE)
- Tout= ((arr+1)*(psc+1))/Tclk
- Tclk: TIMx 的输入时钟频率(单位为 Mhz)
- Tout: TIMx溢出时间(单位为 us)
- arr:自动重装值
- psc:分频系数
- psc+arr:一般设置为入口参数,用于调节定时周期
- 如arr = 200-1,psc = 7200-1,则其产生的脉冲周期为50hz,20ms
- 使能相应的CHx,以及TIMx
重要
关于输出指定大小的方波来控制舵机旋转。
-
控制PWM输出模式,寄存器
TIMx_CCMR1
,控制代码TIM_OCInitStructure.TIM_OCMode
-
PWM模式1:在向上计数时,一旦
TIMx_CNT<TIMx_CCR1
时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1
时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。 -
PWM模式2:在向上计数时,一旦
TIMx_CNT<TIMx_CCR1
时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1
时通道1为有效电平,否则为无效电平。
可以参考
stm32控制舵机旋转到不同角度
STM32控制舵机的原理及代码
完成相应配置
控制线用于传输角度控制信号。这个角度是由控制信号脉冲的持续时间决定的,这叫做脉冲编码调制(PCM)。舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms-2.5ms范围,总间隔为2ms。脉冲的宽度将决定马达转动的距离。例如:1.5毫秒的脉冲,电机将转向90度的位置(通常称为中立位置,对于180°舵机来说,就是90°位置)。如果脉冲宽度小于1.5毫秒,那么电机轴向朝向0度方向。如果脉冲宽度大于1.5毫秒,轴向就朝向180度方向。以180度舵机为例,对应的控制关系是这样的:
0.5ms————-0度;
1.0ms————45度;
1.5ms————90度;
2.0ms———–135度;
2.5ms———–180度;
计算方法
舵机配置需要满足频率为50HZ,PWM占空比是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,由于PWM周期为20ms,所以(以舵机会转动 45°为例),占空比就应该为1ms/20ms = 5%,所以TIM_SetCompare1的 TIMx 捕获比较 1 寄存器就为200-200*5% = 190
DMA的基本知识
DMA,全称为: Direct Memory Access,即直接存储器访问。 DMA 传输方式无需 CPU 直接
控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备
开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
配置
.c
DMA_InitTypeDef DMA_InitStructure;
u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度
/**
* @brief DMA1的各通道配置,从存储器->外设模式/8位数据宽度/存储器增量模式
* @param DMA_CHx:DMA通道CHx
* @param cpar:外设地址
* @param cmar:存储器地址
* @param cndtr:数据传输量
* @retval None
* @note 这里的传输形式是固定的,这点要根据不同的情况来修改
* @note_time 2020-11-23 小刘同学
**/
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值
DMA1_MEM_LEN=cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设ADC基地址
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_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
}
/**
* @brief 开启一次DMA传输
* @param DMA_Channel_TypeDef *DMA_CHx DMA的通道口地址
* @retval None
* @note_time 2020-11-19 小刘同学
**/
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
DMA_Cmd(DMA_CHx, ENABLE); //使能USART1 TX DMA1 所指示的通道
}