騰訊企點app下載安裝關鍵詞優(yōu)化公司電話
1. 獨立看門狗IWDG介紹(341.45)
什么是看門狗?
- 在由單片機構成的微型計算機系統(tǒng)中,由于單片機的工作常常會受到來自外界電磁場的干擾,造成程序的跑飛,而陷入死循環(huán),程序的正常運行被打斷,由單片機控制的系統(tǒng)無法繼續(xù)工作,會造成整個系統(tǒng)的陷入停滯狀態(tài),發(fā)生不可預料的后果,所以出于對單片機運行狀態(tài)進行實時監(jiān)測的考慮,便產(chǎn)生了一種專門用于監(jiān)測單片機程序運行狀態(tài)的模塊或者芯片,俗稱“看門狗”(watchdog)。
- 獨立看門狗工作在主程序之外,能夠完全獨立工作,它的時鐘是專用的低速時鐘(LSI),由 VDD 電壓供電, 在停止模式和待機模式下仍能工作。
獨立看門狗本質(zhì)
- 本質(zhì)是一個 12 位的遞減計數(shù)器,當計數(shù)器的值從某個值一直減到 0 的時候,系統(tǒng)就會產(chǎn)生一個復位信號,即 IWDG_RESET 。
- 如果在計數(shù)沒減到 0 之前,刷新了計數(shù)器的值的話,那么就不會產(chǎn)生復位信號,這個動作就是我們經(jīng)常說的喂狗。
獨立看門狗框圖
獨立看門狗時鐘
- 獨立看門狗的時鐘由獨立的 RC 振蕩器 LSI 提供,即使主時鐘發(fā)生故障它仍然有效,非常獨立。啟用 IWDG 后,LSI 時鐘會自動開啟(不能主動停止,除非重置/重啟)。LSI 時鐘頻率并不精確,F1 用 40kHz。
- LSI 經(jīng)過一個 8 位的預分頻器得到計數(shù)器時鐘。
- 分頻系數(shù)算法:(prer:0–8 是IWDG_PR 的值)
重裝載寄存器
- 重裝載寄存器是一個 32 位的寄存器,用于存放重裝載值,低 12 位有效,即最大值為 4096,這個值的大小決定著獨立看門狗的溢出時間。
鍵寄存器
- 鍵寄存器IWDG_KR可以說是獨立看門狗的一個控制寄存器,主要有三種控制方式,往這個寄存器寫入下面三個不同的值有不同的效果。
溢出時間計算公式(RLR:計數(shù)多少次)
2. 獨立看門狗實驗(342.46)
- 需求: 開啟獨立看門狗,溢出時間為 1 秒,使用按鍵1進行喂狗。
- 硬件接線:
- KEY1 – PA0
- UART1 – PA9/PA10
- 溢出時間計算(1000ms): PSC=64,RLR=625,f=40
- 編程實現(xiàn):
- 代碼(18.iwdg_test/MDK-ARM)
#include <string.h>
int main()
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_IWDG_Init();MX_USART1_UART_Init();HAL_UART_Transmit(&huart1, "程序啟動...\n", strlen("程序啟動...\n"), 100);while (1){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)//檢測到key1被按下時(低電平)HAL_IWDG_Refresh(&hiwdg);HAL_Delay(50);}
}
- STM32CubeMx工程配置
3. 窗口看門狗WWDG介紹(343.47)
什么是窗口看門狗?
- 窗口看門狗用于監(jiān)測單片機程序運行時效是否精準,主要檢測軟件異常,一般用于需要精準檢測程序運行時
間的場合。 - 窗口看門狗的本質(zhì)是一個能產(chǎn)生系統(tǒng)復位信號和提前喚醒中斷的6位計數(shù)器。
- 產(chǎn)生復位條件:
- 當遞減計數(shù)器值從 0x40 減到 0x3F 時復位(即T6位跳變到0)
- 計數(shù)器的值大于 W[6:0] 值時喂狗會復位。
- 產(chǎn)生中斷條件:
- 當遞減計數(shù)器等于 0x40 時可產(chǎn)生提前喚醒中斷 (EWI)。
- 在窗口期內(nèi)重裝載計數(shù)器的值,防止復位,也就是所謂的喂狗。
窗口看門狗工作原理
控制寄存器
配置寄存器
狀態(tài)寄存器
超時時間計算
- Tout 是 WWDG 超時時間(沒喂狗)
- Fwwdg 是 WWDG 的時鐘源頻率(最大36M)
- 4096 是 WWDG 固定的預分頻系數(shù)
- 2^WDGTB 是 WWDG_CFR 寄存器設置的預分頻系數(shù)值
- T[5:0] 是 WWDG 計數(shù)器低 6 位,最多 63
4. 窗口看門狗實驗(344.48)
- 需求: 開啟窗口看門狗,計數(shù)器值設置為 0X7F ,窗口值設置為 0X5F ,預分頻系數(shù)為 8 。程序啟動時點亮 LED1 ,
300ms 后熄滅。在提前喚醒中斷服務函數(shù)進行喂狗,同時翻轉 LED2 狀態(tài)。
- 硬件接線:
- LED1 – PB8
- LED2 – PB9
- 超時時間計算(ms): 預分頻系數(shù)=8,T[6:0]=127,W[6:0]=95,Fwwdg=36MHz=36000kHz
- WWDG配置:
- 代碼(19.wwdg_test/MDK-ARM)
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{HAL_WWDG_Refresh(hwwdg);//提前喚醒中斷:喂狗HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);//喂狗之后翻轉led的狀態(tài)
}
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);HAL_Delay(300);//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);MX_WWDG_Init();while (1){HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);HAL_Delay(40);}
}
5. 獨立看門狗和窗口看門狗異同點(345.49)
6. DMA介紹
什么是DMA?
- 令人頭禿的描述:
- DMA(Direct Memory Access,直接存儲器訪問) 提供在外設與內(nèi)存、存儲器和存儲器、外設與外設之間的高速數(shù)據(jù)傳輸使用。
- 它允許不同速度的硬件裝置來溝通,而不需要依賴于 CPU,在這個時間中,CPU 對于內(nèi)存的工作來說就無法使用。
- 簡單描述:就是一個數(shù)據(jù)搬運工
DMA的意義
- 代替 CPU 搬運數(shù)據(jù),為 CPU 減負。
- 數(shù)據(jù)搬運的工作比較耗時間;
- 數(shù)據(jù)搬運工作時效要求高(有數(shù)據(jù)來就要搬走);
- 沒啥技術含量(CPU 節(jié)約出來的時間可以處理更重要的事)。
搬運什么數(shù)據(jù)?
- 存儲器、外設
這里的外設指的是 spi、usart、iic、adc 等基于 APB1 、APB2 或 AHB 時鐘的外設,而這里的存儲器包括自身的閃存(flash)或者內(nèi)存(SRAM)以及外設的存儲設備都可以作為訪問地源或者目的。
- 三種搬運方式:
- 存儲器→存儲器(例如:復制某特別大的數(shù)據(jù) buf)
- 存儲器→外設 (例如:將某數(shù)據(jù) buf 寫入串口 TDR 寄存器)
- 外設→存儲器 (例如:將串口 RDR 寄存器寫入某數(shù)據(jù) buf)
存儲器→存儲器
存儲器→外設
外設→存儲器
DMA 控制器
- STM32F103 有 2 個 DMA 控制器,DMA1 有 7 個通道,DMA2 有 5 個通道。
- 一個通道每次只能搬運一個外設的數(shù)據(jù)。如果同時有多個外設的 DMA 請求,則按照優(yōu)先級進行響應。
- DMA1 有 7 個通道:
- DMA2 有 5 個通道:
DMA及通道的優(yōu)先級
- 優(yōu)先級管理采用軟件+硬件:
- 軟件: 每個通道的優(yōu)先級可以在 DMA_CCRx 寄存器中設置,有 4 個等級
最高級 > 高級 > 中級 > 低級 - 硬件: 如果 2 個請求,它們的軟件優(yōu)先級相同,則較低編號的通道比較高編號的通道有較高的優(yōu)先權。
比如:如果軟件優(yōu)先級相同,通道 2 優(yōu)先于通道 4
- 軟件: 每個通道的優(yōu)先級可以在 DMA_CCRx 寄存器中設置,有 4 個等級
DMA傳輸方式
- DMA_Mode_Normal(正常模式)
- 一次 DMA 數(shù)據(jù)傳輸完后,停止 DMA 傳送 ,也就是只傳輸一次
- DMA_Mode_Circular(循環(huán)傳輸模式)
當傳輸結束時,硬件自動會將傳輸數(shù)據(jù)量寄存器進行重裝,進行下一輪的數(shù)據(jù)傳輸。 也就是多次傳輸
模式
指針遞增模式
- 外設和存儲器指針在每次傳輸后可以自動向后遞增或保持常量。當設置為增量模式時,下一個要傳輸?shù)牡刂?br /> 將是前一個地址加上增量值。
7. DMA實驗1(內(nèi)存到內(nèi)存)
實驗要求和配置
- 使用 DMA 的方式將數(shù)組 A 的內(nèi)容復制到數(shù)組 B 中,搬運完之后將數(shù)組 B 的內(nèi)容打印到屏幕。
- STM32CubeMx工程配置
- 重定向 printf 的話記得將下面這個勾打開:
用到的庫函數(shù)
HAL_DMA_Start
HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress,
uint32_t DstAddress, uint32_t DataLength)
參數(shù)一:DMA_HandleTypeDef *hdma,DMA通道句柄
參數(shù)二:uint32_t SrcAddress,源內(nèi)存地址
參數(shù)三:uint32_t DstAddress,目標內(nèi)存地址
參數(shù)四:uint32_t DataLength,傳輸數(shù)據(jù)長度。注意:需要乘以sizeof(uint32_t)
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
__HAL_DMA_GET_FLAG
#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))
參數(shù)一:HANDLE,DMA通道句柄
參數(shù)二:FLAG,數(shù)據(jù)傳輸標志。DMA_FLAG_TCx表示數(shù)據(jù)傳輸完成標志
返回值:FLAG的值(SET/RESET)
代碼實現(xiàn)
- 開啟數(shù)據(jù)傳輸
- 等待數(shù)據(jù)傳輸完成
- 打印數(shù)組內(nèi)容
- 代碼(20.dma_test1/MDK-ARM)
#define BUF_SIZE 16
// 源數(shù)組
uint32_t srcBuf[BUF_SIZE] = {0x00000000,0x11111111,0x22222222,0x33333333,0x44444444,0x55555555,0x66666666,0x77777777,0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
};
// 目標數(shù)組
uint32_t desBuf[BUF_SIZE];
int fputc(int ch, FILE *f)
{ unsigned char temp[1]={ch};HAL_UART_Transmit(&huart1,temp,1,0xffff); return ch;
}
int main(void)
{int i = 0;HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();// 開啟數(shù)據(jù)傳輸HAL_DMA_Start(&hdma_memtomem_dma1_channel1,(uint32_t)srcBuf, (uint32_t)desBuf, sizeof(uint32_t) * BUF_SIZE);// 等待數(shù)據(jù)傳輸完成while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);// 打印數(shù)組內(nèi)容for (i = 0; i < BUF_SIZE; i++)printf("Buf[%d] = %x\r\n", i, desBuf[i]);//x大/小寫即輸出大/小寫
}
8. DMA實驗2(內(nèi)存到外設)
實驗要求和配置
- 使用DMA的方式將內(nèi)存數(shù)據(jù)搬運到串口1發(fā)送寄存器,同時閃爍LED1。
- STM32CubeMx工程配置
用到的庫函數(shù)
HAL_UART_Transmit_DMA
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData,
uint16_t Size)
參數(shù)一:UART_HandleTypeDef *huart,串口句柄
參數(shù)二:uint8_t *pData,待發(fā)送數(shù)據(jù)首地址
參數(shù)三:uint16_t Size,待發(fā)送數(shù)據(jù)長度
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
代碼實現(xiàn)
- 準備數(shù)據(jù)
- 將數(shù)據(jù)通過串口DMA發(fā)送
- 代碼(20.dma_test2/MDK-ARM)
#define BUF_SIZE 1000
// 待發(fā)送的數(shù)據(jù)
unsigned char sendBuf[BUF_SIZE];
int main(void)
{int i = 0;HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();// 準備數(shù)據(jù)for (i = 0; i < BUF_SIZE; i++)sendBuf[i] = 'B';// 將數(shù)據(jù)通過串口DMA發(fā)送HAL_UART_Transmit_DMA(&huart1, sendBuf, BUF_SIZE);while (1){HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);HAL_Delay(100);}
}
9. DMA實驗3(外設到內(nèi)存)
實驗要求和配置
- 使用 DMA 的方式將串口接收緩存寄存器的值搬運到內(nèi)存中,同時閃爍 LED1。
- STM32CubeMx工程配置
用到的庫函數(shù)
__HAL_UART_ENABLE
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U)
== UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) &
UART_IT_MASK)): \(((__INTERRUPT__) >> 28U)
== UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) &
UART_IT_MASK)): \((__HANDLE__)->Instance-
>CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
參數(shù)一:HANDLE,串口句柄
參數(shù)二:INTERRUPT,需要使能的中斷
返回值:無
2. HAL_UART_Receive_DMA
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData,
uint16_t Size)
參數(shù)一:UART_HandleTypeDef *huart,串口句柄
參數(shù)二:uint8_t *pData,接收緩存首地址
參數(shù)三:uint16_t Size,接收緩存長度
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
3. __HAL_UART_GET_FLAG
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &
(__FLAG__)) == (__FLAG__))
參數(shù)一:HANDLE,串口句柄
參數(shù)二:FLAG,需要查看的FLAG
返回值:FLAG的值
4. __HAL_UART_CLEAR_IDLEFLAG
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
參數(shù)一:HANDLE,串口句柄
返回值:無
5. HAL_UART_DMAStop
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
參數(shù)一:UART_HandleTypeDef *huart,串口句柄
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
6. __HAL_DMA_GET_COUNTER
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
參數(shù)一:HANDLE,串口句柄
返回值:未傳輸數(shù)據(jù)大小
代碼實現(xiàn)
- 如何判斷串口接收是否完成?如何知道串口收到數(shù)據(jù)的長度?
- 使用串口空閑中斷(IDLE)!
- 串口空閑時,觸發(fā)空閑中斷;
- 空閑中斷標志位由硬件置1,軟件清零
- 利用串口空閑中斷,可以用如下流程實現(xiàn)DMA控制的任意長數(shù)據(jù)接收:
- 使能 IDLE 空閑中斷;
- 使能 DMA 接收中斷;
- 收到串口接收中斷,DMA 不斷傳輸數(shù)據(jù)到緩沖區(qū);
- 一幀數(shù)據(jù)接收完畢,串口暫時空閑,觸發(fā)串口空閑中斷;
- 在中斷服務函數(shù)中,清除中斷標志位,關閉DMA傳輸(防止干擾);
- 計算剛才收到了多少個字節(jié)的數(shù)據(jù)。
- 處理緩沖區(qū)數(shù)據(jù),開啟DMA傳輸,開始下一幀接收。
- 代碼(20.dma_test3/MDK-ARM)
- 有三個文件需要修改:
- main.c
uint8_t rcvBuf[BUF_SIZE]; // 接收數(shù)據(jù)緩存數(shù)組
uint8_t rcvLen = 0; // 接收一幀數(shù)據(jù)的長度
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能IDLE空閑中斷
HAL_UART_Receive_DMA(&huart1,rcvBuf,100); // 使能DMA接收中斷
while (1)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);HAL_Delay(300);
}
- main.h
#define BUF_SIZE 100
- stm32f1xx_it.c
extern uint8_t rcvBuf[BUF_SIZE];
extern uint8_t rcvLen;
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET) // 判斷IDLE標志位是否被置位{__HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除標志位HAL_UART_DMAStop(&huart1); // 停止DMA傳輸,防止干擾uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx); rcvLen = BUF_SIZE - temp; //計算數(shù)據(jù)長度HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);//發(fā)送數(shù)據(jù)HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);//開啟DMA}/* USER CODE END USART1_IRQn 1 */
}