STM32 HAL库开发_STM32 HAL库开发[通俗易懂]

STM32 HAL库开发_STM32 HAL库开发[通俗易懂]divid=”article_content”class=”article_contentclearfix”linkrel=”stylesheet”href=”https://csdnimg.cn/release

STM32 HAL库开发_STM32 HAL库开发[通俗易懂]

前言
一、CubeMX配置(第十三届模拟题完整版)
二、代码相关定义、声明
1.函数声明
2.宏定义
3.变量定义
三、主要函数
1.按键扫描
2.配置模式
3.LCD显示
4.频率检测(TIM2输入捕获中断函数)
5.PWM输出(TIM3)
6.Main函数
四、实验结果
1.输入频率检测
2.R值
3.输出频率
五、源码(转载请注明出处)
总结
前言
相关说明:

开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第十三届蓝桥杯嵌入式模拟题
题目难点:根据输入的PWM,实时更新输出PWM,R37电压值为0V时如何输出持续的低电平,电压为3.3V时如何输出持续的高电平。

CubeMX配置、主要函数代码及说明:

一、CubeMX配置(第十三届模拟题完整版)
1.使能外部高速时钟:

2.配置时钟树:

3.GPIO:

4.ADC(默认配置即可):
5.TIM2(输入捕获,检测输入信号的频率):
6.TIM3(PWM输出):
7.NVIC(输入捕获中断配置):

二、代码相关定义、声明
1.函数声明
main.c
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//输入捕获中断函数 计算输入信号频率
void LCD_Init_Show(void);    //LCD初始化显示
void LCD_Refresh(void);        //LCD更新显示 

gpio.h
void KEY_Scan(void);//按键扫描
void LED_AllClose(uint8_t *LED_Close);//LED更新显示
void LED_Change(void);//LED状态改变

adc.h
double ADC_GetValue(void);//获取R37电压值

time.h
void PWM_Out(double R37_V,uint32_t FRQ,uint8_t    R);//PWM输出

2.宏定义
#define LED_GPIO_PORT    GPIOC
#define LED1_GPIO_PIN    GPIO_PIN_8
#define LED2_GPIO_PIN    GPIO_PIN_9
#define LED3_GPIO_PIN    GPIO_PIN_10
#define LED4_GPIO_PIN    GPIO_PIN_11
#define LED5_GPIO_PIN    GPIO_PIN_12
#define LED6_GPIO_PIN    GPIO_PIN_13
#define LED7_GPIO_PIN    GPIO_PIN_14
#define LED8_GPIO_PIN    GPIO_PIN_15

#define ON     GPIO_PIN_RESET
#define OFF GPIO_PIN_SET

#define LED1(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)
#define LED2(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)
#define LED3(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)
#define LED4(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)

#define KEY1_GPIO_PORT    GPIOB
#define KEY1_GPIO_PIN    GPIO_PIN_0
#define KEY2_GPIO_PORT    GPIOB
#define KEY2_GPIO_PIN    GPIO_PIN_1
#define KEY3_GPIO_PORT    GPIOB
#define KEY3_GPIO_PIN    GPIO_PIN_2
#define KEY4_GPIO_PORT    GPIOA
#define KEY4_GPIO_PIN    GPIO_PIN_0

3.变量定义
char str[30];        //用于组合字符串
uint32_t FRQ;        //输入信号频率
uint32_t TIM_Clock=1000000;    //定时器时钟频率
double R37_V;        //R37电压值
uint8_t R=4;        //R值
uint8_t R_step=2;    //R每次改变值
uint8_t R_max=10;    //R改变上限值
uint8_t R_min=2;    //R改变下限值

uint8_t LED_Close[5]={0,0,1,0,0};    //LED关闭数组 值为1,则下标对应LED关闭
uint8_t Page=0;                        //LCD显示页(数据显示0,数据更改1)

三、主要函数
1.按键扫描
尽量将按键实现的功能封装为独立的函数,降低函数耦合度。

void KEY_Scan()
{

    if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)//Setting
    {

        HAL_Delay(10);
        if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
        {

            while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
            Page=1;//页数更新
            LCD_Refresh();//LCD更新显示
            LED_Change();//LED状态改变
            LED_AllClose(LED_Close);//LED更新显示
            Setting_Mode();//进入设置模式
        }
    }
    
    else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)//ban
    {

        HAL_Delay(10);
        if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
        {

            while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);
            LED_ban=!LED_ban;//LED禁用标志位状态翻转
            if(LED_ban)//如果禁用则将LED全部关闭
            {

                LED_Close[1]=1;
                LED_Close[2]=1;
                LED_Close[3]=1;
                LED_Close[4]=1;
            }
            else//否则重新开启
            {

                LED_Close[1]=0;
                LED_Close[2]=0;
                LED_Close[3]=0;
                LED_Close[4]=0;
            }
        }
    }
}

2.配置模式
用到两个函数对数据进行更改:
1.Dat_change(uint16_t mode),参数为数据更改方式(加/减)。
2.Setting_Mode(),按键按下后调用函数即可对数据进行更改操作,更改完后再更新显示。

void Dat_change(uint16_t mode)//数据改变
{

    switch(mode)//数据改变模式(ADD +,SUB -)
    {

        case ADD:
            R+=R_step;
            if(R>R_max)//不可大于上限
                R=R_max;
            break;
        
        case SUB:
            R-=R_step;
            if(R<R_min)//不可小于下限
                R=R_min;
            break;
    }
}

void Setting_Mode()//设置模式
{

    while(1)
    {

        if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)//save
        {

            HAL_Delay(10);
            if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
            {

                while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
                Page=0;//页数更新
                LCD_Refresh();//LCD显示更新
                break;//break;
            }
        }
        
        else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)//++
        {

            HAL_Delay(10);
            if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
            {

                while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET);
                Dat_change(ADD);//R++
                LCD_Refresh();//LCD显示更新
            }
        }
        
        else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)//–
        {

            HAL_Delay(10);
            if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
            {

                while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET);
                Dat_change(SUB);//R–
                LCD_Refresh();//LCD显示更新
            }
        }
    }
}

3.LCD显示
共有两个函数:
1.LCD_Init_Show(),在上电启动后对LCD进行初始化显示操作。
2.LCD_Refresh(),LCD更新显示,数据更新后需要实时进行更新显示。

void LCD_Init_Show()//LCD初始化显示
{

    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
    
    LCD_DisplayStringLine(Line1,(unsigned char*)”       Data           “);
    sprintf(str,”   FRQ:%dHz           “,FRQ);
    LCD_DisplayStringLine(Line3,(unsigned char*)str);
    sprintf(str,”   R37:%.2fV          “,R37_V);
    LCD_DisplayStringLine(Line5,(unsigned char*)str);
}

void LCD_Refresh()//LCD更新显示 
{

    if(Page==0)//数据显示页面
    {

        LCD_DisplayStringLine(Line1,(unsigned char*)”       Data           “);
        sprintf(str,”   FRQ:%dHz           “,FRQ);
        LCD_DisplayStringLine(Line3,(unsigned char*)str);
        sprintf(str,”   R37:%.2fV          “,R37_V);
        LCD_DisplayStringLine(Line5,(unsigned char*)str);
    }
    else if(Page==1)//数据更改页面
    {

        LCD_DisplayStringLine(Line1,(unsigned char*)”       Para           “);
        sprintf(str,”      R:%d            “,R);
        LCD_DisplayStringLine(Line3,(unsigned char*)str);
        LCD_DisplayStringLine(Line5,(unsigned char*)”                      “);
    }
}
4.频率检测(TIM2输入捕获中断函数)
检测输入信号频率分为五步:
1.配置定时器相应通道功能为输入捕获并使能中断,上升沿捕获或下降沿捕获均可。

2.开启输入捕获中断:HAL_TIM_IC_Start_IT(&htimX,TIM_CHANNEL_X);

3.在进入中断函数后,获取定时器的计数值,该计数值/定时器时钟频率即为输入信号周期。

4.频率=1/周期,即频率是周期的倒数,则输入信号频率=定时器时钟频率/计数值。

5.计数值清零。

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//输入捕获中断函数 计算输入信号频率
{

    uint32_t count;
    count=TIM2->CNT;//获取定时器计数值
    FRQ=TIM_Clock/count;//频率=1/周期  所以频率=1/计数值/定时器时钟频率=定时器时钟频率/计数值
    TIM2->CNT=0;//计数值清零
}

5.PWM输出(TIM3)
PWM输出的代码看似很长,但起始有一大段是GPIO结构体的配置,该题配置PWM输出共分为四步:
1.根据R37电压值的不同,将PA7输出分为三种方式:a.电压值为0V;b.电压值为3.3V;c.0V<电压值<3.3V;ab两种情况对应持续的低电平和高电平,c对应PWM输出。

2.ab两种情况时先关闭PWM,并将GPIO引脚输出方式更改为通用推挽输出(否则PA7无法正常输出持续的高低电平),重新初始化GPIO后调用HAL_GPIO_WritePin即可正常输出。

3.c则根据输入信号频率以及R37电压值来配置PWM参数。首先要知道两条公式:
PWM输出频率=定时器时钟频率/重装载值。
占空比=Pulse/重装载值*100%。
现在知道的数据是输出信号频率(输入信号频率/R值)和占空比(R37电压值/3.3V),则可以得出:
重装载值=(定时器时钟频率/输出信号频率)-1。
Pulse=R37电压值/3.3V×重装载值。

4.根据计算得出的数值直接对寄存器进行配置:
TIM3->ARR=Period;TIM3->CCR2=Pulse;
最后再重启启动PWM即可。

void PWM_Out(double R37_V,uint32_t FRQ,uint8_t R) //PWM输出
{

    uint32_t Period;//配置重装载值
    uint32_t Pulse;//配置占空比
    GPIO_InitTypeDef GPIO_InitStruct;//重新配置GPIO 默认为PWM输出 
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    if(R37_V<0.01)//如果R37电压值为0V
    {

        HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);//PWM关闭
        GPIO_InitStruct.Mode =GPIO_MODE_OUTPUT_PP;//配置为通用推挽输出 如不配置 无法正常输出高低电平
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    //重新初始化
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);//输出低电平
    }
    else if(R37_V>3.29)//如果R37电压值为3.3V
    {

        HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);//PWM关闭
        GPIO_InitStruct.Mode =GPIO_MODE_OUTPUT_PP;//配置为通用推挽输出
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    //重新初始化
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET);//输出低电平
    }
    else if(R37_V>0 && R37_V<3.3)//输入PWM
    {

        Period=TIM_Clock/(FRQ/R)-1;//输出PWM的重装载值=(定时器时钟频率/目标频率)-1
        Pulse=R37_V/3.3*Period-1;//输出PWM的Pulse=(R37电压值/3.3*重装载值)-1
        TIM3->ARR=Period;//直接对寄存器进行配置
        TIM3->CCR2=Pulse;
        HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);//开启PWM
    }
}

6.Main函数
注意起始时要将TIM2中断标志位清零:TIM2->SR=0;

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration——————————————————–*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC2_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
    LCD_Init();//LCD初始化
    LCD_Init_Show();//LCD初始化显示
    TIM2->SR=0;//TIM2中断标志位清零
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);//打开TIM2输入捕获中断
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        KEY_Scan();//按键扫描
        R37_V=ADC_GetValue();//更新R37电压值
        LCD_Refresh();//LCD更新显示
        LED_Change();//LED状态改变
        LED_AllClose(LED_Close);//LED更新显示
        PWM_Out(R37_V,FRQ,R); //PWM输出配置
        FRQ=0;//FRQ清零
  }
  /* USER CODE END 3 */
}

四、实验结果
1.输入频率检测
此处用的是另一块开发板为该板提供信号,频率为1KHz;并调节R37使其电压为1.65V,预期占空比为1.65/3.3=50%。

2.R值
默认R值为4,作用为将输入信号进行分频处理;预期输出频率为1K/4=250Hz。

3.输出频率
这里直接用示波器检测输出PWM,频率为250Hz,占空比为50%,符合预期。

五、源码(转载请注明出处)
————————————————
版权声明:本文为CSDN博主「AゞOctopus๊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Octopus1633/article/details/123476310

今天的文章STM32 HAL库开发_STM32 HAL库开发[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/70141.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注