说明
在离散系统控制算法运算中,控制系统通常采用定步长采样。已知固定的采样步长,可以方便进行控制系统的微分、积分运算。这里参考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周期输出电平置反,使用示波器测量出来周期大小,从而修改控制采样步长。