当前位置: 首页 > news >正文

【STM32开发】-基础开发笔记(STM32F103,HAL库开发)

一、软件使用

1.1、MDK5

        MDK5 由两个部分组成:MDK Core 和 Software Packs,MDK Core 又分成四个部分:uVision IDE with Editor(编辑器),ARMC/C++ Compiler(编译器),Pack Installer(包安装器),uVision Debugger with Trace(调试跟踪器);Software Packs(包安装器)又分为:Device(芯片支持),CMSIS(ARM Cortex 微控制器软件接口标准)和 Mdidleware(中间库)三个小部分。

安装包网上有很多资源,MDK5安装完后,还需要安装 STM32F1的器件支持包:Keil.STM32F1xx_DFP.1.0.5.pack。

1.2、STM32CubeF1

        STM32Cube 是 ST 提供的一套性能强大的免费开发工具和嵌入式软件模块,主要有两部分:

1、图形配置工具 STM32CubeMX;

2、嵌入式软件包(STM32Cube 库)。

经常会有人问,HAL库与标准库有什么区别,实际HAL 库和标准库本质上是一样的,都是提供底层硬件操作 API;

HAL 库在设计的时候更注重软硬件分离,HAL 库的目的应该就是尽量抽离物理层,HAL库的 API 集中关注各个外设的公共函数功能,以便定义通用性更好的 API 函数接口,从而具有更好的可移植性。HAL 库写的代码在不同的 STM32 产品上移植,非常方便,效率得到提升。
目前 HAL 库支持 STM32 各个系列产品。

1.3、新建工程文件说明

        在keil新建工程,保存后会生成如下目录:

Template.uvprojx:工程文件

DebugConfig:存储一些调试配置文件

Listing和Obiects:用来存储MDK编译过程的一些中间文件

将STM32CubeF1包里的源码文件复制到工程目录下:

1、复制Src和Inc文件复制到自己的工程目录下

Src存放的是固件库的.c文件,Inc存放的是对应的.h文件。

2、将startup_stm32f103xe.s复制到CORE目录下:

3、四个头文件:cmsis_armcc.h,cmsis_armclang.h,cmsis_compiler.h,core_cm3.h 同样复制到 CORE 目录下面

4、将3 个文件stm32f1xx.h,system_stm32f1xx.h 和 stm32f103xe.h 复制到 USER 目录之下

5、进入目录\STM32Cube_FW_F1_V1.8.0\Projects\STM3210E_EVAL\Templates 目录下,打开Inc目录,将目录下面的3个头文件stm32f1xx_it.h,stm32f1xx_hal_conf.h 和main.h全部复制到 USER 目录下面。然后我们打开 Src 目录,将下面的四个源文件 system_stm32f1xx.c,stm32f1xx_it.c, stm32f1xx_hal_msp.c 和 main.c 同样全部复制到 USER 目录下面。

让后在MDK上新建,文件移动后,项目结构如图:

1.4、关键部分学习

        1、startup_stm32f103xe.s 启动文件

STM32 系列所有芯片工程都会有一个.s 启动文件。对于不同型号的 stm32 芯片启动文件也
是不一样的。

        2、__weak修饰符

如果函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。

二、C语言基础知识学习

2.1、位操作

        C语言的常规操作:

运算符含义
&按位与
|按位或
~取反
<<左移
>>右移
^按位异或

在单片机开发中,这些运算符经常使用,下面来看看具体使用:

比如对寄存器的操作:

GPIOA->ODR &= 0XFFOF;//将第4-7位请0
GPIOA->ODR |= 0x0040;//设置对应位置的值

移位操作:

GPIOA->ODR|=1<<5;//将ODR寄存器的第5位设置为1,其他位的值不变

取反操作:

GPIOB->ODR=(uint16_t)~(1<<3);//将ODR寄存器的第3为设为0,其他位为1

2.2、宏定义

1、define宏定义

#define 标识符 字符串
#define IIC_VAL ((uint32_t)9600)//定义标识符IIC_VAL的值为9600

可以在代码中直接使用IIC_VAL标识符,而不用直接使用常量 9600,同时也很方便我们修改 IIC_VAL 的值。

2、#ifdef和if defined条件编译

#ifdef 标识符
程序段 1
#else
程序段 2
#endif

作用:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译,
否则编译程序段 2。 其中#else 部分也可以没有。

3、extern变量声明

 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示
编译器遇到此变量和函数时在其他模块中寻找其定义。

特别注意:extern声明变量可以很多次,但是变量的定义只有一个地方定义。

4、typedef类型声明

typedef 用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。

struct IIC
{uint32_t addr;uint32_t data;
};//定义结构体IIC//使用typedef
typedef struct
{uint32_t addr;uint32_t data;
}IIC_TypeDef;//Typedef 为结构体定义一个别名IIC_TypeDef,这样我们可以通过IIC_TypeDef来定义结构体
变量.

三、其他基础知识

3.1、关于端口复用

        GPIO如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。

复用的一般步骤:

1、打开对应IO时钟和复用功能外设时钟(GPIOA和USART1);

__HAL_RCC_GPIO_CLK_ENABLE();//使能GPIO时钟
__HAL_RCC_USART1_CLK_ENABLE();//使能USART1时钟
__HAL_RCC_AFIO_CLK_ENABLE();//时钟辅助功能IO时钟

2、在GPIOx_MODER寄存器中奖需要的IO口配置为复用功能;

3、配置IO的参数,比如上拉/下拉,输出速度等。

3.2、关于中断优先级

{ void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
NVIC_SetPriorityGrouping(PriorityGroup);
}__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
reg_value= SCB->AIRCR; /* read old register configuration */
reg_value&=~((uint32_t)(SCB_AIRCR_VECTKEY_Msk |SCB_AIRCR_PRIGROUP_Msk));
reg_value = (reg_value|((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp<< SCB_AIRCR_PRIGROUP_Pos) );
SCB->AIRCR = reg_value;
}

第一个是HAL库中中断优先级分组函数,这个函数在系统中只需要被调用一次,一旦分组确定就最好不要更改,否则容易造成程序分组混乱。

第二个是设置 SCB->AIRCR 寄存器的值来设置中断优先级,

四、常规开发使用

4.1、串口使用步骤

       1、串口初始化,使能串口

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);

    入口参数是huart,为 UART_HandleTypeDef 结构体指针类型,结构体参数如下:

//串口定义参数结构体
typedef struct
{USART_TypeDef       *Instance;    //基地址,比如串口1,则为USART1UART_InitTypeDef     Init;        uint8_t            *pTxBuffPtr;    //发送的数据缓冲指针uint16_t              TxXferSize;  //发送的数据量__IO uint16_t         TxXferCount; //还剩余的要发送的数据量uint8_t            *pRxBuffPtr;    //接收的数据缓冲指针uint16_t            RxXferSize;    //接收的最大数据量__IO uint16_t         RxXferCount; //还剩余要接收的数据量DMA_HandleTypeDef    *hdmatx;DMA_HandleTypeDef    *hdmarx;HAL_LockTypeDef        Lock;__IO HAL_UART_StateTypeDef gState;__IO HAL_UART_StateTypeDef RxState;__IO uint32_t ErrorCode;
}UART_HandleTypeDef;//UART初始化结构体
typedef struct
{uint32_t BaudRate;    //波特率uint32_t WordLength;    //字长uint32_t StopBits;    //停止位uint32_t Parity;    //奇偶校验uint32_t Mode;    //收/发模式设置uint32_t HwFlowCtl;//硬件流设置uint32_t OverSampling; //过采样设置
}UART_InitTypeDef//初始化一般格式,以USART1为例
UART_HandleTypeDef UART1_Handler; //UART 句柄
UART1_Handler.Instance=USART1;    //USART1
UART1_Handler.Init.BaudRate=115200;//波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1;//停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE;//无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX;//收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能 UART1

2、使能串口和时钟

__HAL_RCC_USART1_CLK_ENABLE();//使能 USART1 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();//使能 GPIOA 时钟

3、串口复用配置

复用映射在初始化时完成,一般在设置串口配置是设置,例如:

GPIO_Initure.Pin=GPIO_PIN_4;    //PA4
GPIO_Initure.Mode=GPIO_MODE_AF_PP;//复用推挽输出
//......其他配置

4、开启相关中断,配置串口中断的优先级

HAL库中常用的中断配置方法如下:

//使能中断
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);//开启接收完成中断
__HAL_UART_DISABLE_IT(huart,UART_IT_RXNE);//关闭接收完成中断
//中断优先级
HAL_NVIC_EnableIRQ(USART1_IRQn);//使能 USART1 中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3);//抢占优先级 3,子优先级 3

5、中断服务函数

发生中断时,程序就会执行中断服务函数,比如:

void USART1_IRQHandler(void) 
{HAL_UART_IRQHandler(&UART1_Handler); //调用 HAL 库中断处理公用函数…//中断处理完成后的结束工作
}

6、发送和接收和发送

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart,
uint8_t *pData, uint16_t Size, uint32_t Timeout);//发送数据函数
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart,
uint8_t *pData, uint16_t Size, uint32_t Timeout);//接收数据函数

使用HAL库可以直接调用相关函数。

4.2、关于独立看门狗和窗口看门狗

对于STM32,在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用看门狗,对于HAL库,有特定的函数:

1、取消寄存器写保护

HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);//对应结构体定义:
typedef struct
{IWDG_TypeDef *Instance;    //看门狗寄存器基地址IWDG_InitTypeDef Init;    
}IWDG_HandleTypeDef;//初始化结构体
typedef struct
{uint32_t Prescaler;    //看门狗预分频系数uint32_t Reload;    //重装载值
}IWDG_InitTypeDef;

2、重载计数值喂狗

HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);

把值0xAAAA写入到IWDG_KR寄存器,从而触发计数器重载,即实现独立看门狗的喂狗操作

3、启动看门狗

__HAL_IWDG_START(hiwdg);

对于窗口看门狗:

窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序
背离正常的运行序列而产生的软件故障。

1、使能WWDG时钟

WWDG 不同于 IWDG,IWDG 有自己独立的 40Khz 时钟,不存在使能问题。而 WWDG
使用的是 PCLK1 的时钟,需要先使能时钟。

__HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟

2、设置窗口值,分频数和计数器初始值

HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg);//WWDG_HandleTypeDef 结构体定义
typedef struct
{WWDG_TypeDef *Instance;WWDG_InitTypeDef Init;
}WWDG_HandleTypeDef;//WWDG_InitTypeDef 结构体类型
typedef struct
{uint32_t Prescaler;//预分频系数uint32_t Window;//窗口值uint32_t Counter;//计数器值uint32_t EWIMode ; //是否启用 WWDG 早期唤醒中断
}WWDG_InitTypeDef;

3、使能中断通道并配置优先级

HAL_NVIC_SetPriority(WWDG_IRQn,2,3);//抢占优先级 2,子优先级为 3
HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断

4、写对应的中断服务函数

通过该函数来喂狗,喂狗要快,否则当会引起软复位。

//中断服务函数
void WWDG_IRQHandler(void);
//喂狗
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg);

5、重写中断处理回调函数

void HAL_WWDG_EarlyWakeupCallback (WWDG_HandleTypeDef* hwwdg);

4.3、定时器中断使用(重要)

1、首先使能要使用的定时器

__HAL_RCC_TIM2_CLK_ENABLE(); //使能 TIM2 时钟

2、初始化定时器

HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);//TIM_HandleTypeDef 类型结构体
typedef struct
{TIM_TypeDef *Instance;            //寄存器基地址TIM_Base_InitTypeDef    Init;      //初始化HAL_TIM_ActiveChannel    Channel;    //通道DMA_HandleTypeDef    *hdma[7];    //DMA功能HAL_LockTypeDef Lock;            //状态标识符__IO HAL_TIM_StateTypeDef State;//状态标识符
}TIM_HandleTypeDef;//TIM_Base_InitTypeDef 结构体
typedef struct
{uint32_t Prescaler;//预分频系数uint32_t CounterMode; //计数方式uint32_t Period;//自动装载值 ARRuint32_t ClockDivision;//时钟分频因子uint32_t RepetitionCounter;
} TIM_Base_InitTypeDef;

3、使能定时器

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);

4、中断优先级设定

中断优先级配置我们都会放在该回调函数内部:

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(); //使能 TIM2 时钟HAL_NVIC_SetPriority(TIM2_IRQn,1,3);//设置中断优先级,抢占优先级 1,子优先级 3HAL_NVIC_EnableIRQ(TIM2_IRQn); //开启 ITM2 中断}
}

5、中断服务函数

void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);

如需回调函数,则使用需要重写该函数:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

来自正点原子视频学习总结。

http://www.dtcms.com/a/305025.html

相关文章:

  • 【ComfyUI学习笔记04】案例学习:局部重绘 - 上
  • 墨者:XPath注入漏洞实战
  • 第二十五节 MATLAB矩阵的加法和减法、除法(左,右)矩阵
  • Arduino声控RGB矩阵音乐节奏灯DIY全攻略
  • 解密数据结构之二叉树
  • Android11平台下rk3568的ATGM332D定位模块适配
  • 全志T507平台GPIO 控制(二)
  • OpenCV图像算数运算可莉版
  • bash命令创建新conda环境
  • Kubernetes自动扩容方案
  • 力扣-104. 二叉树的最大深度
  • Linux系统的虚拟控制台介绍(桌面卡死的拯救方案)
  • 深入探索爬虫与自动化脚本:释放效率的利器
  • 手写简易Spring框架
  • 万字详解——OSI七层模型:网络通信的完整架构解析
  • mysql 之多表
  • others-Facebook落地页自建归因逻辑
  • 如何快速把Clickhouse数据同步到Mysql
  • 解决百度网盘双击没反应打不开的问题
  • Element Plus常见基础组件(二)
  • 16大工程项目管理系统对比:开源与付费版本
  • 科研小tip3|Windows中的CompressAi下载与使用
  • leaflet中绘制轨迹线的大量轨迹点,解决大量 marker 绑定 tooltip 同时显示导致的性能问题
  • 机器学习-十大算法之一线性回归算法
  • 通用算法与深度学习基础
  • 机器学习课程介绍
  • 机器学习线性回归:从基础到实践的入门指南
  • 机器学习——线性回归(LinearRegression)
  • 出现错误,Microsoft store初始化失败。请尝试刷新或稍后返回。
  • 深入理解异或运算(XOR)及应用