STM32HAL库快速入门教程——常用外设学习(2)
目录
一、STM32HAL库开发(8)——CubeMX配置DMA
1.1、什么是DMA?
1.2、内存内存之间的传输(单次)
编辑
1.3、内存外设之间的传输(ADC)
二、STM32HAL库开发(9)——CubeMX配置RTC
2.1、RTC实时重要一环——BKP 寄存器
2.2、RTC实时时钟
三、STM32HAL库开发(10)——单片机工作模式
3.1、低功耗睡眠模式(Sleep)
3.2、低功耗停止模式(Stop)
3.3、低功耗待机模式(StandBy)
四、STM32HAL库开发(11)——看门狗
4.1、独立看门狗
4.2、窗口看门狗
前言:前面已经更新了7个常用外设了,所以这个文章就从8开始了!
一、STM32HAL库开发(8)——CubeMX配置DMA
1.1、什么是DMA?
DMA(Direct Memory Access),即直接存储器访问。 DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
STM32F103C8T6内部有 2 个 DMA 控制器(DMA2 仅存大容量产品中), DMA1 有 7 个通道。DMA2 有 5 个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。
众所周知,DMA中有另种模式,①内存内存之间,②内存外设之间
内存很多外设之间都有,比如:
还有更多的可以去看芯片手册!
② 通道:DMA 具有 12 个独立可编程的通道,其中 DMA1 有 7 个通道, DMA2 有 5 个通道,每个通道对应不同的外设的 DMA 请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。
所以为什么同一时间只能接收一个通道呢?假如一次性有两个通道要转换,谁先谁后呢?为什么这么有规则呢?是因为有着一个大哥管理着:仲裁器
③ 仲裁器:当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器管理谁优先执行。
1.2、内存内存之间的传输(单次)
实验现象:
声明两个数组uint8_t DataA[]={1,2,3,4};uint8_t DataB[]={0,0,0,0};,刚开始显示1234和0000,经过DMA转运后,显示1234和1234!(将DataA的值传输给了DataB)
CubeMX配置:
byte:字节,通用8位,与u8相同
word:字长,与硬件的位数相同,STM32是32位,所以对应是u32
Half Word:半个字长,所以对应是u16
代码实现:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
OLED_Init();//OLED初始化
OLED_CLS();
//显示转运前数据DataA、DataB
uint8_t DataA[]={1,2,3,4};
uint8_t DataB[]={0,0,0,0};
OLED_ShowStr(0,0,"DataA:",2); //显示字符串
OLED_ShowStr(0,10,"DataB:",2); //显示字符串
OLED_ShowNum(50, 0, DataA[0], 1, 16);
OLED_ShowNum(60, 0, DataA[1], 1, 16);
OLED_ShowNum(70, 0, DataA[2], 1, 16);
OLED_ShowNum(80, 0, DataA[3], 1, 16);
OLED_ShowNum(50, 10, DataB[0], 1, 16);
OLED_ShowNum(60, 10, DataB[1], 1, 16);
OLED_ShowNum(70, 10, DataB[2], 1, 16);
OLED_ShowNum(80, 10, DataB[3], 1, 16);
// 启动DMA传输
HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)&DataA,(uint32_t)&DataB, 4);
// 等待传输完成
HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel1,HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
HAL_Delay(1000);//延时显示,方便观看数字变化
OLED_ShowStr(0,0,"DataA:",2); //显示字符串
OLED_ShowStr(0,10,"DataB:",2); //显示字符串
OLED_ShowNum(50, 0, DataA[0], 1, 16);
OLED_ShowNum(60, 0, DataA[1], 1, 16);
OLED_ShowNum(70, 0, DataA[2], 1, 16);
OLED_ShowNum(80, 0, DataA[3], 1, 16);
OLED_ShowNum(50, 10, DataB[0], 1, 16);
OLED_ShowNum(60, 10, DataB[1], 1, 16);
OLED_ShowNum(70, 10, DataB[2], 1, 16);
OLED_ShowNum(80, 10, DataB[3], 1, 16);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
现象:
1.3、内存外设之间的传输(ADC)
前面讲解ADC的时候已经演示过了:STM32HAL库入门教程——常用外设学习(1)
二、STM32HAL库开发(9)——CubeMX配置RTC
2.1、RTC实时重要一环——BKP 寄存器
BKP寄存器简介
BKP(Backup Registers)备份寄存器
BKP可用于存储用户应用程序数据。当VDD(2.03.6V)电源被切断,他们仍然由VBAT(1.83.6V)维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时他们也不会被复位
TAMPER引脚产生的侵入事件将所有备份寄存器内容清除
RTC引脚输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲
存储RTC时钟校准寄存器
用户数据存储容量:
20字节(中容量和小容量)/ 84字节(大容量和互联型)
小实验将要实现的现象:单片机掉电后,不会遗忘BKP寄存器中的数值!
硬件连接:(需要额外供电给VB引脚)
第一次先写:
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,100);
uint32_t Ret= HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1);//断电后靠VBAT供电OLED_ShowNum(0, 0, Ret, 4, 12);
运行后,第二次注释掉HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,100);并且把单片机供电拔了,继续运行,按道理说,这个Ret数值就会随着单片机断电变成0,但是当VDD电源被切断,他仍然由VBAT维持供电!所以数值会维持不变!
CubeMX配置简单使用BKP寄存器:
BKP 寄存器通常与 RTC 一起使用,在CubeIDE中假如需要使用BKP寄存器,需要先打开RTC
代码实现:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
OLED_Init();//OLED初始化
OLED_CLS();
// HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,100);//写BKP
uint32_t Ret= HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1);//断电后靠VBAT供电。读BKP
OLED_ShowNum(0, 0, Ret, 4, 12);//显示数值
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
2.2、RTC实时时钟
CubeMX配置:
1、打开RTC并设置时间


int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RTC_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
RTC_TimeTypeDef RTC_Time ;//时分秒
RTC_DateTypeDef RTC_Date; //年月日
OLED_Init();
OLED_CLS();
OLED_ShowStr(0, 0, "Data:20 - -", 2);
OLED_ShowStr(0, 2, "Time:", 2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_RTC_GetDate(&hrtc, &RTC_Date, RTC_FORMAT_BIN);
HAL_RTC_GetTime(&hrtc,&RTC_Time, RTC_FORMAT_BIN);
OLED_ShowNum(57, 0, RTC_Date.Year, 2, 16);
OLED_ShowNum(78, 0, RTC_Date.Month, 2, 16);
OLED_ShowNum(100, 0, RTC_Date.Date, 2, 16);
OLED_ShowNum(57, 2, RTC_Time.Hours , 2, 16);
OLED_ShowNum(78, 2, RTC_Time.Minutes, 2, 16);
OLED_ShowNum(100, 2, RTC_Time.Seconds, 2, 16);
}
/* USER CODE END 3 */
}
效果:
三、STM32HAL库开发(10)——单片机工作模式
3.1、低功耗睡眠模式(Sleep)
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);
打开睡眠模式。由于Hal库与标准库在管理系统时钟和低功耗模式时的处理方式不同,Hal库中需要多手动关闭SysTick定时器,因为SysTick定时器会周期性触发中断,会唤醒睡眠模式。
HAL_SuspendTick();//关闭SysTick定时器
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);
HAL_ResumeTick();//恢复SysTick定时器
3.2、低功耗停止模式(Stop)
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON,PWR_STOPENTRY_WFI);//进入Stop模式
SystemClock_Config();//恢复时钟
3.3、低功耗待机模式(StandBy)

HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
while(1)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnterSTANDBYMode();
}
四、STM32HAL库开发(11)——看门狗
4.1、独立看门狗

HAL_IWDG_Refresh(&hiwdg);//初始化完成后执行一次喂狗
//获取当前的复位是IWDG造成的复位还是按Rst键造成的复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
{
// IWDG reset flag is set
OLED_ShowString(2, 1, "IWDGRST"); //OLED闪烁IWDGRST字符串
HAL_Delay(500);
OLED_ShowString(2, 1, " ");
HAL_Delay(100);
__HAL_RCC_CLEAR_RESET_FLAGS();
}
else
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符串
HAL_Delay(500);
OLED_ShowString(3, 1, " ");
HAL_Delay(100);
}
while (1)
{
HAL_IWDG_Refresh(&hiwdg);//喂狗
HAL_Delay(1100);
}
4.2、窗口看门狗
代码:
//获取当前的复位是WWDG造成的复位还是按Rst键复位,这里代码有大耗时,需要在
MX_WWDG_Init(); 之前
if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET)
{
// IWDG reset flag is set
OLED_ShowString(2, 1, "WWDGRST"); //OLED闪烁IWDGRST字符
HAL_Delay(500);
OLED_ShowString(2, 1, " ");
HAL_Delay(100);
__HAL_RCC_CLEAR_RESET_FLAGS();
}
else
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符
HAL_Delay(500);
OLED_ShowString(3, 1, " ");
HAL_Delay(100);
}
MX_WWDG_Init();
while (1)
{
HAL_Delay(40);
HAL_WWDG_Refresh(&hwwdg);//喂狗
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);//开
}