STM32标准库版本

STM32标准库版本

一. 基础操作

1. 新建工程文件夹

在这里插入图片描述

文件夹名称作用
Drivers存放与硬件相关的驱动层文件
Middlewares存放中间层文件
Output存放工程编译输出文件
Projects存放 MDK 工程文件
User存放 HAL 库用户配置文件、main.c、中断处理文件,以及分散加载文件

1.1 Drivers文件

其中Drivers文件下又包含:

文件夹名称作用
BSP存放开发板板级支持包驱动代码,如各种外设驱动
CMSIS存放 CMSIS 底层代码,如启动文件(.s 文件)等
SYSTEM存放系统级核心驱动代码,如 sys.c、delay.c 和 usart.c 等
STM32F1xx_HAL_Driver存放 ST 提供的 F1 系列 HAL 库驱动源码

在这里插入图片描述
该部分的操作可以查看良许老师的文档!!!良许老师

二 . 系统时钟配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "sys.h"

void stm32_clock_init(uint32_t plln)
{
    HAL_StatusTypeDef ret= HAL_ERROR;
    RCC_OscInitTypeDef rcc_osc_init = {0};
    RCC_ClkInitTypeDef rcc_clk_init = {0};
    
    rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    rcc_osc_init.HSEState = RCC_HSE_ON;
    rcc_osc_init.HSEPredivValue= RCC_HSE_PREDIV_DIV1; //预分频系数
    rcc_osc_init.PLL.PLLState = RCC_PLL_ON;
    rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    rcc_osc_init.PLL.PLLMUL = plln;
    
    ret = HAL_RCC_OscConfig(&rcc_osc_init);
    
    if(ret != HAL_OK){
        while(1);
    }
    
    rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
    rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1;
    rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2;
    rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1;
    
    ret = HAL_RCC_ClockConfig(&rcc_clk_init, FLASH_LATENCY_2);
    if(ret != HAL_OK){
        while(1);
    }
}

三. GPIO工作模式

在这里插入图片描述

3.1 点亮一个LED灯

在Drivers/BSP新建LED文件夹并放置Led.c和Led.h

########################################## Led.h #########################################
#ifndef __LED_H__
#define __LED_H__

void Led_Init(void);
void Led_On(void);
void Led_Off(void);
void Led_Toggle(void);

#endif


########################################## Led.c #########################################
    #include "Led.h"
    #include "sys.h"


    //初始化GPIO函数
    void Led_Init(void)
    {
        /*
        typedef struct
        {
          uint32_t Pin;       < Specifies the GPIO pins to be configured.
                                   This parameter can be any value of @ref GPIO_pins_define 
          uint32_t Mode;      < Specifies the operating mode for the selected pins.
                                   This parameter can be a value of @ref GPIO_mode_define 
          uint32_t Pull;      < Specifies the Pull-up or Pull-Down activation for the selected pins.
                                   This parameter can be a value of @ref GPIO_pull_define 
          uint32_t Speed;     < Specifies the speed for the selected pins.
                                   This parameter can be a value of @ref GPIO_speed_define 
        } GPIO_InitTypeDef;
        */
        
        GPIO_InitTypeDef Gpio_Initstruct;
        Gpio_Initstruct.Pin = GPIO_PIN_8;
        Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出模式
        Gpio_Initstruct.Pull = GPIO_PULLDOWN;//上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速模式
        
        //打开时钟
        __HAL_RCC_GPIOB_CLK_ENABLE();
        //调用GPIO初始化函数
        //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
        HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
        //关闭LED
        Led_Off();
    }

    //点亮LED函数
    void Led_On(void)
    {
        //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
    }

    //熄灭LED函数
    void Led_Off(void)
    {
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
    }

    //翻转LED状态函数
    void Led_Toggle(void)
    {
        //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
    }


########################################## main.c #########################################
#include "sys.h"
#include "delay.h"
#include "Led.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
//    Led_On();
//    Led_Off();
    
    while(1)
    {
        Led_Toggle();
        HAL_Delay(500);
    }
}

3.2 流水灯

################################# Led.h ######################################
#ifndef __LED_H__
#define __LED_H__

void Led_Init(void);
void Led1_On(void);
void Led1_Off(void);
void Led1_Toggle(void);

void Led2_On(void);
void Led2_Off(void);
void Led2_Toggle(void);

#endif

################################# Led.c ######################################
#include "Led.h"
#include "sys.h"

//初始化GPIO函数
void Led_Init(void)
{
    /*
    typedef struct
    {
      uint32_t Pin;       < Specifies the GPIO pins to be configured.
                               This parameter can be any value of @ref GPIO_pins_define 
      uint32_t Mode;      < Specifies the operating mode for the selected pins.
                               This parameter can be a value of @ref GPIO_mode_define 
      uint32_t Pull;      < Specifies the Pull-up or Pull-Down activation for the selected pins.
                               This parameter can be a value of @ref GPIO_pull_define 
      uint32_t Speed;     < Specifies the speed for the selected pins.
                               This parameter can be a value of @ref GPIO_speed_define 
    } GPIO_InitTypeDef;
    */
    GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
    Gpio_Initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出模式
    Gpio_Initstruct.Pull = GPIO_PULLDOWN;           //上拉输出(默认高电平)
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();                   //使能GPIOB时钟
    //调用GPIO初始化函数
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
    //关闭LED
    Led1_Off();
    Led2_Off();
}

//点亮LED1函数
void Led1_On(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}

//熄灭LED1函数
void Led1_Off(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}

//翻转LED1状态函数
void Led1_Toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}

//点亮LED2函数
void Led2_On(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}

//熄灭LED2函数
void Led2_Off(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}

//翻转LED2状态函数
void Led2_Toggle(void)
{
    //void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}

#################################### main.c ###########################################
#include "sys.h"
#include "delay.h"
#include "Led.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    
    while(1)
    {
        Led1_On();
        Led2_Off();
        delay_ms(500);
        Led1_Off();
        Led2_On();
        delay_ms(500);
    }
}

3.3 蜂鸣器

在这里插入图片描述

#################################### beep.h ##########################
#ifndef __BEEP_H__
#define __BEEP_H__

void Beep_Init(void);
void Beep_On(void);
void Beep_Off(void);

#endif

############################## beep.c #####################################
#include "beep.h"
#include "sys.h"


//初始化GPIO函数
void Beep_Init(void)
{
    /*
    typedef struct
    {
      uint32_t Pin;       < Specifies the GPIO pins to be configured.
                               This parameter can be any value of @ref GPIO_pins_define 
      uint32_t Mode;      < Specifies the operating mode for the selected pins.
                               This parameter can be a value of @ref GPIO_mode_define 
      uint32_t Pull;      < Specifies the Pull-up or Pull-Down activation for the selected pins.
                               This parameter can be a value of @ref GPIO_pull_define 
      uint32_t Speed;     < Specifies the speed for the selected pins.
                               This parameter can be a value of @ref GPIO_speed_define 
    } GPIO_InitTypeDef;
    */
    
    GPIO_InitTypeDef Gpio_Initstruct;
    Gpio_Initstruct.Pin = GPIO_PIN_8;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出模式
    Gpio_Initstruct.Pull = GPIO_PULLDOWN;//上拉输出(默认高电平)
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速模式
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    //调用GPIO初始化函数
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
    //关闭蜂鸣器
    Beep_Off();
}

//蜂鸣器响
void Beep_On(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}

//蜂鸣器不响
void Beep_Off(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}

#################################### main.c #####################################
#include "sys.h"
#include "delay.h"
#include "beep.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Beep_Init();
    
    while(1)
    {
        Beep_On();
        delay_ms(500);
        Beep_Off();
        delay_ms(500);
    }
}

3.4 按键控制小灯

################################## key.h ##################################
#ifndef __KEY_H__
#define __KEY_H__

#include "sys.h"

void Key_Init(void);
uint8_t Key_Scan(void);

#endif

############################### key.c #######################################
#include "key.h"
#include "delay.h"

//初始化GPIO
void Key_Init(void)
{
    GPIO_InitTypeDef gpio_initsturct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟
    
    gpio_initsturct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    gpio_initsturct.Mode = GPIO_MODE_INPUT;         //输入模式
    gpio_initsturct.Pull = GPIO_PULLUP;             //上拉(默认高电平)
    gpio_initsturct.Speed = GPIO_SPEED_FREQ_HIGH;
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOA,&gpio_initsturct);
}

//按键扫描函数
uint8_t Key_Scan(void)
{
    //检测按键是否被按下
    //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
    {
        //消抖
        delay_ms(10);
        //再判断按键是否被按下
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            //若确实按下状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
            //返回按键值
            return 1;
        }
    }
    
    //检测按键是否被按下
    //GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
    {
        //消抖
        delay_ms(10);
        //再判断按键是否被按下
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
        {
            //若确实按下状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
            //返回按键值
            return 2;
        }
    }
    //返回默认值
    return 0;
}

##################################### main.c ###################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "key.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    Key_Init();
    
    uint8_t key_num = 0;
    
    while(1)
    {
        key_num = Key_Scan();
        if(key_num == 1)
            Led1_Toggle();
        
        if(key_num == 2)
            Led2_Toggle();
    }
}

四. 中断

在这里插入图片描述
在这里插入图片描述

4.1 NVIC(嵌套向量中断控制器)

在这里插入图片描述
NVIC可以管理多个中断请求,并按优先级处理它们。在STM32中,中断优先级被划分为抢占式优先级响应优先级,可以根据具体的应用需求进行配置。不同的优先级分组方式会影响中断的响应和处理顺序。

  • 抢占优先级
    如果一个中断的抢占优先级高于当前正在执行的中断,那么它可以打断当前中断,优先得到执行。数值越小,优先级越高。
  • 响应优先级
    如果两个中断同时到达,且它们的==抢占优先级相同,那么响应优先级高的中断将首先得到响应。==数值越小,优先级越高。
  • 自然优先级
    自然优先级是由硬件固定并预先设定的,用户无法更改。当抢占优先级和响应优先级都相同时,自然优先级将决定哪个中断先得到处理。
  • 优先级执行顺序
    当多个中断同时发生时,执行顺序首先由抢占优先级决定。如果抢占优先级相同,则进一步由响应优先级决定。如果响应优先级也相同,则最终由自然优先级决定。
    在中断嵌套的情况下,高抢占优先级的中断可以打断低抢占优先级的中断,但高响应优先级的中断不能打断低响应优先级的中断(当它们具有相同的抢占优先级时)。

4.1.1 NVIC寄存器

在这里插入图片描述

4.2 EXTI

EXTI 是 External Interrupt 的缩写,表示外部中断事件控制器。EXTI 可以监测指定 GPIO 口的电平信号变化,并在检测到指定条件时,向内核的中断控制器 NVIC 发出中断申请。NVIC 在裁决后,如果满足条件,会中断CPU的主程序,使 CPU 转而执行 EXTI 对应的中断服务序。

4.2.1 中断事件

中断会打断CPU当前正在执行的程序,转而去执行中断服务程序,待中断服务程序执行完毕后,CPU会返回到原来的程序执行点继续执行。
事件只是简单地表示某个动作或状态的变化,而不会打断CPU当前正在执行的程序。当事件发生时它会根据配置来决定是否触发相应的中断。如果开放了对应的中断屏蔽位,事件就可以触发相应的中断,否则事件只会作为一个信号存在,不会被CPU处理。
在这里插入图片描述
在这里插入图片描述

4.3 AFIO

AFIO 是 Alternate Function Input/Output 的缩写,表示复用功能 IO,主要用于实现 I/O 端口的复用功能以及外部中断的控制。
在这里插入图片描述

4.4 EXTI配置流程

在这里插入图片描述

4.5 实操-按键点灯(中断法)

使用中断的方法,按下 KEY1 翻转 LED1 状态,而 LED2 一直保持 500ms 的频率闪烁。

############################## exti.h ###################################
#ifndef __EXTI_H__
#define __EXTI_H__

void exti_init(void);

#endif

############################ exti.c #######################################
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "Led.h"

void exti_init(void)
{
    GPIO_InitTypeDef gpio_initsturct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟
    
    gpio_initsturct.Pin = GPIO_PIN_0;
    gpio_initsturct.Mode = GPIO_MODE_IT_FALLING;    //输入模式(中断下降沿))
    gpio_initsturct.Pull = GPIO_PULLUP;             //上拉(默认高电平)
    gpio_initsturct.Speed = GPIO_SPEED_FREQ_HIGH;
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOA,&gpio_initsturct);
    
    //设置优先级
    //void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
    HAL_NVIC_SetPriority(EXTI0_IRQn , 2 , 0);  //一定是EXTI0 因为使用PA0口  若使用PA4口则使用EXTI4
    //使能中断
    //void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
//中断处理函数
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);//该函数会自动调用下面的函数
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_0)
    {
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            Led1_Toggle();
        }
    }
}

############################# main.c #######################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    exti_init();
    
    while(1)
    {
        Led2_Off();
        delay_ms(500);
        Led2_On();
        delay_ms(500);
    }
}

4.6 振动传感器

########################## exit.h ####################################
#ifndef __EXTI_H__
#define __EXTI_H__

#include "stdint.h" 

#define TRUE   1
#define FALSE  0

void exti_init(void);
uint8_t vibrate_flag_get(void);
void vibrate_flag_set(uint8_t value);

#endif

################################## exti.c ###################################
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "Led.h"

uint8_t vibrate_flag = FALSE;

void exti_init(void)
{
    GPIO_InitTypeDef gpio_initsturct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟
    
    gpio_initsturct.Pin = GPIO_PIN_4;
    gpio_initsturct.Mode = GPIO_MODE_IT_FALLING;    //输入模式(中断下降沿触发))
    gpio_initsturct.Pull = GPIO_PULLUP;             //上拉(默认高电平)
    gpio_initsturct.Speed = GPIO_SPEED_FREQ_HIGH;
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOA,&gpio_initsturct);
    
    //void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
    HAL_NVIC_SetPriority(EXTI4_IRQn , 2 , 0);       //设置EXTI0中断线的优先级
    //void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}

void EXTI4_IRQHandler(void)
{
	//公共中断处理函数
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_4)
    {
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4) == GPIO_PIN_RESET)
        {
            //Led1_Toggle();
            vibrate_flag = TRUE;
        }
    }
}

uint8_t vibrate_flag_get(void)
{
	//设置临时变量,承接该变量
    uint8_t temp = vibrate_flag;
    vibrate_flag = FALSE;
    return temp;
    
}

void vibrate_flag_set(uint8_t value)
{
    vibrate_flag = value;
}

######################################## main.c ######################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    exti_init();
    
    while(1)
    {
        if(vibrate_flag_get() == TRUE)
        {
            Led1_On();
            delay_ms(2000);
            Led1_Off();
            /*因为震动传感器在传输信息时,会传入好几个波形,即使exit.c中的vibrate_flag_get
            让其标志位为FLASE,但是只是一瞬间,当下一个波形来时还会触发*/
            vibrate_flag_set(FALSE);
        }
    }
}

4.7 继电器

################################ alarm.h ################################
#ifndef __ALARM_H__
#define __ALARM_H__

#include "stdint.h"

#define ALARM_STATUS_ON   0
#define ALARM_STATUS_OFF  1

void alarm_init(void);
void alarm_on(void);
void alarm_off(void);
uint8_t alarm_status_get(void);

#endif

################################ alarm.c #################################
#include "alarm.h"
#include "sys.h"


//初始化GPIO函数
void alarm_init(void)
{
    /*
    typedef struct
    {
      uint32_t Pin;       < Specifies the GPIO pins to be configured.
                               This parameter can be any value of @ref GPIO_pins_define 
      uint32_t Mode;      < Specifies the operating mode for the selected pins.
                               This parameter can be a value of @ref GPIO_mode_define 
      uint32_t Pull;      < Specifies the Pull-up or Pull-Down activation for the selected pins.
                               This parameter can be a value of @ref GPIO_pull_define 
      uint32_t Speed;     < Specifies the speed for the selected pins.
                               This parameter can be a value of @ref GPIO_speed_define 
    } GPIO_InitTypeDef;
    */
    
    GPIO_InitTypeDef Gpio_Initstruct;
    Gpio_Initstruct.Pin = GPIO_PIN_7;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出模式
    Gpio_Initstruct.Pull = GPIO_PULLDOWN;//上拉输出(默认高电平)
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;//高速模式
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    //调用GPIO初始化函数
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
    //关闭LED
    alarm_off();
}


void alarm_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
}

//熄灭LED函数
void alarm_off(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
}

uint8_t alarm_status_get(void)
{
    return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}

############################### main.c ######################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "alarm.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    alarm_init();
    
    
    while(1)
    {
        alarm_on();
        delay_ms(500);
        alarm_off();
        delay_ms(500);
    }
}

4.8 433M无线收发模块

################################## exti.h ##################################
#ifndef __EXTI_H__
#define __EXTI_H__

#include "stdint.h"

#define TRUE  1
#define FLASE 0

void exti_init(void);
uint8_t buttonA_flag_get(void);
void buttonA_flag_set(uint8_t value);
uint8_t buttonB_flag_get(void);
void buttonB_flag_set(uint8_t value);

#endif

################################# exti.c #########################################3
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "Led.h"

uint8_t buttonA_flag = FLASE;
uint8_t buttonB_flag = FLASE;

void exti_init(void)
{
    GPIO_InitTypeDef gpio_initsturct;
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();                   //使能GPIOB时钟
    
    //调用GPIO初始化函数
    gpio_initsturct.Pin = GPIO_PIN_12;
    gpio_initsturct.Mode = GPIO_MODE_IT_RISING;    //输入模式(中断上升沿))
    gpio_initsturct.Pull = GPIO_PULLDOWN;             //下拉(默认低电平)
    gpio_initsturct.Speed = GPIO_SPEED_FREQ_HIGH;
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOA,&gpio_initsturct);
    
    //设置中断优先级
    //void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
    HAL_NVIC_SetPriority(EXTI15_10_IRQn , 2 , 0);
    //使能中断
    //void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
    
    //调用GPIO初始化函数
    gpio_initsturct.Pin = GPIO_PIN_5;
    gpio_initsturct.Mode = GPIO_MODE_IT_RISING;    //输入模式(中断上升沿))
    gpio_initsturct.Pull = GPIO_PULLDOWN;             //下拉(默认低电平)
    gpio_initsturct.Speed = GPIO_SPEED_FREQ_HIGH;
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOB,&gpio_initsturct);
    
    //设置中断优先级
    //void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
    HAL_NVIC_SetPriority(EXTI9_5_IRQn , 2 , 0);
    //使能中断
    //void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}

void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}

void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_12)
    {
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == GPIO_PIN_SET)
            buttonB_flag = TRUE;
    }else if (GPIO_Pin == GPIO_PIN_5)
    {
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_SET)
            buttonA_flag = TRUE;
    }
}

uint8_t buttonA_flag_get(void)
{
    uint8_t temp = buttonA_flag;
    buttonA_flag = FLASE;
    return temp;
}

void buttonA_flag_set(uint8_t value)
{
    buttonA_flag = value;
}

uint8_t buttonB_flag_get(void)
{
    uint8_t temp = buttonB_flag;
    buttonB_flag = FLASE;
    return temp;
}

void buttonB_flag_set(uint8_t value)
{
    buttonB_flag = value;
}

################################################ main.c ##########################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    exti_init();
    
    while(1)
    {
        if(buttonA_flag_get() == TRUE)
            Led1_Toggle();
        if(buttonB_flag_get() == TRUE)
            Led2_Toggle();
    }
}

4.9 电动车报警器

在这里插入图片描述

五. SysTick(滴答定时器)

Systick,即滴答定时器,是内核中的一个特殊定时器,用于提供系统级的定时服务。该定时器是一个24位的递减计数器,具有自动重载值寄存器的功能。当计数器到达自动重载值时,它会自动重新加载并开始新的计数周期。

5.1 工作原理

在使用Systick定时器进行延时操作时,可以设定初值并使能后,每经过一个系统时钟周期,计数值就减1。当计数到0时,Systick计数器自动重装初值并继续计数,同时内部的COUNTFLAG标志会置位,触发中断(如果中断使能)。这样,可以在中断处理函数中实现特定的延时逻辑。
在这里插入图片描述
72MHZ 相当于 1s数了72M下 ≈ 1us 数了72下
=-----------------------
在这里插入图片描述
在这里插入图片描述

void delay_us(uint32_t nus)
{
    uint32_t temp = 0;
    
    //1. 确定要数多少个数
    SysTick->LOAD =  72 * nus;                      // 设置定时器重装值
    //2. 清空当前计数
    SysTick->VAL = 0x00;                            // 清空当前计数值
    //3. 配置分频系数
    SysTick->CTRL |= 1 << 2;                        // 设置分频系数为1分频
    //4. 开启定时器  
    SysTick->CTRL |= 1 << 0;                        // 启动定时器
    do
    {
        temp = SysTick->CTRL;
    }while((temp & 0x01) && (!(temp & (1 << 16)))); // 等待计数到0
    SysTick->CTRL &= ~(1 << 0);                     // 关闭定时器
}

在这里插入图片描述
在这里插入图片描述

5.2 带操作系统延时函数

void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t tcnt = 0 , told , tnow;
    uint32_t reload = SysTick->LOAD;
    
    ticks = nus * 72;
    told = SysTick->VAL;
    
    while(1)
    {
        tnow = SysTick->VAL;
        if(tnow != told)
        {
            if(tnow < told)
                tcnt += told - tnow;
            else
                tcnt += reload - (tnow - told);
            
            told = tnow;
            
            if(tcnt >= ticks)
                break;
        }
    }
}

5.3 SysTick模拟多线程

在这里插入图片描述
系统已经规定,1ms进入进入一次中断,即HAL_InitTick函数中
在这里插入图片描述
每1ms进入该函数中!!!

########################## stm32f1xx.it.c #############################
void SysTick_Handler(void)
{
    HAL_IncTick();
    
    systick_isr();
}

############################# tasks.c ############################
#include "tasks.h"
#include "Led.h"
#include "stdint.h"

uint32_t task1_cnt = 0;
uint32_t task2_cnt = 0;

uint8_t task1_flag = 0;
uint8_t task2_flag = 0;

void systick_isr(void)
{
    if(task1_cnt < 1000)  //相当于1s翻转一次
        task1_cnt++;
    else
    {
        task1_flag = 1;
        task1_cnt = 0;
    }
    
    if(task2_cnt < 500)   //相当于0.5s翻转一次
        task2_cnt++;
    else
    {
        task2_flag = 1;
        task2_cnt = 0;
    }
}

void task1(void)
{
    if(task1_flag == 0)
        return ;
    
    task1_flag = 0;
    
    Led1_Toggle();
}

void task2(void)
{
    if(task2_flag == 0)
        return ;
    
    task2_flag = 0;
    
    Led2_Toggle();
}

#################################### tasks.h ########################
#ifndef __TASHS_H__
#define __TASHS_H__

#include "sys.h"

void systick_isr(void);
void task1(void);
void task2(void);

#endif

################################ main.c ############################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "tasks.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    
    while(1)
    {
        task1();
        task2();
    }
}

相当于,while(1)中每次都进入task1和task2中,查看cnt值是否达到要求,但是cnt内的值是1ms增加一次(SysTick)

六. 模块

6.1 红外模块

在这里插入图片描述
有障碍物,返回,OUT产生低电平

################################## exit.h ######################################
#ifndef __EXTI_H__
#define __EXTI_H__

#include "stdint.h" 

#define TRUE   1
#define FALSE  0

void exti_init(void);
uint8_t ia_flag_get(void);
void ia_flag_set(uint8_t value);

#endif

################################ exit.c ####################################
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "Led.h"

uint8_t ia_flag = FALSE;

void exti_init(void)
{
    GPIO_InitTypeDef gpio_initsturct;
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();                   //使能GPIOA时钟
    
    gpio_initsturct.Pin = GPIO_PIN_4;
    gpio_initsturct.Mode = GPIO_MODE_IT_FALLING;    //输入模式(中断下降沿触发))
    gpio_initsturct.Pull = GPIO_PULLUP;             //上拉(默认高电平)
    gpio_initsturct.Speed = GPIO_SPEED_FREQ_HIGH;
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOB,&gpio_initsturct);
    
    //void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
    HAL_NVIC_SetPriority(EXTI4_IRQn , 2 , 0);       //设置EXTI0中断线的优先级
    //void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}

void EXTI4_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_4)
    {
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4) == GPIO_PIN_RESET)
        {
            //Led1_Toggle();
            ia_flag = TRUE;
        }
    }
}

uint8_t ia_flag_get(void)
{
    uint8_t temp = ia_flag;
    ia_flag = FALSE;
    return temp;
    
}

void ia_flag_set(uint8_t value)
{
    ia_flag = value;
}

######################################### main.c ###############################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    exti_init();
    
    while(1)
    {
        if(ia_flag_get() == TRUE)
        {
            Led1_On();
            delay_ms(2000);
            Led1_Off();
            ia_flag_set(FALSE);
        }
    }
}

6.2 LCD1602显示器

LCD1602
良许老师

6.2.1 显示一个字符

在这里插入图片描述
在这里插入图片描述

################################ lcd1602.h ################################
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include "lcd1602.h"
#include "sys.h"

void lcd1602_init(void);
void lcd1602_gpio_init(void);
void lcd1602_start(void);
void lcd1602_write_cmd(char cmd);
void lcd1602_write_data(char dataShow);
void lcd1602_show_char(void);

#endif

################################ lcd1602.c ################################
#include "lcd1602.h"
#include "delay.h"


//RS引脚定义
#define RS_GPIO_Port    GPIOB
#define RS_GPIO_Pin     GPIO_PIN_1
#define RS_HIGH         HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET);
#define RS_LOW          HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET);

//RW引脚定义
#define RW_GPIO_Port    GPIOB
#define RW_GPIO_Pin     GPIO_PIN_2
#define RW_HIGH         HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET);
#define RW_LOW          HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET);

//EN引脚定义
#define EN_GPIO_Port    GPIOB
#define EN_GPIO_Pin     GPIO_PIN_10
#define EN_HIGH         HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET);
#define EN_LOW          HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET);


void lcd1602_gpio_init(void);
void lcd1602_start(void);

void lcd1602_init(void)
{
    //初始化GPIO
    lcd1602_gpio_init();
    //开机初始化
    lcd1602_start();
}

void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
    //打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    Gpio_Initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
                          GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 ;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出模式
    Gpio_Initstruct.Pull = GPIO_PULLUP;           //上拉输出(默认高电平)
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
    HAL_GPIO_Init(GPIOA,&Gpio_Initstruct);
    
    Gpio_Initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10 ;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出模式
    Gpio_Initstruct.Pull = GPIO_PULLUP;           //上拉输出(默认高电平)
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
    HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
}

void lcd1602_start(void)
{
//    (1)延时 15ms
    delay_ms(15);
//    (2)写指令 38H(不检测忙信号)
    lcd1602_write_cmd(0x38);
//    (3)延时 5ms
    delay_ms(5);
//    (4)以后每次写指令,读/写数据操作均需要检测忙信号
    
//    (5)写指令 38H:显示模式设置
    lcd1602_write_cmd(0x38);
//    (6)写指令 08H:显示关闭
    lcd1602_write_cmd(0x08);
//    (7)写指令 01H:显示清屏
    lcd1602_write_cmd(0x01);
//    (8)写指令 06H:显示光标移动设置
    lcd1602_write_cmd(0x06);
//    (9)写指令 0CH:显示开及光标设置
    lcd1602_write_cmd(0x0C);
}

void lcd1602_write_cmd(char cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;//寄存器映射
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}

void lcd1602_write_data(char dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;//寄存器映射
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}

void lcd1602_show_char()
{
    //在哪显示
    lcd1602_write_cmd(0x80+0x02);
    //显示什么
    lcd1602_write_data('Q');
}

################################ main.c ################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "lcd1602.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    lcd1602_init();
    lcd1602_show_char();
    
    while(1)
    {
        
    }
}

6.2.2 显示字符串

void lcd1602_show_line(char row,char col,char *string)
{
    switch(row)
    {
        case 1:
            lcd1602_write_cmd(0x80 + col);
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
        case 2:
            lcd1602_write_cmd(0x80 + 0x40 + col);
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
    }
}



#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "lcd1602.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    lcd1602_init();
    lcd1602_show_char();
    lcd1602_show_line(1,2,"STM32");
    lcd1602_show_line(2,1,"Qiang");
    
    while(1)
    {
        
    }
}

七. 定时器

7.1 定时器介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.2 定时器中断

在这里插入图片描述

在这里插入图片描述
msp 函数是对 MCU 相关的硬件进行初始化设置,通常被设计用于处理特定硬件外设或功能的底层初始化工作。

7.2.1 定时器中断点灯

使用定时器 2 进行中断点灯,500ms LED 灯翻转一次。

################################## timer.h ##################################
#ifndef __TIMER_H__
#define __TIMER_H__

#include "sys.h"

void timer_init(uint16_t arr , uint16_t psc);

#endif

################################## timer.c ##################################
#include "timer.h"
#include "Led.h"

TIM_HandleTypeDef timer_handle = {0};

//定时器初始化函数
void timer_init(uint16_t arr , uint16_t psc)
{
    /*
    TIM_TypeDef           *Instance;         < Register base address
    TIM_Base_InitTypeDef  Init;              < TIM Time Base required parameters  
    */
    /*
    typedef struct
{
    uint32_t Prescaler(PSC);         < Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF 

    uint32_t CounterMode(三种计数模式);       < Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode 

    uint32_t Period(ARR);            < Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  

    uint32_t ClockDivision;     < Specifies the clock division.
                                       This parameter can be a value of @ref TIM_ClockDivision 

    uint32_t RepetitionCounter;  < Specifies the repetition counter value. Each time the RCR downcounter
                                        reaches zero, an update event is generated and counting restarts
                                        from the RCR value (N).
                                        This means in PWM mode that (N+1) corresponds to:
                                            - the number of PWM periods in edge-aligned mode
                                            - the number of half PWM period in center-aligned mode
                                         GP timers: this parameter must be a number between Min_Data = 0x00 and
                                         Max_Data = 0xFF.
                                         Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
                                         Max_Data = 0xFFFF. 

    uint32_t AutoReloadPreload(自动重装载);  < Specifies the auto-reload preload.
                                       This parameter can be a value of @ref TIM_AutoReloadPreload 
    }TIM_Base_InitTypeDef;
    */
    timer_handle.Instance = TIM2;                                         //选择定时器2
    timer_handle.Init.Prescaler = psc;                                    //PSC
    timer_handle.Init.Period = arr;                                       //ARR
    timer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                   //计数模式(递增计数模式)
    timer_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //关自动重装载
    //时基工作参数配置
    //HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
    HAL_TIM_Base_Init(&timer_handle);
    //使能更新中断和启动计数器
    //HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
    HAL_TIM_Base_Start_IT(&timer_handle);
}

//msp函数(各种定时器中断都会调用这个函数)
//上面的HAL_TIM_Base_Init会自动调用该函数
//__weak void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();                //
        //设置优先级
        //void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority(抢占优先级), uint32_t SubPriority(响应优先级))
        HAL_NVIC_SetPriority(EXTI2_IRQn , 2 , 2);
        //使能中断(相当于开中断)
        //void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
        HAL_NVIC_EnableIRQ(EXTI2_IRQn);
        
    }
}

//中断服务函数
void TIM2_IRQHandler(void)
{
    //void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
    HAL_TIM_IRQHandler(&timer_handle);
}


//更新中断回调函数
//__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        Led1_Toggle();
    }
}

################################## main.c ##################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "timer.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    timer_init(5000 - 1 , 7200 - 1);
    
    while(1)
    {
        
    }
}

7.3 输出比较原理

输出比较可以通过比较定时计数器的值 CNT 与设定的比较值 CCR,可以控制输出引脚的电平状态(置高或置低),从而实现生成一定频率和占空比的 PWM 波形
在这里插入图片描述
在这里插入图片描述

7.4 PWM

PWM波形(Pulse Width Modulation,脉冲宽度调制波形)是一种占空比可变的脉冲波形。这种调制方式通过改变脉冲的宽度来控制电路中的信号强度和频率。具体来说,PWM波形中的高电平持续时间和低电平持续时间可以根据需要进行调整,从而实现对模拟信号电平的数字编码。

频率 = 1/Ts
占空比 = Ton / Ts
分辨率 = 占空比变化步距
在这里插入图片描述

7.4.1 定时器输出PWM波配置步骤

在这里插入图片描述

7.4.2 呼吸灯实验

使用定时器 4 通道 3 生成 PWM 波控制 LED1 ,实现呼吸灯效果。
在这里插入图片描述

######################################### pwm.h ############################################
#ifndef __PWM_H__
#define __PWM_H__

#include "sys.h"

void pwm_init(uint16_t arr , uint16_t psc);
void pwm_compare_set(uint16_t val);

#endif
######################################### pwm.c ############################################
#include "pwm.h"

TIM_HandleTypeDef pwm_handler = {0};

//init函数
void pwm_init(uint16_t arr , uint16_t psc)
{
    TIM_OC_InitTypeDef pwm_config = {0};
    /*
   {
    TIM_TypeDef            *Instance;         < Register base address                             
    TIM_Base_InitTypeDef   Init;              < TIM Time Base required parameters               
    HAL_TIM_ActiveChannel  Channel;           < Active channel                                 
    DMA_HandleTypeDef      *hdma[7];          < DMA Handlers array
                                              This array is accessed by a @ref DMA_Handle_index 
    */
    /*
    typedef struct
    {
      uint32_t Prescaler;         < Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF 

      uint32_t CounterMode;       < Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode 

      uint32_t Period;            < Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. 

      uint32_t ClockDivision;     < Specifies the clock division.
                                       This parameter can be a value of @ref TIM_ClockDivision 

      uint32_t RepetitionCounter;  < Specifies the repetition counter value. Each time the RCR downcounter
                                        reaches zero, an update event is generated and counting restarts
                                        from the RCR value (N).
                                        This means in PWM mode that (N+1) corresponds to:
                                            - the number of PWM periods in edge-aligned mode
                                            - the number of half PWM period in center-aligned mode
                                         GP timers: this parameter must be a number between Min_Data = 0x00 and
                                         Max_Data = 0xFF.
                                         Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
                                         Max_Data = 0xFFFF. 

      uint32_t AutoReloadPreload;  < Specifies the auto-reload preload.
                                       This parameter can be a value of @ref TIM_AutoReloadPreload 
    } TIM_Base_InitTypeDef;
    */
    pwm_handler.Instance = TIM4;                            //定时器4
    pwm_handler.Init.Prescaler = psc;                       //PSC预分频系数
    pwm_handler.Init.Period = arr;                          //ARR计数大小
    pwm_handler.Init.CounterMode = TIM_COUNTERMODE_UP;      //向上计数
    //HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
    HAL_TIM_PWM_Init(&pwm_handler);
    
    /*
    typedef struct
{
      uint32_t OCMode;        < Specifies the TIM mode.
                                   This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes 

      uint32_t Pulse;         < Specifies the pulse value to be loaded into the Capture Compare Register.
                                   This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF 

      uint32_t OCPolarity;    < Specifies the output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_Polarity 

      uint32_t OCNPolarity;   < Specifies the complementary output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                                   @note This parameter is valid only for timer instances supporting break feature. 

      uint32_t OCFastMode;    < Specifies the Fast mode state.
                                   This parameter can be a value of @ref TIM_Output_Fast_State
                                   @note This parameter is valid only in PWM1 and PWM2 mode. 


      uint32_t OCIdleState;   < Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                                   @note This parameter is valid only for timer instances supporting break feature. 

      uint32_t OCNIdleState;   < Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                                   @note This parameter is valid only for timer instances supporting break feature. 
    } TIM_OC_InitTypeDef;
    */
    pwm_config.OCMode = TIM_OCMODE_PWM1;            //PWM模式1
    pwm_config.Pulse = arr/2;                       //CCR(仅为初值,不修改也可以)
    pwm_config.OCPolarity = TIM_OCPOLARITY_LOW;     //因为点灯,低电平点亮,所以设置有效电平为低电平
    /*
    HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
                                            const TIM_OC_InitTypeDef *sConfig,
                                            uint32_t Channel)
    */
    //PWM模式,CCR寄存器设置
    HAL_TIM_PWM_ConfigChannel(&pwm_handler , &pwm_config , TIM_CHANNEL_3); 
    //使能输出并启动定时器
    //HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
    HAL_TIM_PWM_Start(&pwm_handler,TIM_CHANNEL_3);
    
}

//msp函数(各种定时器中断都会调用这个函数)
//上面的HAL_TIM_PWM_Init会自动调用该函数
//__weak void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM4)
    {
        //GPIO口初始化
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        Gpio_Initstruct.Pin = GPIO_PIN_8;
        Gpio_Initstruct.Mode = GPIO_MODE_AF_PP;         //推挽复用输出
        Gpio_Initstruct.Pull = GPIO_PULLUP;             //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        
        //打开时钟
        __HAL_RCC_GPIOB_CLK_ENABLE();                   //使能GPIOB时钟
        __HAL_RCC_TIM4_CLK_ENABLE();                    //开定时器4时钟
        //调用GPIO初始化函数
        //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
        HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
        }
}

//修改CCR值函数
void pwm_compare_set(uint16_t val)
{
    //__HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__)
    __HAL_TIM_SET_COMPARE(&pwm_handler , TIM_CHANNEL_3 , val);
}

######################################### main.c ############################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "pwm.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    pwm_init(500 -1 , 72 - 1);
    
    uint16_t i =0;
    
    while(1)
    {
        //逐渐变亮
        for(i=0 ; i<300 ; i++)
        {
            pwm_compare_set(i);
            delay_ms(10);
        }
        //逐渐变暗
        for(i=0 ; i<300 ; i++)
        {
            pwm_compare_set(300 - i);
            delay_ms(10);
        }
    }
}

7.5 输入捕获原理

在这里插入图片描述

7.5.1 定时器输入捕获实验配置步骤

在这里插入图片描述
在这里插入图片描述

7.5.2 测量按键按下时长

在这里插入图片描述

在这里插入图片描述
使用定时器 2 通道 2 来捕获按键 2 按下时间,并通过串口打印。
计一个数的时间:1us,PSC=71,ARR=65535
计一个数时间(PSC+1)/Tclk
下降沿捕获、输入通道 2 映射在 TI2 上、不分频、不滤波
在这里插入图片描述

####################################### ic.h #######################################
#ifndef __IC_H__
#define __IC_H__

#include "sys.h"

void ic_init(uint16_t arr , uint16_t psc);
void pressed_time_get(void);

#endif

####################################### ic.c #######################################
#include "ic.h"
#include "stdio.h"
#include "string.h"

struct
{
    uint8_t succeed_flag;         //完整捕获
    uint8_t rising_flag;          //捕获上升沿
    uint8_t falling_flag;         //下降沿捕获
    uint16_t timeout_cnt;
}capture_status = {0};

uint16_t last_cnt = 0;

TIM_HandleTypeDef ic_handler = {0};

//时基工作参数配置
void ic_init(uint16_t arr , uint16_t psc)
{
    TIM_IC_InitTypeDef ic_config = {0};
    
    ic_handler.Instance = TIM2;
    ic_handler.Init.Prescaler = psc;                      //PSC
    ic_handler.Init.Period = arr;                         //ARR
    ic_handler.Init.CounterMode = TIM_COUNTERMODE_UP;     //向上计数
    //HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)
    HAL_TIM_IC_Init(&ic_handler);
    
    //配置输入通道
    /*
    typedef struct
    {
      uint32_t  ICPolarity;  < Specifies the active edge of the input signal.
                                  This parameter can be a value of @ref TIM_Input_Capture_Polarity 

      uint32_t ICSelection;  < Specifies the input.
                                  This parameter can be a value of @ref TIM_Input_Capture_Selection 

      uint32_t ICPrescaler;  < Specifies the Input Capture Prescaler.
                                  This parameter can be a value of @ref TIM_Input_Capture_Prescaler 

      uint32_t ICFilter;     < Specifies the input capture filter.
                                  This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF 
    } TIM_IC_InitTypeDef;
    */
    ic_config.ICPolarity = TIM_ICPOLARITY_FALLING;            //极性(捕捉下降沿)
    ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;         //映射
    ic_config.ICPrescaler = TIM_ICPSC_DIV1;                   //分频(选择不分频即1分频)
    ic_config.ICFilter = 0;                                  //滤波(不滤波0)
    //HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, const TIM_IC_InitTypeDef *sConfig, uint32_t Channel)
    HAL_TIM_IC_ConfigChannel(&ic_handler , &ic_config , TIM_CHANNEL_2);
    
    //更新使能中断
    //#define __HAL_TIM_ENABLE_IT(__HANDLE__, __INTERRUPT__)  TIM_IT_UPDATE--更新中断
    __HAL_TIM_ENABLE_IT(&ic_handler , TIM_IT_UPDATE);
    
    //使能捕获、捕获中断及计数器
    //HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
    HAL_TIM_IC_Start_IT(&ic_handler , TIM_CHANNEL_2);
}

//msp初始化
//HAL_TIM_IC_Init自动调用该函数
//__weak void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        //GPIO口初始化
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        Gpio_Initstruct.Pin = GPIO_PIN_1;
        Gpio_Initstruct.Mode = GPIO_MODE_INPUT;         //输入模式
        Gpio_Initstruct.Pull = GPIO_PULLUP;             //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        
        //打开时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟
        __HAL_RCC_TIM2_CLK_ENABLE();                    //开定时器2时钟
        //调用GPIO初始化函数
        //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
        HAL_GPIO_Init(GPIOA,&Gpio_Initstruct);
        //设置优先级
        HAL_NVIC_SetPriority(TIM2_IRQn , 2 , 2);
        //使能中断
        HAL_NVIC_EnableIRQ(TIM2_IRQn);
    }
}

//中断函数
void TIM2_IRQHandler(void)
{
    //公共处理函数
    //void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
    HAL_TIM_IRQHandler(&ic_handler);
}

//__weak void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    //因为捕获到的信号会映射到TIM2上,所以检测TIM2
    if(htim->Instance == TIM2)
    {
        if(capture_status.succeed_flag == 0)
        {
            if(capture_status.falling_flag == 1)
            {
                printf("捕获到上降沿\r\n");
                capture_status.succeed_flag = 1;
                
                //获取最后CCR的值
                //uint32_t HAL_TIM_ReadCapturedValue(const TIM_HandleTypeDef *htim, uint32_t Channel)
                last_cnt = HAL_TIM_ReadCapturedValue(&ic_handler , TIM_CHANNEL_2);
                
                //清除通道设置
                //TIM_RESET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__)
                TIM_RESET_CAPTUREPOLARITY(&ic_handler , TIM_CHANNEL_2);
                //设置下降沿捕获
                //TIM_SET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__, __POLARITY__)
                TIM_SET_CAPTUREPOLARITY(&ic_handler , TIM_CHANNEL_2 , TIM_ICPOLARITY_FALLING);
                //memset(&capture_status , 0 , sizeof(capture_status));
            }
            else
            {
                printf("捕获到下降沿\r\n");
                //第一次启动程序走该处
                memset(&capture_status , 0 , sizeof(capture_status));
                capture_status.falling_flag = 1;
                
                //定时器关闭
                //__HAL_TIM_DISABLE(__HANDLE__)
                __HAL_TIM_DISABLE(&ic_handler);
                //定时器清零
                //__HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)
                __HAL_TIM_SET_COUNTER(&ic_handler , 0);
                
                //清除通道设置
                //TIM_RESET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__)
                TIM_RESET_CAPTUREPOLARITY(&ic_handler , TIM_CHANNEL_2);
                //设置上升沿捕获
                //TIM_SET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__, __POLARITY__)
                TIM_SET_CAPTUREPOLARITY(&ic_handler , TIM_CHANNEL_2 , TIM_ICPOLARITY_RISING);
                //第一次程序执行到此处执行捕获上升沿
                //当松开按键时,仍触发该中断!!!
                
                //启动定时器
                __HAL_TIM_ENABLE(&ic_handler);
            }
        }
    }
}

//溢出中断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        if(capture_status.falling_flag == 1)
        {
            capture_status.timeout_cnt++;
        }
    }
}

void pressed_time_get(void)
{
    if(capture_status.succeed_flag == 1)
    {
        printf("按下时间:%d us\r\n",capture_status.timeout_cnt * 65536 + last_cnt);
        memset(&capture_status , 0 , sizeof(capture_status));
    }
}

####################################### main.c #######################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "ic.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    printf("hello word!\r\n");
    ic_init(65536 - 1 , 72 - 1);
    
    while(1)
    {
        pressed_time_get();
        delay_ms(10);
    }
}

7.6 脉冲计数原理

7.6.1 脉冲计数配置步骤

在这里插入图片描述

7.6.2 脉冲计数实验

将定时器 2 通道 2 输入的低电平脉冲作为定时器 2 的时钟,并通过串口打印脉冲数
PSC=0,ARR=65535
外部时钟模式1、触发选择、上升沿触发、不分频、不滤波
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

###################################### counter.h ######################################
#ifndef __COUNTER_H__
#define __COUNTER_H__

#include "sys.h"

void counter_init(uint16_t arr , uint16_t psc);
void counter_get(void);

#endif

###################################### counter.c ######################################
#include "counter.h"
#include "stdio.h"

TIM_HandleTypeDef counter_handler = {0};

uint16_t new_count = 0;
uint16_t old_count = 0;

void counter_init(uint16_t arr , uint16_t psc)
{
    TIM_SlaveConfigTypeDef slave_config = {0};      //从模式配置器
    
    //时基工作参数配置
    counter_handler.Instance = TIM2;
    counter_handler.Init.Prescaler = psc;
    counter_handler.Init.Period = arr;
    counter_handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    //HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)
    HAL_TIM_IC_Init(&counter_handler);
    
    //配置定时器的从模式(捕获边沿、映射、通道、滤波)
    /*
    typedef struct
    {
      uint32_t  SlaveMode;         < Slave mode selection
                                   This parameter can be a value of @ref TIM_Slave_Mode 
      uint32_t  InputTrigger;      < Input Trigger source
                                   This parameter can be a value of @ref TIM_Trigger_Selection 
      uint32_t  TriggerPolarity;   < Input Trigger polarity
                                   This parameter can be a value of @ref TIM_Trigger_Polarity 
      uint32_t  TriggerPrescaler;  < Input trigger prescaler
                                   This parameter can be a value of @ref TIM_Trigger_Prescaler 
      uint32_t  TriggerFilter;     < Input trigger filter
                                   This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF  
    } TIM_SlaveConfigTypeDef;
    */
    slave_config.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;           //使用外部模式1
    slave_config.InputTrigger = TIM_TS_TI2FP2;                  //输入触发来源
    slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_FALLING; //下降沿触发
    slave_config.TriggerFilter = 0;                             //滤波(不使用0)
    //HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro(TIM_HandleTypeDef *htim, const TIM_SlaveConfigTypeDef *sSlaveConfig)
    HAL_TIM_SlaveConfigSynchro(&counter_handler , &slave_config);
    
    //使能捕获,并启动定时器
    //HAL_StatusTypeDef HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
    HAL_TIM_IC_Start(&counter_handler , TIM_CHANNEL_2);
}

//msp初始化
//__weak void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        Gpio_Initstruct.Pin = GPIO_PIN_1;       
        Gpio_Initstruct.Mode = GPIO_MODE_AF_PP;     //复用输出模式
        Gpio_Initstruct.Pull = GPIO_PULLUP;           //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        
        //打开时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟
        __HAL_RCC_TIM2_CLK_ENABLE();
        //调用GPIO初始化函数
        //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        }
}

//获取cnt值
void counter_get(void)
{
    //获取cnt值
    //__HAL_TIM_GET_COUNTER(__HANDLE__)
    new_count = __HAL_TIM_GET_COUNTER(&counter_handler);
    if(old_count != new_count)
    {
        old_count = new_count;
        printf("CNT: %d\r\n",new_count);
    }
}
###################################### main.c ######################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "counter.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    printf("hello word!\r\n");
    counter_init(65536 - 1 , 0);
    
    while(1)
    {
        counter_get();
    }
}

八. 模块

8.1 sg90舵机

良许老师

###################################### sg90.h ######################################
#ifndef __SG90_H__
#define __SG90_H__

#include "sys.h"

void sg90_init(void);
void sg90_angle_set(uint16_t angle);

#endif

###################################### sg90.c ######################################
#include "sg90.h"

TIM_HandleTypeDef tim3_handler = {0};

//init函数
void tim3_init(void)
{
    TIM_OC_InitTypeDef pwm_config = {0};
    /*
   {
    TIM_TypeDef            *Instance;         < Register base address                             
    TIM_Base_InitTypeDef   Init;              < TIM Time Base required parameters               
    HAL_TIM_ActiveChannel  Channel;           < Active channel                                 
    DMA_HandleTypeDef      *hdma[7];          < DMA Handlers array
                                              This array is accessed by a @ref DMA_Handle_index 
    */
    /*
    typedef struct
    {
      uint32_t Prescaler;         < Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF 

      uint32_t CounterMode;       < Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode 

      uint32_t Period;            < Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. 

      uint32_t ClockDivision;     < Specifies the clock division.
                                       This parameter can be a value of @ref TIM_ClockDivision 

      uint32_t RepetitionCounter;  < Specifies the repetition counter value. Each time the RCR downcounter
                                        reaches zero, an update event is generated and counting restarts
                                        from the RCR value (N).
                                        This means in PWM mode that (N+1) corresponds to:
                                            - the number of PWM periods in edge-aligned mode
                                            - the number of half PWM period in center-aligned mode
                                         GP timers: this parameter must be a number between Min_Data = 0x00 and
                                         Max_Data = 0xFF.
                                         Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
                                         Max_Data = 0xFFFF. 

      uint32_t AutoReloadPreload;  < Specifies the auto-reload preload.
                                       This parameter can be a value of @ref TIM_AutoReloadPreload 
    } TIM_Base_InitTypeDef;
    */
    tim3_handler.Instance = TIM3;                            //定时器4
    tim3_handler.Init.Prescaler = 7200 - 1;                       //PSC预分频系数
    tim3_handler.Init.Period = 200 - 1;                          //ARR计数大小
    tim3_handler.Init.CounterMode = TIM_COUNTERMODE_UP;      //向上计数
    //HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
    HAL_TIM_PWM_Init(&tim3_handler);
    
    /*
    typedef struct
{
      uint32_t OCMode;        < Specifies the TIM mode.
                                   This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes 

      uint32_t Pulse;         < Specifies the pulse value to be loaded into the Capture Compare Register.
                                   This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF 

      uint32_t OCPolarity;    < Specifies the output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_Polarity 

      uint32_t OCNPolarity;   < Specifies the complementary output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                                   @note This parameter is valid only for timer instances supporting break feature. 

      uint32_t OCFastMode;    < Specifies the Fast mode state.
                                   This parameter can be a value of @ref TIM_Output_Fast_State
                                   @note This parameter is valid only in PWM1 and PWM2 mode. 


      uint32_t OCIdleState;   < Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                                   @note This parameter is valid only for timer instances supporting break feature. 

      uint32_t OCNIdleState;   < Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                                   @note This parameter is valid only for timer instances supporting break feature. 
    } TIM_OC_InitTypeDef;
    */
    pwm_config.OCMode = TIM_OCMODE_PWM1;            //PWM模式1
    pwm_config.Pulse = 100;                       //CCR
    pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH;     //设置有效电平为高电平
    
    //PWM模式,CCR寄存器设置
    /*
    HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
                                            const TIM_OC_InitTypeDef *sConfig,
                                            uint32_t Channel)
    */
    HAL_TIM_PWM_ConfigChannel(&tim3_handler , &pwm_config , TIM_CHANNEL_1); 
    
    //使能输出并启动定时器
    //HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
    HAL_TIM_PWM_Start(&tim3_handler,TIM_CHANNEL_1);
    
}

//msp函数(各种定时器中断都会调用这个函数)
//上面的HAL_TIM_PWM_Init会自动调用该函数
//__weak void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        //GPIO口初始化
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        
        Gpio_Initstruct.Pin = GPIO_PIN_6;
        Gpio_Initstruct.Mode = GPIO_MODE_AF_PP;         //推挽复用输出
        Gpio_Initstruct.Pull = GPIO_PULLUP;             //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        
        //打开时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOB时钟
        __HAL_RCC_TIM3_CLK_ENABLE();                    //开定时器4时钟
        //调用GPIO初始化函数
        //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        }
}

//修改CCR值函数
void tim3_compare_set(uint16_t val)
{
    //__HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__)
    __HAL_TIM_SET_COMPARE(&tim3_handler , TIM_CHANNEL_1 , val);
}

void sg90_init(void)
{
    tim3_init();
}

void sg90_angle_set(uint16_t angle)
{
    uint16_t CCRx = (1.0 / 9.0) * angle + 5.0;
    tim3_compare_set(CCRx);
}
###################################### main.c ######################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "sg90.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    sg90_init();
    
    while(1)
    {
        sg90_angle_set(0);
        delay_ms(1000);
        sg90_angle_set(20);
        delay_ms(1000);
        sg90_angle_set(40);
        delay_ms(1000);
        sg90_angle_set(90);
        delay_ms(1000);
        sg90_angle_set(120);
        delay_ms(1000);
        sg90_angle_set(150);
        delay_ms(1000);
        sg90_angle_set(180);
        delay_ms(1000);
    }
}

8.2 超声波传感器

良许老师

############################## hcsr04.h ##############################
#ifndef __HCSR04_H__
#define __HCSR04_H__

#include "sys.h"

#define TRIG_PORT                   GPIOB
#define TRIG_PIN                    GPIO_PIN_6
#define TRIG_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define TRIG_HIGH()                 HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_SET)
#define TRIG_LOW()                  HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_RESET)

#define ECHO_PORT                   GPIOB
#define ECHO_PIN                    GPIO_PIN_7
#define ECHO_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define ECHO_STATUS()               HAL_GPIO_ReadPin(ECHO_PORT,ECHO_PIN)

void hcsr04_init(void);
float hcsr04_get_length(void);

#endif

############################## hcsr04.c ##############################
#include "hcsr04.h"
#include "delay.h"

TIM_HandleTypeDef tim2_handler = {0};

//定时器初始化函数
void tim2_init(void)
{
    /*
    TIM_TypeDef           *Instance;         < Register base address
    TIM_Base_InitTypeDef  Init;              < TIM Time Base required parameters  
    */
    /*
    typedef struct
{
    uint32_t Prescaler(PSC);         < Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF 

    uint32_t CounterMode(三种计数模式);       < Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode 

    uint32_t Period(ARR);            < Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  

    uint32_t ClockDivision;     < Specifies the clock division.
                                       This parameter can be a value of @ref TIM_ClockDivision 

    uint32_t RepetitionCounter;  < Specifies the repetition counter value. Each time the RCR downcounter
                                        reaches zero, an update event is generated and counting restarts
                                        from the RCR value (N).
                                        This means in PWM mode that (N+1) corresponds to:
                                            - the number of PWM periods in edge-aligned mode
                                            - the number of half PWM period in center-aligned mode
                                         GP timers: this parameter must be a number between Min_Data = 0x00 and
                                         Max_Data = 0xFF.
                                         Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
                                         Max_Data = 0xFFFF. 

    uint32_t AutoReloadPreload(自动重装载);  < Specifies the auto-reload preload.
                                       This parameter can be a value of @ref TIM_AutoReloadPreload 
    }TIM_Base_InitTypeDef;
    */
    tim2_handler.Instance = TIM2;                                         //选择定时器2
    tim2_handler.Init.Prescaler = 72 - 1;                                    //PSC
    tim2_handler.Init.Period = 65536 - 1;                                       //ARR
    tim2_handler.Init.CounterMode = TIM_COUNTERMODE_UP;                   //计数模式(递增计数模式)
    tim2_handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //关自动重装载
    //HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
    HAL_TIM_Base_Init(&tim2_handler);
}

//msp函数(各种定时器中断都会调用这个函数)
//上面的HAL_TIM_Base_Init会自动调用该函数
//__weak void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();                
    }
}

void tim2_start(void)
{
    HAL_TIM_Base_Start(&tim2_handler);
}

void tim2_stop(void)
{
    HAL_TIM_Base_Stop(&tim2_handler);
}

uint16_t tim2_get_cnt(void)
{
    return __HAL_TIM_GetCounter(&tim2_handler);
}

void tim2_set_cnt(uint16_t val)
{
    __HAL_TIM_SetCounter(&tim2_handler , val);
}

void hcsr04_gpio_init(void)
{
    GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
    
    //打开时钟
    TRIG_GPIO_CLK_ENABLE();                   //使能GPIOB时钟
    ECHO_GPIO_CLK_ENABLE();
    
    /*
    typedef struct
    {
      uint32_t Pin;       < Specifies the GPIO pins to be configured.
                               This parameter can be any value of @ref GPIO_pins_define 
      uint32_t Mode;      < Specifies the operating mode for the selected pins.
                               This parameter can be a value of @ref GPIO_mode_define 
      uint32_t Pull;      < Specifies the Pull-up or Pull-Down activation for the selected pins.
                               This parameter can be a value of @ref GPIO_pull_define 
      uint32_t Speed;     < Specifies the speed for the selected pins.
                               This parameter can be a value of @ref GPIO_speed_define 
    } GPIO_InitTypeDef;
    */
    //初始化Trig引脚
    Gpio_Initstruct.Pin = TRIG_PIN;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出模式
    Gpio_Initstruct.Pull = GPIO_NOPULL;           
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
    
    //调用GPIO初始化函数
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(TRIG_PORT , &Gpio_Initstruct);
    
    //初始化Echo引脚
    Gpio_Initstruct.Pin = ECHO_PIN;          
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
    
    //调用GPIO初始化函数
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(ECHO_PORT , &Gpio_Initstruct);
}

void hcsr04_init(void)
{
    tim2_init();
    hcsr04_gpio_init();
    
}

float hcsr04_get_length(void)
{
    uint16_t total_time = 0;
    float distance = 0;
    
    //1. Trig,给Trig端口至少10us的高电平
    TRIG_HIGH();
    delay_us(15);
    TRIG_LOW();
    
    //2. Echo引脚,由低电平跳转到高电平,表示开始发送波
            //波发出去的那一下,开始启动定时器
    while(ECHO_STATUS() == GPIO_PIN_RESET);
    tim2_start();
    tim2_set_cnt(0);
    
    //3. Echo,由高电平跳转回低电平,表示波回来了
            //波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
    while(ECHO_STATUS() == GPIO_PIN_SET);
    tim2_stop();
    
    //4. 计算出中间经过多长时间Echo引脚维持高电平的时间
    total_time = tim2_get_cnt();
    
    //5. 距离 = 速度(343m/s) * 时间 / 2
    distance = total_time * 0.01715;
    return distance;
}

############################## main.c ##############################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "hcsr04.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    hcsr04_init();
    printf("hello word!\r\n");
    
    while(1)
    {
        printf("dis: %2f\r\n",hcsr04_get_length());
        delay_ms(1000);
    }
}

九. 独立看门狗

9.1 IWDG简介

独立看门狗(Independent Watchdog,通常缩写为IWDG)主要作用是主要用于检测外界电磁干扰,或硬
件异常导致的程序跑飞问题。

  • IWDG本质上是一个12位的递减计数器。当计数器的值从某个初始值开始递减,并一直减到0时,系统
    会产生一个复位信号(IWDG_RESET)。CPU在接收到这个复位信号后,会重新启动系统,以确保系统
    从可能的错误或死锁状态中恢复。
  • 在计数器的值减到0之前,如果程序通过特定的“喂狗”操作(即重置计数器的值)来刷新计数器,那么
    就不会产生复位信号,系统将继续正常运行。这种“喂狗”操作通常是由程序在正常运行时定期执行的,
    以确保IWDG不会因计数器超时而产生复位信号。
  • 它使用专用的低速时钟(LSI)作为时钟源,即使在主时钟发生故障时,IWDG仍然能够继续运行。
    IWDG可以在停止模式和待机模式下工作,确保在这些模式下系统仍然受到保护。

在这里插入图片描述
在这里插入图片描述

9.2 IWDG溢出时间计算

LSI 时钟频率并不精确,RC 频率会在 30kHz 到 60kHz 之间变化 ,F1 用 40kHz 进行计算即可。
在这里插入图片描述
Tout:看门狗溢出时间
fIWDG:看门狗的时钟源频率
psc:看门狗预分频系数
rlr:看门狗重装载值
在这里插入图片描述
在这里插入图片描述

9.3 独立看门狗喂狗实验

配置 IWDG 溢出时间为 1 秒左右,并验证未及时喂狗时系统将被复位。

######################## iwdg.h ########################
#ifndef __IWDG_H__
#define __IWDG_H__

#include "sys.h"

void iwdg_init(uint8_t psc , uint16_t rlr);
void iwdg_feed(void);

#endif

######################## iwdg.c ########################
#include "iwdg.h"

IWDG_HandleTypeDef iwdg_handler = {0};

void iwdg_init(uint8_t psc , uint16_t rlr)
{
    //HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
    iwdg_handler.Instance = IWDG;
    iwdg_handler.Init.Prescaler = psc;
    iwdg_handler.Init.Reload = rlr;
    
    HAL_IWDG_Init(&iwdg_handler);
}

void iwdg_feed(void)
{
    //喂狗
    //HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
    HAL_IWDG_Refresh(&iwdg_handler);
}

######################## main.c ########################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "iwdg.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    iwdg_init(IWDG_PRESCALER_32 , 1250 - 1);
    printf("hello word!\r\n");
    printf("喂狗!\r\n");
    
    if(__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
    {
        printf("独立看门狗复位\r\n");
        __HAL_RCC_CLEAR_RESET_FLAGS();
    }
    else
    {
        printf("外部复位\r\n");
    }
    
    while(1)
    {
        delay_ms(1500);
        iwdg_feed();
        printf("喂狗成功!\r\n");
    }
}

十. 窗口看门狗

10.1 WWDG简介

窗口看门狗用于监测单片机程序运行时效是否精准,主要检测软件异常,一般用于需要精准检测程序运行时
间的场合。
窗口看门狗的本质是一个能产生系统复位信号和提前唤醒中断的6位计数器
产生复位条件:

  • 当递减计数器值从 0x40 减到 0x3F 时复位(即T6位跳变到0)
  • 计数器的值大于 W[6:0] 值时喂狗会复位。

产生中断条件:

  • 当递减计数器等于 0x40 时可产生提前唤醒中断 (EWI)。
  • 在窗口期内重装载计数器的值,防止复位,也就是所谓的喂狗。

在这里插入图片描述

10.2 WWDG配置步骤

在这里插入图片描述

10.3 窗口看门狗喂狗实验

**开启窗口看门狗,计数器值设置为 0X7F ,窗口值设置为 0X5F ,预分频系数为 8 。**在 while 循环里喂狗,同时翻转 LED1 状态;在提前唤醒中断服务函数进行喂狗,同时翻转 LED2 状态。

######################### wwdg.h #########################
#ifndef __WWDG_H__
#define __WWDG_H__

#include "sys.h"

void wwdg_init(uint8_t tr , uint8_t wr , uint32_t psc);
void wwdg_feed(void);

#endif

######################### wwdg.c #########################
#include "wwdg.h"
#include "Led.h"

WWDG_HandleTypeDef wwdg_handler = {0};

void wwdg_init(uint8_t tr , uint8_t wr , uint32_t psc)
{
    wwdg_handler.Instance = WWDG;
    wwdg_handler.Init.Counter = tr;                 //计数器值
    wwdg_handler.Init.Window = wr;                  //窗口值
    wwdg_handler.Init.Prescaler = psc;              //预分频系数
    wwdg_handler.Init.EWIMode = WWDG_EWI_ENABLE;    //开中断
    //设置WWDG预分频系数、重装载值、窗口值
    HAL_WWDG_Init(&wwdg_handler);
}

//msp初始化(NVIC、CLOCK)
//__weak void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
    __HAL_RCC_WWDG_CLK_ENABLE();
    
    HAL_NVIC_SetPriority(WWDG_IRQn , 2 ,2);
    HAL_NVIC_EnableIRQ(WWDG_IRQn);
}

//中断服务函数
void WWDG_IRQHandler(void)
{
    //void HAL_WWDG_IRQHandler(WWDG_HandleTypeDef *hwwdg)
    HAL_WWDG_IRQHandler(&wwdg_handler);
}

//回调函数
//__weak void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
    wwdg_feed();
    Led2_Toggle();
}

void wwdg_feed(void)
{
    HAL_WWDG_Refresh(&wwdg_handler);
}

######################### main.c #########################

#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "wwdg.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    wwdg_init(0x7f , 0x5f , WWDG_PRESCALER_8);
    printf("hello word!\r\n");
    
    if(__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET)
    {
        printf("窗口看门狗复位!\r\n");
        __HAL_RCC_CLEAR_RESET_FLAGS();
    }
    else
    {
        printf("外部复位!\r\n");
    }
    
    while(1)
    {
        delay_ms(40);
        wwdg_feed();
        Led1_Toggle();
        
    }
}


十一. 串口

11.1 简介

在这里插入图片描述
在这里插入图片描述
SR:状态寄存器,每个比特位标志了串口控制器中不同的状态变化
RXNE:接收数据寄存器非空标志 1表示RDR寄存器中有数据,可以读取,0表示RDR寄存器中没有数据
TXE:发送数据寄存器为空标志 1表示TDR寄存器中没有数据,可以发送,0表示TDR寄存器中有数据不能发送(覆盖上一次发送的数据)

== 当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断 ==
接受到一个字节的数据,触发中断
比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。

11.2 通过串口发送/接收一个字符

在这里插入图片描述
接收到数据则触发中断,执行中断函数内部程序!
HAL_UART_Receive(&uart1_handler , &receive_data , 1 , 1000); //一个字节(相当于一个)一个字节接收
一个字节可以表示一个数据,也可以表示一个英文字母或其他特殊字符,两个字节可以表示一个汉字

在这里插入图片描述

############################# uart.h ############################# 
#ifndef __USART_H__
#define __USART_H__

#include "sys.h"

void uart1_init(uint32_t baudrate);


#endif

############################# uart.c ############################# 
#include "uart1.h"

UART_HandleTypeDef uart1_handler = {0};

void uart1_init(uint32_t baudrate)
{
    uart1_handler.Instance = USART1;
    uart1_handler.Init.BaudRate = baudrate;                 //波特率
    uart1_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    uart1_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    uart1_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    uart1_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    uart1_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&uart1_handler);
}

//__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        //打开时钟
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOB时钟
        
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        Gpio_Initstruct.Pin = GPIO_PIN_9;               //TXD1
        Gpio_Initstruct.Mode = GPIO_MODE_AF_PP;         //推挽输出模式
        Gpio_Initstruct.Pull = GPIO_PULLUP;             //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        
        Gpio_Initstruct.Pin = GPIO_PIN_10;              //RXD1
        Gpio_Initstruct.Mode = GPIO_MODE_AF_INPUT;      //推挽输入模式
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        HAL_NVIC_SetPriority(USART1_IRQn , 2 ,2);
        
        //__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
        __HAL_UART_ENABLE_IT(huart , UART_IT_RXNE);     //中断使能 UART_IT_RXNE位  若接收数据存储器不为空,触发中断,调用中断服务函数
    }
}

//中断服务函数
void USART1_IRQHandler(void)
{
    uint8_t receiv_data = 0;
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) 判断RXNE是否为1
    //RXNE-(接受数据存储器)不为空,置1,触发中断
    if(__HAL_UART_GET_FLAG(&uart1_handler , UART_FLAG_RXNE) != RESET)  
    {
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&uart1_handler , &receiv_data , 1 , 1000);
        //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Transmit(&uart1_handler , &receiv_data , 1 , 1000);
    }
}

############################# main.c ############################# 
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
//    Led_On();
//    Led_Off();
    
    while(1)
    {
        Led1_On();
        Led2_Off();
        delay_ms(500);
        Led1_Off();
        Led2_On();
        delay_ms(500);
    }
}

11.3 串口接收不定长数据(接收中断)

良许老师

每接收一个字节就进一次中断

################################### uart.h ################################### 
#ifndef __USART_H__
#define __USART_H__

#include "sys.h"

#define UART1_RX_BUF_SIZE 128
#define UART1_TX_BUF_SIZE 64
#define UART_EOK     0
#define UART_ERROR   1
#define UART_ETIMOUT 2
#define UART_EINVAL  3

void uart1_init(uint32_t baudrate);
void uart1_receiv_test(void);


#endif

################################### uart.c ################################### 
#include "uart1.h"
#include "string.h"
#include "stdio.h"


uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
uint16_t uart1_rx_len = 0;
uint16_t uart1_cnt = 0 , uart1_cntPre = 0;

UART_HandleTypeDef uart1_handler = {0};

void uart1_init(uint32_t baudrate)
{
    uart1_handler.Instance = USART1;
    uart1_handler.Init.BaudRate = baudrate;                 //波特率
    uart1_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    uart1_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    uart1_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    uart1_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    uart1_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&uart1_handler);
}

//__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        //打开时钟
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOB时钟
        
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        Gpio_Initstruct.Pin = GPIO_PIN_9;
        Gpio_Initstruct.Mode = GPIO_MODE_AF_PP;         //推挽输出模式
        Gpio_Initstruct.Pull = GPIO_PULLUP;             //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        
        Gpio_Initstruct.Pin = GPIO_PIN_10;
        Gpio_Initstruct.Mode = GPIO_MODE_AF_INPUT;       //推挽输入模式
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        HAL_NVIC_SetPriority(USART1_IRQn , 2 ,2);
        
        //__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
        __HAL_UART_ENABLE_IT(huart , UART_IT_RXNE);
    }
}

void USART1_IRQHandler(void)
{
    uint8_t receive_data = 0;
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)
    if(__HAL_UART_GET_FLAG(&uart1_handler , UART_FLAG_RXNE) != RESET)
    {
        //接收数据长度大于缓冲区大小--清零
        if(uart1_cnt >= sizeof(uart1_rx_buf))
            uart1_cnt = 0;
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&uart1_handler , &receive_data , 1 , 1000);   //一个字节一个字节接收
        uart1_rx_buf[uart1_cnt++] = receive_data;      //接收数据,uart1_cnt接收数据长度
        
        //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
        //HAL_UART_Transmit(&uart1_handler , &receiv_data , 1 , 1000);
    }
}

//更改printf内部源代码
int fputc(int ch, FILE *f)
{
    while((USART1->SR & 0X40) == 0);
    
    USART1->DR = (uint8_t)ch;
    return ch;
}

uint8_t uart1_wait_receive(void)
{
    //若接收长度为0 
    if(uart1_cnt == 0)
        return UART_ERROR;
    
    //无数据传入
    if(uart1_cnt == uart1_cntPre)
    {
        uart1_cnt = 0;
        return UART_EOK;
    }
    //正在传入数据
    uart1_cntPre = uart1_cnt;
    return UART_ERROR;
}

void uart1_rx_clear(void)
{
    memset(uart1_rx_buf , 0 , sizeof(uart1_rx_buf));
    uart1_rx_len = 0;
}

void uart1_receiv_test(void)
{
    if(uart1_wait_receive() == UART_EOK)        //数据接收完整
    {
        printf("recv: %s\r\n", uart1_rx_buf);
        uart1_rx_clear();
    }
}


################################### main.h ################################### 
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
//    Led_On();
//    Led_Off();
    
    while(1)
    {
        uart1_receiv_test();
        delay_ms(10);
    }
}


11.4 串口接收不定长数据(空闲中断)

串口在一段时间里未接收到新的数据,则会触发空闲中断。

################################# uart.h #################################
#ifndef __USART_H__
#define __USART_H__

#include "sys.h"

#define UART1_RX_BUF_SIZE 128
#define UART1_TX_BUF_SIZE 64
#define UART_EOK     0
#define UART_ERROR   1
#define UART_ETIMOUT 2
#define UART_EINVAL  3

void uart1_init(uint32_t baudrate);
void uart1_receiv_test(void);


#endif

################################# uart.c #################################
#include "uart1.h"
#include "string.h"
#include "stdio.h"


uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
uint16_t uart1_rx_len = 0;

UART_HandleTypeDef uart1_handler = {0};

void uart1_init(uint32_t baudrate)
{
    uart1_handler.Instance = USART1;
    uart1_handler.Init.BaudRate = baudrate;                 //波特率
    uart1_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    uart1_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    uart1_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    uart1_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    uart1_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&uart1_handler);
}

//__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        //打开时钟
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOB时钟
        
        GPIO_InitTypeDef Gpio_Initstruct;               //定义一个结构体
        Gpio_Initstruct.Pin = GPIO_PIN_9;
        Gpio_Initstruct.Mode = GPIO_MODE_AF_PP;         //推挽输出模式
        Gpio_Initstruct.Pull = GPIO_PULLUP;           //上拉输出(默认高电平)
        Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        
        Gpio_Initstruct.Pin = GPIO_PIN_10;
        Gpio_Initstruct.Mode = GPIO_MODE_AF_INPUT;         //推挽输入模式
        HAL_GPIO_Init(GPIOA , &Gpio_Initstruct);
        
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        HAL_NVIC_SetPriority(USART1_IRQn , 2 ,2);
        
        //__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
        __HAL_UART_ENABLE_IT(huart , UART_IT_RXNE);
        __HAL_UART_ENABLE_IT(huart , UART_IT_IDLE);     //开空闲中断
    }
}

void uart1_rx_clear(void)
{
    memset(uart1_rx_buf , 0 , sizeof(uart1_rx_buf));
    uart1_rx_len = 0;
}

void USART1_IRQHandler(void)
{
    uint8_t receive_data = 0;
    //数据接收
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)
    if(__HAL_UART_GET_FLAG(&uart1_handler , UART_FLAG_RXNE) != RESET)
    {
        //接收数据长度大于缓冲区大小--清零
        if(uart1_rx_len >= sizeof(uart1_rx_buf))
            uart1_rx_len = 0;
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&uart1_handler , &receive_data , 1 , 1000);
        uart1_rx_buf[uart1_rx_len++] = receive_data;      //接收数据,uart1_cnt接收数据长度
    }
    //数据发送完,触发空闲中断
    if(__HAL_UART_GET_FLAG(&uart1_handler , UART_FLAG_IDLE) != RESET)
    {
        printf("recv: %s\r\n",uart1_rx_buf);
        uart1_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handler);          //清除空闲标志位
    }
}

//更改printf内部源代码
int fputc(int ch, FILE *f)
{
    while((USART1->SR & 0X40) == 0);
    
    USART1->DR = (uint8_t)ch;
    return ch;
}

################################# main.c #################################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
//    Led_On();
//    Led_Off();
    
    while(1)
    {
        
    }
}

十二. 模块

12.1 蓝牙模块

良许老师
在这里插入图片描述
蓝牙模块使用串口2
通过给蓝牙软件发送数据,透传模式进入stm32,放到接收数据寄存器,串口1的RXNE发挥作用,触发中断,接收完成,串口1的空闲中断IDLE触发,把接收到的信息发送给电脑。
在这里插入图片描述

############################### bluetooth.h ###############################
#ifndef __BLUETOOTH_H__
#define __BLUETOOTH_H__

#include "stdio.h"
#include "sys.h"


/* UART收发缓冲大小 */
#define UART2_RX_BUF_SIZE            128
#define UART2_TX_BUF_SIZE            64


void bt_init(uint32_t bound);            /* 串口初始化函数 */
//void bt_send(char *send_buf , uint8_t size);
void bt_send(char * format , ...);

#endif

############################### bluetooth.c ###############################
#include "bluetooth.h"
#include "uart1.h"
#include "string.h"
#include "stdarg.h"

UART_HandleTypeDef uart2_handle;                                            /* UART2句柄 */

uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];                                    /* uart2接收缓冲区 */
uint16_t uart2_rx_len = 0;                                                  /* uart2接收字符长度 */


/**
 * @brief       串口2初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void bt_init(uint32_t baudrate)
{
    /*uart2 初始化设置*/
    uart2_handle.Instance = USART2;                                         /* USART1 */
    uart2_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart2_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart2_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart2_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart2_handle);                                           /* HAL_UART_Init()会使能uart2 */
}

/**
 * @brief       uart2接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart2_rx_clear(void)
{
    memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));                          /* 清空接收缓冲区 */
    uart2_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口2中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart2_rx_len >= sizeof(uart2_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart2_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart2_rx_buf[uart2_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("bt recv: %s\r\n", uart2_rx_buf);                            /* 将接收到的数据打印出来 */
        uart2_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);                           /* 清除UART总线空闲中断 */
    }
}

//void bt_send(char *send_buf , uint8_t size)
//{
//    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
//    HAL_UART_Transmit(&uart2_handle , (uint8_t *)send_buf , size , 100);
//}

void bt_send(char * format , ...)
{
    uint8_t send_buf[128] = {0};
    va_list arg;                                //创建arg列表
    va_start(arg , format);                     //把传入数据丢进列表中
    vsprintf((char *)send_buf , format , arg);  //把列表中的数据以format格式传进send_buf中
    va_end(arg);                              
    HAL_UART_Transmit(&uart2_handle , send_buf , sizeof(send_buf) , 100);
}

############################### uart1.c ###############################
#include "sys.h"
#include "uart1.h"
#include "string.h"

UART_HandleTypeDef uart1_handle;                                            /* UART1句柄 */

uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收缓冲区 */
uint16_t uart1_rx_len = 0;                                                  /* UART1接收字符长度 */

/**
 * @brief       重定义fputc函数
 * @note        printf函数最终会通过调用fputc输出字符串到串口
 */
int fputc(int ch, FILE *f)
{
    while ((USART1->SR & 0X40) == 0);                                       /* 等待上一个字符发送完成 */

    USART1->DR = (uint8_t)ch;                                               /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}

/**
 * @brief       串口1初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void uart1_init(uint32_t baudrate)
{
    /*UART1 初始化设置*/
    uart1_handle.Instance = USART1;                                         /* USART1 */
    uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart1_handle);                                           /* HAL_UART_Init()会使能UART1 */
}

/**
 * @brief       UART底层初始化函数
 * @param       huart: UART句柄类型指针
 * @note        此函数会被HAL_UART_Init()调用
 *              完成时钟使能,引脚配置,中断配置
 * @retval      无
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART1)                                          /* 如果是串口1,进行串口1 MSP初始化 */
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();                                       /* 使能串口TX脚时钟 */
        __HAL_RCC_USART1_CLK_ENABLE();                                      /* 使能串口时钟 */

        gpio_init_struct.Pin = GPIO_PIN_9;                                  /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* IO速度设置为高速 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
                
        gpio_init_struct.Pin = GPIO_PIN_10;                                 /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);                            /* 串口RX脚 必须设置成输入模式 */
        
        HAL_NVIC_EnableIRQ(USART1_IRQn);                                    /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);                            /* 组2,最低优先级:抢占优先级3,子优先级3 */

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);                          /* 使能UART1接收中断 */
        __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);                          /* 使能UART1总线空闲中断 */
    }
    else if (huart->Instance == USART2)                                     /* 如果是串口2,进行串口2 MSP初始化 */
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();                                       /* 使能串口TX脚时钟 */
        __HAL_RCC_USART1_CLK_ENABLE();                                      /* 使能串口时钟 */

        gpio_init_struct.Pin = GPIO_PIN_2;                                  /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* IO速度设置为高速 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
                
        gpio_init_struct.Pin = GPIO_PIN_3;                                  /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);                            /* 串口RX脚 必须设置成输入模式 */
        
        HAL_NVIC_EnableIRQ(USART2_IRQn);                                    /* 使能USART2中断通道 */
        HAL_NVIC_SetPriority(USART2_IRQn, 3, 3);                            /* 组2,最低优先级:抢占优先级3,子优先级3 */

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);                          /* 使能UART2接收中断 */
        __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);                          /* 使能UART1总线空闲中断 */
    }
}

/**
 * @brief       UART1接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart1_rx_clear(void)
{
    memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));                          /* 清空接收缓冲区 */
    uart1_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口1中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART1_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart1_rx_len >= sizeof(uart1_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart1_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart1_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart1_rx_buf[uart1_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("recv: %s\r\n", uart1_rx_buf);                               /* 将接收到的数据打印出来 */
        uart1_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);                           /* 清除UART总线空闲中断 */
    }
}

################################## main.c ##############################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "bluetooth.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    bt_init(115200);
    printf("hello word!\r\n");
    
    uint8_t i = 0;
    while(1)
    {
        bt_send("hello ,bt1%d\r\n",i++);
        delay_ms(500);
    }
}

12.2 蓝牙遥控插座实验

蓝牙、继电器

###################### bluetooth.c #######################################
#include "bluetooth.h"
#include "uart1.h"
#include "string.h"
#include "stdarg.h"
#include "plugin.h"

UART_HandleTypeDef uart2_handle;                                            /* UART2句柄 */

uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];                                    /* uart2接收缓冲区 */
uint16_t uart2_rx_len = 0;                                                  /* uart2接收字符长度 */


/**
 * @brief       串口2初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void bt_init(uint32_t baudrate)
{
    /*uart2 初始化设置*/
    uart2_handle.Instance = USART2;                                         /* USART1 */
    uart2_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart2_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart2_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart2_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart2_handle);                                           /* HAL_UART_Init()会使能uart2 */
}

/**
 * @brief       uart2接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart2_rx_clear(void)
{
    memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));                          /* 清空接收缓冲区 */
    uart2_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口2中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart2_rx_len >= sizeof(uart2_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart2_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart2_rx_buf[uart2_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("bt recv: %s\r\n", uart2_rx_buf);                            /* 将接收到的数据打印出来 */
        
        if(strstr((char *)uart2_rx_buf , "on") != NULL)
            plugin_on();
        else if(strstr((char *)uart2_rx_buf , "off"))
            plugin_off();
        
        uart2_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);                           /* 清除UART总线空闲中断 */
    }
}

//void bt_send(char *send_buf , uint8_t size)
//{
//    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
//    HAL_UART_Transmit(&uart2_handle , (uint8_t *)send_buf , size , 100);
//}

void bt_send(char * format , ...)
{
    uint8_t send_buf[128] = {0};
    va_list arg;                                //创建arg列表
    va_start(arg , format);                     //把传入数据丢进列表中
    vsprintf((char *)send_buf , format , arg);  //把列表中的数据以format格式传进send_buf中
    va_end(arg);                              
    HAL_UART_Transmit(&uart2_handle , send_buf , sizeof(send_buf) , 100);
}

###################### plugin.c #######################################
#include "plugin.h"
#include "sys.h"


//初始化GPIO函数
void plugin_init(void)
{
    /*
    typedef struct
    {
      uint32_t Pin;       < Specifies the GPIO pins to be configured.
                               This parameter can be any value of @ref GPIO_pins_define 
      uint32_t Mode;      < Specifies the operating mode for the selected pins.
                               This parameter can be a value of @ref GPIO_mode_define 
      uint32_t Pull;      < Specifies the Pull-up or Pull-Down activation for the selected pins.
                               This parameter can be a value of @ref GPIO_pull_define 
      uint32_t Speed;     < Specifies the speed for the selected pins.
                               This parameter can be a value of @ref GPIO_speed_define 
    } GPIO_InitTypeDef;
    */
    
    GPIO_InitTypeDef Gpio_Initstruct;
    Gpio_Initstruct.Pin = GPIO_PIN_6;
    Gpio_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出模式
    Gpio_Initstruct.Pull = GPIO_PULLUP;             //上拉输出(默认高电平)
    Gpio_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速模式
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    //调用GPIO初始化函数
    //void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    HAL_GPIO_Init(GPIOB,&Gpio_Initstruct);
    //关闭LED
    plugin_off();
}


void plugin_on(void)
{
    //void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
}

//熄灭LED函数
void plugin_off(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}

uint8_t plugin_status_get(void)
{
    return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6);
}

12.3 ESP8266

12.3.1 简介

良许老师

12.3.2 ESP8266作为设备模式(STA)

一般用于远距离传输。ESP8266 通过路由器连接互联网,终端设备通过互联网实现对设备的远程控制。简单来说,此时的 ESP8266 可以当作是一个客户端,可以向服务端进行数据的下载与传输。这就类似于,手机/平板/笔记本(客户端)可以通过 WIFI 连接到路由器进行上网。

12.3.3 ESP8266作为路由模式(AP)

一般用于近距离传输。ESP8266 作为热点,提供无线接入服务、数据访问,一般的无线路由/网桥工作在 AP 模式下,最多支持 4 台设备接入。简单来说,此时的 ESP8266 可以当作是一个服务端。这就类似于,ESP8266 变身为一个路由器,然后手机/平板/笔记本可以通过 WIFI 连接到 ESP8266 进行上网。

12.3.4 ESP8266串口通讯功能

串口2模拟ESP8266

########################### esp8266.h #######################################
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "sys.h"

#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64
#define ESP8266_EOK     0
#define ESP8266_ERROR   1
#define ESP8266_ETIMOUT 2
#define ESP8266_EINVAL  3

void esp8266_init(uint32_t baudrate);
void esp8266_receive_data(void);
void esp8266_test(void);

#endif

########################### esp8266.c #######################################
#include "esp8266.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"

uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE];
uint16_t esp8266_cnt = 0 , esp8266_cntPre = 0;

UART_HandleTypeDef esp8266_handler = {0};

void esp8266_uart_init(uint32_t baudrate)
{
    esp8266_handler.Instance = USART2;
    esp8266_handler.Init.BaudRate = baudrate;                 //波特率
    esp8266_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    esp8266_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    esp8266_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    esp8266_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    esp8266_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&esp8266_handler);
}


void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)
    if(__HAL_UART_GET_FLAG(&esp8266_handler , UART_FLAG_RXNE) != RESET)
    {
        //接收数据长度大于缓冲区大小--清零
        if(esp8266_cnt >= sizeof(esp8266_rx_buf))
            esp8266_cnt = 0;
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&esp8266_handler , &receive_data , 1 , 1000);   //一个字节一个字节接收
        esp8266_rx_buf[esp8266_cnt++] = receive_data;      //接收数据,esp8266_cnt接收数据长度
        
        //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
        //HAL_UART_Transmit(&esp8266_handler , &receiv_data , 1 , 1000);
    }
}


uint8_t esp8266_wait_receive(void)
{
    //若接收长度为0 
    if(esp8266_cnt == 0)
        return ESP8266_ERROR;
    
    //无数据传入
    if(esp8266_cnt == esp8266_cntPre)
    {
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //正在传入数据
    esp8266_cntPre = esp8266_cnt;
    return ESP8266_ERROR;
}

void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf , 0 , sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK)        //数据接收完整
    {
        printf("esp8266 recv: %s\r\n", esp8266_rx_buf);  //使用串口1打印--printf
        esp8266_rx_clear();
    }
}

void esp8266_init(uint32_t baudrate)
{
    esp8266_uart_init(baudrate);            //串口初始化
    
    //esp8266其他初始化
}

uint8_t esp8266_send_command(char *cmd , char *res)
{
    uint8_t time_out = 250;
    
    esp8266_rx_clear();
    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_UART_Transmit(&esp8266_handler , (uint8_t *)cmd , strlen(cmd) , 100);
    
    while(time_out--)
    {
        if(esp8266_wait_receive() == ESP8266_EOK)
        {
            if(strstr((const char *)esp8266_rx_buf , res) != NULL)
                return ESP8266_EOK;
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

void esp8266_test(void)
{
    if(esp8266_send_command("AT" , "OK") == ESP8266_EOK)
        printf("esp8266 test:%s\r\n",esp8266_rx_buf);
}

######################## uart1.c ##############################
#include "sys.h"
#include "uart1.h"
#include "string.h"

UART_HandleTypeDef uart1_handle;                                            /* UART1句柄 */

uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收缓冲区 */
uint16_t uart1_rx_len = 0;                                                  /* UART1接收字符长度 */

/**
 * @brief       重定义fputc函数
 * @note        printf函数最终会通过调用fputc输出字符串到串口
 */
int fputc(int ch, FILE *f)
{
    while ((USART1->SR & 0X40) == 0);                                       /* 等待上一个字符发送完成 */

    USART1->DR = (uint8_t)ch;                                               /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}

/**
 * @brief       串口1初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void uart1_init(uint32_t baudrate)
{
    /*UART1 初始化设置*/
    uart1_handle.Instance = USART1;                                         /* USART1 */
    uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart1_handle);                                           /* HAL_UART_Init()会使能UART1 */
}

/**
 * @brief       UART底层初始化函数
 * @param       huart: UART句柄类型指针
 * @note        此函数会被HAL_UART_Init()调用
 *              完成时钟使能,引脚配置,中断配置
 * @retval      无
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART1)                                          /* 如果是串口1,进行串口1 MSP初始化 */
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();                                       /* 使能串口TX脚时钟 */
        __HAL_RCC_USART1_CLK_ENABLE();                                      /* 使能串口时钟 */

        gpio_init_struct.Pin = GPIO_PIN_9;                                  /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* IO速度设置为高速 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
                
        gpio_init_struct.Pin = GPIO_PIN_10;                                 /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);                            /* 串口RX脚 必须设置成输入模式 */
        
        HAL_NVIC_EnableIRQ(USART1_IRQn);                                    /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);                            /* 组2,最低优先级:抢占优先级3,子优先级3 */

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);                          /* 使能UART1接收中断 */
        __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);                          /* 使能UART1总线空闲中断 */
    }
        else if (huart->Instance == USART2)                                     /* 如果是串口2,进行串口2 MSP初始化 */
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();                                       /* 使能串口TX脚时钟 */
        __HAL_RCC_USART1_CLK_ENABLE();                                      /* 使能串口时钟 */

        gpio_init_struct.Pin = GPIO_PIN_2;                                  /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* IO速度设置为高速 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
                
        gpio_init_struct.Pin = GPIO_PIN_3;                                  /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);                            /* 串口RX脚 必须设置成输入模式 */
        
        HAL_NVIC_EnableIRQ(USART2_IRQn);                                    /* 使能USART2中断通道 */
        HAL_NVIC_SetPriority(USART2_IRQn, 3, 3);                            /* 组2,最低优先级:抢占优先级3,子优先级3 */

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);                          /* 使能UART2接收中断 */
        //__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);                          /* 使能UART1总线空闲中断 */
    }
}

/**
 * @brief       UART1接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart1_rx_clear(void)
{
    memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));                          /* 清空接收缓冲区 */
    uart1_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口1中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART1_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart1_rx_len >= sizeof(uart1_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart1_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart1_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart1_rx_buf[uart1_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("recv: %s\r\n", uart1_rx_buf);                               /* 将接收到的数据打印出来 */
        uart1_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);                           /* 清除UART总线空闲中断 */
    }
}

######################## main.c ##############################
#include "sys.h"
#include "delay.h"
#include "Led.h"
#include "uart1.h"
#include "esp8266.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    Led_Init();
    uart1_init(115200);
    esp8266_init(115200);
    
    printf("hello word!\r\n");
    
    while(1)
    {
        //esp8266_receive_data();
        esp8266_test();
        delay_ms(10);
    }
}

12.3.5 ESP8266 联网功能

需将ESP8266插入串口2中

#include "esp8266.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"

uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE];
uint16_t esp8266_cnt = 0 , esp8266_cntPre = 0;

UART_HandleTypeDef esp8266_handler = {0};

void esp8266_uart_init(uint32_t baudrate)
{
    esp8266_handler.Instance = USART2;
    esp8266_handler.Init.BaudRate = baudrate;                 //波特率
    esp8266_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    esp8266_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    esp8266_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    esp8266_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    esp8266_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&esp8266_handler);
}


void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)
    if(__HAL_UART_GET_FLAG(&esp8266_handler , UART_FLAG_RXNE) != RESET)
    {
        //接收数据长度大于缓冲区大小--清零
        if(esp8266_cnt >= sizeof(esp8266_rx_buf))
            esp8266_cnt = 0;
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&esp8266_handler , &receive_data , 1 , 1000);   //一个字节一个字节接收
        esp8266_rx_buf[esp8266_cnt++] = receive_data;      //接收数据,esp8266_cnt接收数据长度
        
        //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
        //HAL_UART_Transmit(&esp8266_handler , &receiv_data , 1 , 1000);
    }
}


uint8_t esp8266_wait_receive(void)
{
    //若接收长度为0 
    if(esp8266_cnt == 0)
        return ESP8266_ERROR;
    
    //无数据传入
    if(esp8266_cnt == esp8266_cntPre)
    {
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //正在传入数据
    esp8266_cntPre = esp8266_cnt;
    return ESP8266_ERROR;
}

void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf , 0 , sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK)        //数据接收完整
    {
        printf("esp8266 recv: %s\r\n", esp8266_rx_buf);  //使用串口1打印--printf
        esp8266_rx_clear();
    }
}

uint8_t esp8266_send_command(char *cmd , char *res)
{
    uint8_t time_out = 250;
    
    esp8266_rx_clear();
    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_UART_Transmit(&esp8266_handler , (uint8_t *)cmd , strlen(cmd) , 100);
    
    while(time_out--)
    {
        if(esp8266_wait_receive() == ESP8266_EOK)
        {
            if(strstr((const char *)esp8266_rx_buf , res) != NULL)
                return ESP8266_EOK;
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\r\n" , "OK");
}

uint8_t esp8266_set_mode(uint8_t mode)
{
    switch(mode)
    {
        case ESP8266_STA_MODE:
            return esp8266_send_command("AT+CWMODE=1\r\n" , "OK");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n" , "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n" , "OK");
        
        default:
            return ESP8266_EINVAL;
    }
}

uint8_t esp8266_join_ap(char * ssid , char *pwd)
{
    char cmd[64];
    sprintf(cmd , "AT+CWJAP=\"%s\" , \"%s\"\r\n" , ssid , pwd);
    return esp8266_send_command(cmd , "WIFI GOT IP");
}

uint8_t esp8266_connection_mode(uint8_t mode)
{
    char cmd[64];
    sprintf(cmd , "AT+CIPMUX=%d\r\n" , mode);
    return esp8266_send_command(cmd , "OK");
}

void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    esp8266_uart_init(baudrate);            //串口初始化
    
    //esp8266其他初始化
    printf("1. 测试esp8266是否存在...\r\n");
    while(esp8266_at_test())
        delay_ms(500);
    printf("2. 设置工作模式为STA...\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE))
        delay_ms(500);
    printf("3. 设置单路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION))
        delay_ms(500);
    printf("4. 链接wifi,SSID:%s , PWD:%s\r\n",WIFI_SSID , WIFI_PWD);
    while(esp8266_join_ap(WIFI_SSID , WIFI_PWD))
        delay_ms(1500);
    printf("ESP8266初始化完成!\r\n");
}


void esp8266_test(void)
{
    if(esp8266_send_command("AT" , "OK") == ESP8266_EOK)
        printf("esp8266 test:%s\r\n",esp8266_rx_buf);
}

12.3.6 ESP8266 连接TCP服务器

####################################### esp8266.c #######################################
#include "esp8266.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"
#include "stdarg.h"

uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE];
uint8_t esp8266_tx_buf[ESP8266_RX_BUF_SIZE];

uint16_t esp8266_cnt = 0 , esp8266_cntPre = 0;

UART_HandleTypeDef esp8266_handler = {0};

void esp8266_uart_init(uint32_t baudrate)
{
    esp8266_handler.Instance = USART2;
    esp8266_handler.Init.BaudRate = baudrate;                 //波特率
    esp8266_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    esp8266_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    esp8266_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    esp8266_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    esp8266_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&esp8266_handler);
}


void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)
    if(__HAL_UART_GET_FLAG(&esp8266_handler , UART_FLAG_RXNE) != RESET)
    {
        //接收数据长度大于缓冲区大小--清零
        if(esp8266_cnt >= sizeof(esp8266_rx_buf))
            esp8266_cnt = 0;
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&esp8266_handler , &receive_data , 1 , 1000);   //一个字节一个字节接收
        esp8266_rx_buf[esp8266_cnt++] = receive_data;      //接收数据,esp8266_cnt接收数据长度
        
        //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
        //HAL_UART_Transmit(&esp8266_handler , &receiv_data , 1 , 1000);
    }
}

uint8_t esp8266_wait_receive(void)
{
    //若接收长度为0 
    if(esp8266_cnt == 0)
        return ESP8266_ERROR;
    
    //无数据传入
    if(esp8266_cnt == esp8266_cntPre)
    {
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //正在传入数据
    esp8266_cntPre = esp8266_cnt;
    return ESP8266_ERROR;
}

void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf , 0 , sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK)        //数据接收完整
    {
        printf("esp8266 recv: %s\r\n", esp8266_rx_buf);  //使用串口1打印--printf
        esp8266_rx_clear();
    }
}

void esp8266_send_data(char *fmt , ...)
{
    va_list ap;
    uint16_t len;
    
    va_start(ap , fmt);
    vsprintf((char *)esp8266_tx_buf , fmt , ap);
    va_end(ap);
    
    len = strlen((const char *)esp8266_tx_buf);
    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_UART_Transmit(&esp8266_handler , esp8266_tx_buf , len , 100);
}

uint8_t esp8266_send_command(char *cmd , char *res)
{
    uint8_t time_out = 250;
    
    esp8266_rx_clear();
    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_UART_Transmit(&esp8266_handler , (uint8_t *)cmd , strlen(cmd) , 100);
    
    while(time_out--)
    {
        if(esp8266_wait_receive() == ESP8266_EOK)
        {
            if(strstr((const char *)esp8266_rx_buf , res) != NULL)
                return ESP8266_EOK;
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\r\n" , "OK");
}

uint8_t esp8266_set_mode(uint8_t mode)
{
    switch(mode)
    {
        case ESP8266_STA_MODE:
            return esp8266_send_command("AT+CWMODE=1\r\n" , "OK");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n" , "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n" , "OK");
        
        default:
            return ESP8266_EINVAL;
    }
}

uint8_t esp8266_join_ap(char * ssid , char *pwd)
{
    char cmd[64];
    sprintf(cmd , "AT+CWJAP=\"%s\" , \"%s\"\r\n" , ssid , pwd);
    return esp8266_send_command(cmd , "WIFI GOT IP");
}

uint8_t esp8266_connection_mode(uint8_t mode)
{
    char cmd[64];
    sprintf(cmd , "AT+CIPMUX=%d\r\n" , mode);
    return esp8266_send_command(cmd , "OK");
}

//连接服务器
uint8_t esp8266_connect_tcp_server(char *server_ip , char *server_port)
{
    char cmd[64];
    sprintf(cmd , "AT+CIPSTART=\"TCP\" , \"%s\" , %s\r\n" , server_ip , server_port);
    return esp8266_send_command(cmd , "CONNECT");
}

uint8_t esp8266_enter_unvarnished(void)
{
    uint8_t ret;
    ret = esp8266_send_command("AT+CIPMODE=1\r\n" , "OK");        // 开启透传模式
    ret += esp8266_send_command("AT+CIPSEND\r\n" , ">");          // 开始发送数据
    if(ret == ESP8266_EOK)
        return ESP8266_EOK;
    else
        return ESP8266_ERROR;
}

//建立TCPserver
uint8_t esp8266_build_tcp_server(void)
{
    
    return esp8266_send_command("AT+CIPSERVE=1\r\n" , "OK");
}

void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    esp8266_uart_init(baudrate);            //串口初始化
    
    //esp8266其他初始化
    printf("1. 测试esp8266是否存在...\r\n");
    while(esp8266_at_test())
        delay_ms(500);
    
    printf("2. 设置工作模式为STA...\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE))
        delay_ms(500);
    printf("3. 设置单路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION))
        delay_ms(500);
    printf("4. 链接wifi,SSID:%s , PWD:%s\r\n",WIFI_SSID , WIFI_PWD);
    while(esp8266_join_ap(WIFI_SSID , WIFI_PWD))
        delay_ms(1500);
    
    printf("5. 连接云服务器,server_ip:%s , server_port:%s\r\n",TCP_SERVER_IP , TCP_SERVER_PORT);
    while(esp8266_connect_tcp_server(TCP_SERVER_IP , TCP_SERVER_PORT));
        delay_ms(500);
    
    printf("6. 进入透传模式...\r\n");
    while(esp8266_enter_unvarnished())
        delay_ms(500);
    
    
    printf("ESP8266已连接TCP服务器并进入透传模式!\r\n");
    printf("ESP8266初始化完成!\r\n");
}


void esp8266_test(void)
{
    esp8266_send_data("This is from esp8266\r\n");
    esp8266_receive_data();
}

12.3.7 ESP8266 作为服务器

####################################### esp8266.c #######################################
#include "esp8266.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"
#include "stdarg.h"

uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE];
uint8_t esp8266_tx_buf[ESP8266_RX_BUF_SIZE];

uint16_t esp8266_cnt = 0 , esp8266_cntPre = 0;

UART_HandleTypeDef esp8266_handler = {0};

void esp8266_uart_init(uint32_t baudrate)
{
    esp8266_handler.Instance = USART2;
    esp8266_handler.Init.BaudRate = baudrate;                 //波特率
    esp8266_handler.Init.WordLength = UART_WORDLENGTH_8B;     //字长
    esp8266_handler.Init.StopBits = UART_STOPBITS_1;          //停止位(1个)
    esp8266_handler.Init.Parity = UART_PARITY_NONE;           //校验位(无校验位)
    esp8266_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流设置
    esp8266_handler.Init.Mode = UART_MODE_TX_RX;              //模式(双工)
    
    //HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
    HAL_UART_Init(&esp8266_handler);
}


void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;
    //__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)
    if(__HAL_UART_GET_FLAG(&esp8266_handler , UART_FLAG_RXNE) != RESET)
    {
        //接收数据长度大于缓冲区大小--清零
        if(esp8266_cnt >= sizeof(esp8266_rx_buf))
            esp8266_cnt = 0;
        //HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
        HAL_UART_Receive(&esp8266_handler , &receive_data , 1 , 1000);   //一个字节一个字节接收
        esp8266_rx_buf[esp8266_cnt++] = receive_data;      //接收数据,esp8266_cnt接收数据长度
        
        //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
        //HAL_UART_Transmit(&esp8266_handler , &receiv_data , 1 , 1000);
    }
}

uint8_t esp8266_wait_receive(void)
{
    //若接收长度为0 
    if(esp8266_cnt == 0)
        return ESP8266_ERROR;
    
    //无数据传入
    if(esp8266_cnt == esp8266_cntPre)
    {
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //正在传入数据
    esp8266_cntPre = esp8266_cnt;
    return ESP8266_ERROR;
}

void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf , 0 , sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK)        //数据接收完整
    {
        printf("esp8266 recv: %s\r\n", esp8266_rx_buf);  //使用串口1打印--printf
        esp8266_rx_clear();
    }
}

void esp8266_send_data(char *fmt , ...)
{
    va_list ap;
    uint16_t len;
    
    va_start(ap , fmt);
    vsprintf((char *)esp8266_tx_buf , fmt , ap);
    va_end(ap);
    
    len = strlen((const char *)esp8266_tx_buf);
    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_UART_Transmit(&esp8266_handler , esp8266_tx_buf , len , 100);
}

uint8_t esp8266_send_command(char *cmd , char *res)
{
    uint8_t time_out = 250;
    
    esp8266_rx_clear();
    //HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_UART_Transmit(&esp8266_handler , (uint8_t *)cmd , strlen(cmd) , 100);
    
    while(time_out--)
    {
        if(esp8266_wait_receive() == ESP8266_EOK)
        {
            if(strstr((const char *)esp8266_rx_buf , res) != NULL)
                return ESP8266_EOK;
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\r\n" , "OK");
}

uint8_t esp8266_set_mode(uint8_t mode)
{
    switch(mode)
    {
        case ESP8266_STA_MODE:
            return esp8266_send_command("AT+CWMODE=1\r\n" , "OK");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n" , "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n" , "OK");
        
        default:
            return ESP8266_EINVAL;
    }
}

uint8_t esp8266_join_ap(char * ssid , char *pwd)
{
    char cmd[64];
    sprintf(cmd , "AT+CWJAP=\"%s\" , \"%s\"\r\n" , ssid , pwd);
    return esp8266_send_command(cmd , "WIFI GOT IP");
}

uint8_t esp8266_connection_mode(uint8_t mode)
{
    char cmd[64];
    sprintf(cmd , "AT+CIPMUX=%d\r\n" , mode);
    return esp8266_send_command(cmd , "OK");
}

//连接服务器
uint8_t esp8266_connect_tcp_server(char *server_ip , char *server_port)
{
    char cmd[64];
    sprintf(cmd , "AT+CIPSTART=\"TCP\" , \"%s\" , %s\r\n" , server_ip , server_port);
    return esp8266_send_command(cmd , "CONNECT");
}

uint8_t esp8266_enter_unvarnished(void)
{
    uint8_t ret;
    ret = esp8266_send_command("AT+CIPMODE=1\r\n" , "OK");        // 开启透传模式
    ret += esp8266_send_command("AT+CIPSEND\r\n" , ">");          // 开始发送数据
    if(ret == ESP8266_EOK)
        return ESP8266_EOK;
    else
        return ESP8266_ERROR;
}

//建立TCPserver
uint8_t esp8266_build_tcp_server(void)
{
    
    return esp8266_send_command("AT+CIPSERVE=1\r\n" , "OK");
}

void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    esp8266_uart_init(baudrate);            //串口初始化
    
    //esp8266其他初始化
    printf("1. 测试esp8266是否存在...\r\n");
    while(esp8266_at_test())
        delay_ms(500);
    
    printf("2. 设置工作模式为AP...\r\n");
    while(esp8266_set_mode(ESP8266_AP_MODE))
        delay_ms(500);
    
    printf("3. 设置多路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_MULTI_CONNECTION))
        delay_ms(500);
    
    printf("4. 建立TCP服务器...\r\n");
    while(esp8266_build_tcp_server())
        delay_ms(500);
    
//    printf("2. 设置工作模式为STA...\r\n");
//    while(esp8266_set_mode(ESP8266_STA_MODE))
//        delay_ms(500);
//    printf("3. 设置单路链接模式...\r\n");
//    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION))
//        delay_ms(500);
//    printf("4. 链接wifi,SSID:%s , PWD:%s\r\n",WIFI_SSID , WIFI_PWD);
//    while(esp8266_join_ap(WIFI_SSID , WIFI_PWD))
//        delay_ms(1500);
//    
//    printf("5. 连接云服务器,server_ip:%s , server_port:%s\r\n",TCP_SERVER_IP , TCP_SERVER_PORT);
//    while(esp8266_connect_tcp_server(TCP_SERVER_IP , TCP_SERVER_PORT));
//        delay_ms(500);
//    
//    printf("6. 进入透传模式...\r\n");
//    while(esp8266_enter_unvarnished())
//        delay_ms(500);
    
    
    printf("ESP8266已连接TCP服务器并进入透传模式!\r\n");
    printf("ESP8266初始化完成!\r\n");
}


void esp8266_test(void)
{
    esp8266_send_data("This is from esp8266\r\n");
    esp8266_receive_data();
}

12.4 WI-FI遥控风扇项目

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小强子!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值