代码目的:
STM32与FPGA通讯,通过8位并口线进行通讯,16byte的数据在10us之内通过8位并口数据线传给FPGA,FPGA读取该数据。
HAL库设置说明:
时钟采用80MHz,由于16byte的数据要在10us之内传完,那么10/(16*2)=0.3125us/次,也就是传输频率得≥3.2MHz。
定时器设置:为了方便起见,先选用了4MHz的传输频率。80MHz/((1+1) * (9 + 1)) = 4MHz,PWM的占空比为5/10=50%
-
关于PWM
PWM中Pulse与占空比有关,当定时器计数递增模式下,计数值从0开始,当CNT的值小于CCRx(也就是Pulse),则输出CH Polarity的极性,当大于CCRx则输出相反极性,当CNT的值=ARR(counter period),则定时器溢出重新开始计数。 -
启动PWM有3种方式
顾名思义可以看出来,第一种启动后就开始输出;第二种是带中断方式,能产生中断;第三种就是DMA的方式,这种应用在电机精准步进控制上比较多,我打算通过这章方式应用到数据传输中,因为传输速度比较快,如果人为进行管脚输出我试过,16byte的数据即使在80MHz的时钟下也得50us左右,因为准备用DMA的方式。以上三种方式各有各的应用场景。
定时器触发DMA设置说明:传输方向是内存到外设端口,传输宽度16位=half word
代码实现
由于我设置的传输宽度是16位的,因此定义一组16位的数据缓冲区:
uint16_t databuf[16];
原来的HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length)查看具体的代码可以知道,主要是针对PWM的占空比进行配置的。因此重写该函数,将外设地址改为GPIOF端口:
HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)pData, (uint32_t)(&(GPIOF->ODR)), Length)
在主函数中进行调用即可。
MY_TIM_PWM_Start_DMA(&htim5,TIM_CHANNEL_2,(uint32_t *)(&databuf), 16);
在回调函数HAL_TIM_PWM_PulseFinishedCallback中停止DMA,并置标志位u8dmacop ,为下一次传输做准备。
while (1)//主函数中
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(u8dmacop)
{
u8dmacop = 0;
htim5.Instance->CNT = 0;
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_SET);
MY_TIM_PWM_Start_DMA(&htim5,TIM_CHANNEL_2,(uint32_t *)(&databuf), 16);
}
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if( htim == &htim5)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_RESET);
u8dmacop = 1;
HAL_TIM_PWM_Stop_DMA(&htim5, TIM_CHANNEL_2);
htim5.Instance->CNT = 0;
}
}
其中HAL_TIM_PWM_PulseFinishedCallback是计数器CNT = CCRx值时进行触发,另外还有一个函数HAL_TIM_PWM_PeriodElapsedCallback,该函数是计数溢出时触发,也就是整个周期结束,CNT=ARR时。
为了用示波器查看方便,把缓冲区数据做递增处理,使得8位并口线最低位可以看到数据的来回变化。用示波器在PF7管脚可以看到4MHz的PWM波在输出,数据最低位管脚在2MHz的频率变化,输出波形有8个,也就是16组数据。