使用hal庫函數通常會經常使用STM32cubeMX這個軟件進行一個配置會相對較爲簡單,軟件安裝在我的另一篇博客裏面有講解,其中引腳的配置可以看一下這位大神寫的:http://www.openedv.com/forum.php?mod=viewthread&tid=309468&highlight=hal%BF%E2
GPIO函數
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
功能: GPIO初始化
實例:HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
功能:在函數初始化之後的引腳恢復成默認的狀態,即各個寄存器復位時的值實例:HAL_GPIO_Init(GPIOC, GPIO_PIN_4);
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:讀取引腳的電平狀態、函數返回值爲0或1實例:HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4);
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
功能:引腳寫0或1實例:HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4,0);
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
翻轉引腳的電平狀態實例:HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_4); 常用在LED上
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:鎖住引腳電平,比如說一個管腳的當前狀態是1,當這個管腳電平變化時保持鎖定時的值。實例:HAL_GPIO_LockPin(GPIOC, GPIO_PIN_4);
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
功能: 外部中斷服務函數,清除中斷標誌位
實例:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
功能: 中斷回調函數,可以理解爲中斷函數具體要響應的動作。實例:HAL_GPIO_EXTI_Callback(GPIO_PIN_4);
uart的發送接收函數:
UART結構體定義
UART_HandleTypeDef huart1;
UART的名稱定義,這個結構體中存放了UART所有用到的功能,後面的別名就是我們所用的uart串口的別名,默認爲huart1
1、串口發送/接收函數
HAL_UART_Transmit();串口發送數據,使用超時管理機制
HAL_UART_Receive();串口接收數據,使用超時管理機制
HAL_UART_Transmit_IT();串口中斷模式發送
HAL_UART_Receive_IT();串口中斷模式接收
HAL_UART_Transmit_DMA();串口DMA模式發送
HAL_UART_Transmit_DMA();串口DMA模式接收
這幾個函數的參數基本都是一樣的,我們挑兩個講解一下串口發送數據:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口發送指定長度的數據。如果超時沒發送完成,則不再發送,返回超時標誌(HAL_TIMEOUT)。參數:
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
*pData 需要發送的數據
Size 發送的字節數
Timeout 最大發送時間,發送數據超過該時間退出發送
舉例: HAL_UART_Transmit(&huart1, (uint8_t *)ZZX, 3, 0xffff); //串口發送三個字節數據,最大傳輸時間0xffff
中斷接收數據:HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口中斷接收,以中斷方式接收指定長度數據。
大致過程是,設置數據存放位置,接收數據長度,然後使能串口接收中斷。接收到數據時,會觸發串口中斷。
再然後,串口中斷函數處理,直到接收到指定長度數據,而後關閉中斷,進入中斷接收回調函數,不再觸發接收中斷。(只觸發一次中斷)。如果使用該函數和回調函數則需要在串口初始化的時刻就進行一次調用開啓中斷參數:
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
*pData 接收到的數據存放地址
Size 接收的字節數
舉例: HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1); //中斷接收一個字符,存儲到value中
2、串口中斷函數
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中斷處理函數
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口發送中斷回調函數
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口發送一半中斷回調函數(用的較少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中斷回調函數
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回調函數(用的較少)
HAL_UART_ErrorCallback();串口接收錯誤函數
串口接收中斷回調函數:HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能:HAL庫的中斷進行完之後,並不會直接退出,而是會進入中斷回調函數中,用戶可以在其中設置代碼,串口中斷接收完成之後,會進入該函數,該函數爲空函數,用戶需自行修改,
參數:
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
舉例: HAL_UART_RxCpltCallback(&huart1){ //用戶設定的代碼 }
串口中斷處理函數HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能:對接收到的數據進行判斷和處理 判斷是發送中斷還是接收中斷,然後進行數據的發送和接收,在中斷服務函數中使用
如果接收數據,則會進行接收中斷處理函數
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
如果發送數據,則會進行發送中斷處理函數/* UART in mode Transmitter ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
3串口查詢函數HAL_UART_GetState(); 判斷UART的接收是否結束,或者發送數據是否忙碌
舉例:
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX) //檢測UART發送結束
重定義printf:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;}
重定義scanf:
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
原子的重定義printf:
int fputc (int ch,FILE *f)
{
while(USART2->SR(0X40) == 0);
USART2->DR = (uint8_t)ch;
return ch;
}
這是直接對寄存器操作。
定時器操作:
HAL_TIM_IRQHandler(&htim2);
定時器中斷處理函數 在stm32f4xx_it.c的 TIM2_IRQHandler()定時器中斷服務函數中這個函數的具體作用是判斷中斷是否正常,然後判斷產生的是哪一類定時器中斷(溢出中斷/PWM中斷.....),然後進入相應的中斷回調函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
在HAL庫中,每進行完一箇中斷,並不會立刻退出,而是會進入到中斷回調函數中,這裏我們是使用定時器溢出中斷回調函數
void TIM3_IRQHandler(void) 首先進入中斷函數
HAL_TIM_IRQHandler(&htim2);之後進入定時器中斷處理函數
判斷產生的是哪一類定時器中斷(溢出中斷/PWM中斷.....) 和定時器通道
void HAL_TIM_PeriodElapsedCallback(&htim2); 進入相對應中斷回調函數
在中斷回調函數中添加用戶代碼
你也可以在在stm32f1xx_it.c中找到中斷回調函數
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
pwm輸出:
SMT32F1系列共有8個定時器:
高級定時器(TIM1、TIM8);通用定時器(TIM2、TIM3、TIM4、TIM5);基本定時器(TIM6、TIM7)。
SMT32F4系列共有15個定時器:
高級定時器(TIM1、TIM8);通用定時器(TIM2、TIM3、TIM4、TIM5、TIM9~TIM14);基本定時器(TIM6、TIM7)。
PWM頻率:
Fpwm =Tclk / ((arr+1)*(psc+1))(單位:Hz)
- arr 是計數器值
- psc 是預分頻值
佔空比:
- duty circle = TIM3->CCR1 / arr(單位:%)
- TIM3->CCR1 用戶設定值
arr=499,TIM3->CCR1=250 則pwm的佔空比爲50%
使能TIM3的PWM Channel1 輸出。
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
修改佔空比:
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, pwmVal);
ADC:
STM32一共有3個ADC ADC1,ADC2,ADC3
F4的ADC支持12位,10位,8位和6位精度,F1只支持12位
F1和F4都具有3個ADC,F1可提供21個輸入通道,F4可以提供24個輸入通道。
F1的ADC最大采樣頻率爲1Msps,2路交替採樣可到2Msps(F1不支持3路交替採樣)。F4的ADC最大采樣頻率爲2.4Msps,3路交替採樣可到7.2Msps
在ADC初始化之後加上AD校準函數
MX_ADC1_Init(); HAL_ADCEx_Calibration_Start(&hadc1); //AD校準
在循環中:
HAL_ADC_Start(&hadc1); //啓動ADC轉換
HAL_ADC_PollForConversion(&hadc1, 50); //等待轉換完成,50爲最大等待時間,單位爲ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
ADC_Value = HAL_ADC_GetValue(&hadc1); //獲取AD值}中斷讀取:
如果使能了ADC轉換結束中斷,並且使能了定時器中斷,可以這樣寫:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定時器中斷回調
{
HAL_ADC_Start_IT(&hadc1); //定時器中斷裏面開啓ADC中斷轉換,1ms開啓一次採集
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) //ADC轉換完成回調
{
HAL_ADC_Stop_IT(&hadc1); //關閉ADC
HAL_TIM_Base_Stop_IT(&htim3); //關閉定時器
AD_Value=HAL_ADC_GetValue(&hadc1); //獲取ADC轉換的值printf("ADC1 Reading : %d \r\n",AD_Value);
printf("%.4f V\r\n",(AD_Value*3.3/4096)); //串口打印電壓信息
HAL_TIM_Base_Start_IT(&htim3); //開啓定時器
}開啓ADC 3種模式 ( 輪詢模式 中斷模式 DMA模式 )
• HAL_ADC_Start(&hadcx); //輪詢模式開啓ADC
• HAL_ADC_Start_IT(&hadcx); //中斷輪詢模式開啓ADC
• HAL_ADC_Start_DMA(&hadcx); //DMA模式開啓ADC關閉ADC 3種模式 ( 輪詢模式 中斷模式 DMA模式 )
• HAL_ADC_Stop()
• HAL_ADC_Stop_IT()
• HAL_ADC_Stop_DMA()ADC校準函數 :
• HAL_ADCEx_Calibration_Start(&hadcx);
F4系列不支持
讀取ADC轉換值
• HAL_ADC_GetValue()
等待轉換結束函數
• HAL_ADC_PollForConversion(&hadc1, 50);
第一個參數爲那個ADC,第二個參數爲最大等待時間
ADC中斷回調函數
• HAL_ADC_ConvCpltCallback()轉換完成後回調,DMA模式下DMA傳輸完成後調用
規則通道及看門狗配置
• HAL_ADC_ConfigChannel() 配置規則組通道
• HAL_ADC_AnalogWDGConfig()
DAC:
/* IO operation functions *****************************************************/
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel); //開啓DAC輸出
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef* hdac, uint32_t Channel); //關閉DAC輸出
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t* pData, uint32_t Length, uint32_t Alignment); //需要函數中不斷開啓 //開啓DAC的DMA輸出
HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel); //關閉DAC的DMA輸出
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data); //設置DAC輸出值
uint32_t HAL_DAC_GetValue(DAC_HandleTypeDef* hdac, uint32_t Channel); //獲取DAC輸出值在main()主函數中設置DAC輸出的數據爲12位右對齊,DAC輸出爲2048,並使能DAC1輸出通道
/* USER CODE BEGIN 2 */
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
/* USER CODE END 2 */HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);
功能:設置DAC的輸出值
參數一: DAC結構體名
參數二: 設置DAC通道
參數三: 設置DAC對齊方式
參數四: 設置輸出電壓值 12位最大位4095HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
功能:開啓DAC輸出
參數一: DAC結構體名
參數二: DAC通道
DMA:
DMA傳輸將數據從一個地址空間複製到另一個地址空間,提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。不經過cpu.
DMA用來提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。無須CPU的干預,通過DMA數據可以快速地移動。這就節省了CPU的資源來做其他操作。
串口DMA發送數據:
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
1
功能:串口通過DMA發送指定長度的數據。參數:
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
*pData 需要發送的數據
Size 發送的字節數
舉例:HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff)); //串口發送Senbuff數組
1
串口DMA接收數據:HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
1
功能:串口通過DMA接受指定長度的數據。參數:
UART_HandleTypeDef *huart UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
*pData 需要存放接收數據的數組
Size 接受的字節數
舉例:HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Recbuff, sizeof(Recbuff)); //串口發送Senbuff數組
1
串口DMA恢複函數HAL_UART_DMAResume(&huart1)
1
作用: 恢復DMA的傳輸返回值: 0 正在恢復 1 完成DMA恢復
IIC:
IIC一共有隻有兩個總線: 一條是雙向的數據線SDA,一條是串行時鐘線SCL
所有接到I2C總線設備上的串行數據SDA都接到總線的SDA上,各設備的時鐘線SCL接到總線的SCL上。I2C總線上的每一個設備都對應一個唯一的地址。
- 起始信號:SCL保持高電平,SDA由高電平變爲低電平後,延時(>4.7us),SCL變爲低電平。
- 停止信號:SCL保持高電平。SDA由低電平變爲高電平。
IIC信號在數據傳輸過程中,當SCL=1高電平時,數據線SDA必須保持穩定狀態,不允許有電平跳變,只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才允許變化。
SCL=1時 數據線SDA的任何電平變換會看做是總線的起始信號或者停止信號。
也就是在IIC傳輸數據的過程中,SCL時鐘線會頻繁的轉換電平,以保證數據的傳輸
每當主機向從機發送完一個字節的數據,主機總是需要等待從機給出一個應答信號,以確認從機是否成功接收到了數據,
應答信號:主機SCL拉高,讀取從機SDA的電平,爲低電平表示產生應答
應答信號爲低電平時,規定爲有效應答位(ACK,簡稱應答位),表示接收器已經成功地接收了該字節;
應答信號爲高電平時,規定爲非應答位(NACK),一般表示接收器接收該字節沒有成功。
時序操作:MCU先發送一個開始信號(START)啓動總線
接着跟上首字節,發送器件寫操作地址(DEVICE ADDRESS)+寫數據(0xA0)
等待應答信號(ACK)
發送數據的存儲地址。24C02一共有256個字節的存儲空間,地址從0x00~0xFF,想把數據存儲>在哪個位置,此刻寫的就是哪個地址。
發送要存儲的數據第一字節、第二字節、…注意在寫數據的過程中,E2PROM每個字節都會>迴應一個“應答位0”,老告訴我們寫E2PROM數據成功,如果沒有迴應答位,說明寫入不成功。
發送結束信號(STOP)停止總線注意:
在寫數據的過程中,每成功寫入一個字節,E2PROM存儲空間的地址就會自動加1,當加到0xFF後,再寫一個字節,地址就會溢出又變成0x00。寫數據的時候需要注意,E2PROM是先寫到緩衝區,然後再“搬運到”到掉電非易失區。所以這個過程需要一定的時間,AT24C02這個過程是不超過5ms!
所以,當我們在寫多個字節時,寫入一個字節之後,再寫入下一個字節之前,必須延時5ms纔可以IIC寫函數
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
1
功能:IIC寫數據
參數:*hi2c 設置使用的是那個IIC 例:&hi2c2
DevAddress 寫入的地址 設置寫入數據的地址 例 0xA0
*pData 需要寫入的數據
Size 要發送的字節數
Timeout 最大傳輸時間,超過傳輸時間將自動退出傳輸函數
IIC讀函數HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
1
功能:IIC讀一個字節
參數:*hi2c: 設置使用的是那個IIC 例:&hi2c2
DevAddress: 寫入的地址 設置寫入數據的地址 例 0xA0
*pDat:a 存儲讀取到的數據
Size: 發送的字節數
Timeout: 最大讀取時間,超過時間將自動退出讀取函數
舉例:
HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;;
1
發送兩個字節數據IIC寫數據函數
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/* 第1個參數爲I2C操作句柄
第2個參數爲從機設備地址
第3個參數爲從機寄存器地址
第4個參數爲從機寄存器地址長度
第5個參數爲發送的數據的起始地址
第6個參數爲傳輸數據的大小
第7個參數爲操作超時時間 */
功能: IIC寫多個數據 該函數適用於IIC外設裏面還有子地址寄存器的設備,比方說E2PROM,除了設備地址,每個存儲字節都有其對應的地址參數:
*hi2c: I2C設備號指針,設置使用的是那個IIC 例:&hi2c2
DevAddress: 從設備地址 從設備的IIC地址 例E2PROM的設備地址 0xA0
MemAddress: 從機寄存器地址 ,每寫入一個字節數據,地址就會自動+1
MemAddSize: 從機寄存器地址字節長度 8位或16位
寫入數據的字節類型 8位還是16位
I2C_MEMADD_SIZE_8BIT
I2C_MEMADD_SIZE_16BIT
在stm32f1xx_hal_i2c.h中有定義*pData: 需要寫入的的數據的起始地址
Size: 傳輸數據的大小 多少個字節
Timeout: 最大讀取時間,超過時間將自動退出函數
使用HAL_I2C_Mem_Write等於先使用HAL_I2C_Master_Transmit傳輸第一個寄存器地址,再用HAL_I2C_Master_Transmit傳輸寫入第一個寄存器的數據。可以傳輸多個數據
void Single_WriteI2C(uint8_t REG_Address,uint8_t REG_data)
{
uint8_t TxData[2] = {REG_Address,REG_data};
while(HAL_I2C_Master_Transmit(&hi2c1,I2C1_WRITE_ADDRESS,(uint8_t*)TxData,2,1000) != HAL_OK)
{
if (HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)
{
Error_Handler();
}
}
}
在傳輸過程,寄存器地址和源數據地址是會自加的。
至於讀函數也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,來寫讀指定設備的指定寄存器數據是十分方便的,讓設計過程省了好多步驟。
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);