STM32定步长控制周期框架

说明

        在离散系统控制算法运算中,控制系统通常采用定步长采样。已知固定的采样步长,可以方便进行控制系统的微分、积分运算。这里参考matlab simulink生成的STM32代码,使用STM32通用定时器编写STM32定步长采样控制框架。并且在主程序while循环里面使用GPIO输出置反的方法,用示波器测量出实际的采样步长。

平台说明

芯片:STM32F429IGT6

simulink生成代码

        关于simulink如何生成代码可以参考:

simulink联合STM32CubeMX开发串口通信程序_simulink stm32_ChownQ的博客-CSDN博客

        关于simulink生成的定步长采样代码分析可以参考

利用simulink开发stm32所设置的采样频率在所生成代码中的体现_simulink的s-function building怎么设置采样频率_楠神神的博客-CSDN博客

        simulink生成的STM32代码下载地址:

simulink联合STM32CubeMX串口通信_simulink安装包资源-CSDN文库

STM32定步长采样框架

        simulink生成的代码使用的是systik滴答定时器,这里采用通用定时器TIM3,并且配置GPIOD4为输出模式,用于后续测量实际的采样步长。

硬件初始化

固件库代码

1、定时器初始化

//arr:自动重装载值
//psc:时钟预分频数
//定时器溢出时间计算方法::Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位Mhz
void TIM3_Int_Init(uint16_t arr,uint16_t psc)
{
	RCC->APB1ENR|=1<<1;	 
 	TIM3->ARR=arr;  	
	TIM3->PSC=psc;  	
	TIM3->DIER|=1<<0;   
	TIM3->CR1|=0x01;    
	
	NVIC_InitTypeDef	NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}
void testFreq_Init(void)
{    	 
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	GPIO_ResetBits(GPIOD,GPIO_Pin_4);
}

2、中断函数

void TIM3_IRQHandler(void)
{ 		    		  			    
	if(TIM3->SR&0X0001)//溢出中断
	{
		if (remainAutoReloadTimerLoopVal_S) {
            remainAutoReloadTimerLoopVal_S--;//定时器标志位复位

		}	    				   				     	    	
	}				   
	TIM3->SR&=~(1<<0);//清除中断标志位
		
}

3、驱动初始化

void BSP_Init()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	TIM3_Int_Init(Control_Freq/0.0001-1,9000-1);//定时器初始化
	testFreq_Init();//GPIO初始化
    /*
        其他外设初始化
    */
}

HAL库代码

定时器配置

TIM_HandleTypeDef htim3;

/* TIM3 init function */
void MX_TIM3_Init(uint16_t arr,uint16_t psc)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = psc;
  htim3.Init.CounterMode = TIM_COUNTERMODE_DOWN;
  htim3.Init.Period = arr;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */

  /* USER CODE END TIM3_MspInit 0 */
    /* TIM3 clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();

    /* TIM3 interrupt Init */
    HAL_NVIC_SetPriority(TIM3_IRQn, 2, 3);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
  /* USER CODE BEGIN TIM3_MspInit 1 */
	
  /* USER CODE END TIM3_MspInit 1 */
  }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspDeInit 0 */

  /* USER CODE END TIM3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM3_CLK_DISABLE();

    /* TIM3 interrupt Deinit */
    HAL_NVIC_DisableIRQ(TIM3_IRQn);
  /* USER CODE BEGIN TIM3_MspDeInit 1 */

  /* USER CODE END TIM3_MspDeInit 1 */
  }
}

中断设置

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
	if(htim== &htim3){
		if (remainAutoReloadTimerLoopVal_S) {
			remainAutoReloadTimerLoopVal_S--;
		}	 
	}
}

驱动初始化

MX_TIM3_Init(Control_Freq/0.0001-1,9000-1);
HAL_TIM_Base_Start_IT(&htim3);                  /* 使能定时器x和定时器x更新中断 */

对于F429,配置系统时钟为:

        SYSCLK=180M; AHB=180M;APB1=45M(分频系数为180/45=4);

外部定时器时钟使用APB总线。

APB1分频系数是1,则CK_INT=1;

APB1分频系数是2或4或其他系数,则CK_INT=2;

CK_PSC=45*2=90M;

Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk

其中Tclk=90M

        假设定时1ms,自动重装值:10-1分频系数:9000-1,计数方式:向下计数。

        这里定时器3初始化函数,固定分频系数为9000,根据控制周期(Control_Freq)计算自动重装载值  arr=Control_Freq/(9000/90M)。

控制框架

main.c

#include "main.h"

/* Number of auto reload timer rotation computed */
uint8_t autoReloadTimerLoopVal_S = 1;

/* Remaining number of auto reload timer rotation to do */
uint8_t remainAutoReloadTimerLoopVal_S = 1;//定时器状态位

/* Flags for taskOverrun */
static unsigned char OverrunFlags[1];

/*控制步长 */
float Control_Freq=0.001;

/**
  * @功	能	主程序入口
  * @参	数	无
  * @返	回	无
  */
int main(void)
{ 

    autoReloadTimerLoopVal_S = 1;
    remainAutoReloadTimerLoopVal_S = autoReloadTimerLoopVal_S;//Set nb of loop to do
	
	/* USER CODE BEGIN WHILE */
    for (uint8_t i=0;i<1;i++) {
        OverrunFlags[i] = 0;
    }
	
	/* 底层驱动初始化 */
	BSP_Init();			
		
	/* 等待命令传入 */
	while(1)
	{	
		/*Process tasks every solver time*/
		
        if (remainAutoReloadTimerLoopVal_S == 0) {
		
            remainAutoReloadTimerLoopVal_S = autoReloadTimerLoopVal_S;
			
			/* Check base rate for overrun */
            if (OverrunFlags[0]) {
				printf("Model Overrun! %d\r\n",OverrunFlags[0]);
            }			
			OverrunFlags[0] = 1;

			/*
            控制代码主体

            */
			GPIO_ToggleBits(GPIOD,GPIO_Pin_4);//GPIO输出置反,用于外部测时序
			OverrunFlags[0] = 0;					
		}    
	}		
}

main.h

#ifndef __MAIN_H
#define __MAIN_H

#include "bsp.h"
#include "stm32f4xx_it.h"


/*控制周期 */
extern float Control_Freq;

/* Remaining number of auto reload timer rotation to do */
extern uint8_t remainAutoReloadTimerLoopVal_S;

/*
其他全局变量
*/

#endif

主程序通过判断变量remainAutoReloadTimerLoopVal_S是否为0,进而判别定时器是否到时间进入主程序。如果出现主程序运行时间超过定时器设定时间,这时候可以通过GPIO周期输出电平置反,使用示波器测量出来周期大小,从而修改控制采样步长。                                                           

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值