重定向
//usart
int fputc(int ch,FILE *F) { unsigned char temp[1]={ch}; HAL_UART_Transmit(&huart1,temp,1,2); return ch; }
串口發送字符串和16進制
//main uint8_t buf_str[]="ABC"; //定義字符串變量 uint16_t len=sizeof(buf_str); uint8_t buf_char=0xD8; //定義16進制變量 HAL_UART_Transmit(&huart1,buf_str,len,1000); //發送字符串變量 while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待發送結束 HAL_UART_Transmit(&huart1,&buf_char,1,1000); //發送16進制變量 while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待發送結束
串口接收
//usart.h // 接收緩存區 #define USART_REC_LEN 200 //定義最大接收字節數 200 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern uint8_t USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 extern uint16_t USART_RX_STA; //接收狀態標記 //用於緩存傳輸來的每一個字節 #define RXBUFFERSIZE 1 //緩存大小 extern uint8_t aRxBuffer[RXBUFFERSIZE];//HAL庫USART接收Buffer
//usart.c //從左 字符串截取函數 char * left(char *dst,char *src, int n) { char *p = src; char *q = dst; int len = strlen(src); if(n>len) n = len; while(n--) *(q++) = *(p++); *(q++)='\0'; /*有必要嗎?很有必要*/ return dst; }
//stm32f4xx_it.c //串口1中斷服務程序 void USART1_IRQHandler(void) { uint32_t timeout=0; uint32_t maxDelay=0x1FFFF; #if SYSTEM_SUPPORT_OS //使用OS OSIntEnter(); #endif HAL_UART_IRQHandler(&huart1); //調用HAL庫中斷處理公用函數 timeout=0; while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)//等待就緒 { timeout++;////超時處理 if(timeout>maxDelay) break; } timeout=0; while(HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次處理完成之后,重新開啟中斷並設置RxXferCount為1 { timeout++; //超時處理 if(timeout>maxDelay) break; } #if SYSTEM_SUPPORT_OS //使用OS OSIntExit(); #endif }
//stm32f4xx_it.c //接收回調函數 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { int len; uint8_t temp[200]; if(huart->Instance==USART1)//如果是串口1 { if((USART_RX_STA&0x8000)==0)//接收未完成 { if(USART_RX_STA&0x4000)//接收到了0x0d { if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始 else { /******************************************************/ USART_RX_STA|=0x8000; //接收完成了 len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度 left((char *)temp,(char *)USART_RX_BUF, len); printf("\r\n您發送的消息為:%s\r\n",temp); if(strcmp((const char *)temp,"78")==0) { printf("\r\n相等\r\n"); } USART_RX_STA=0; /******************************************************/ } } else //還沒收到0X0D { if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000; else { USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ; USART_RX_STA++; if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收 } } } } }
串口main接收
if(USART_RX_STA&0x8000) { len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度 printf("\r\n您發送的消息為:\r\n"); HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //發送接收到的數據 while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待發送結束 printf("\r\n\r\n");//插入換行 USART_RX_STA=0; }
外部中斷回調函數
//stm32f4xx_it 外部中斷通用函數
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN) { if(GPIO_PIN==KEY0_Pin) HAL_GPIO_WritePin(LED_GREEN_GPIO_Port,LED_GREEN_Pin,GPIO_PIN_RESET); else if(GPIO_PIN==KEY1_Pin) HAL_GPIO_WritePin(LED_RED_GPIO_Port,LED_RED_Pin,GPIO_PIN_RESET); else if(GPIO_PIN==KEY2_Pin) HAL_GPIO_WritePin(LED_GREEN_GPIO_Port,LED_GREEN_Pin|LED_RED_Pin,GPIO_PIN_SET); }
打印技巧
#define Log 1 #if Log printf("date"); #endif
定時器中斷回調函數
//stm32f4xx_it 定時器中斷通用函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim==(&TIM3_Handler)) { LED_GREEN=!LED_GREEN; } }
通用定時器輸出PWM
//main HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);//ENABLE PWM PIN __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_3,1000);//change pwm duty cycle
//time
void TIM_SetTIM3Compare4(u32 compare)//改變PWM占空比 { TIM3->CCR4=compare; }
輸入捕獲
//tim.c u8 TIM5CH1_CAPTURE_STA=0; //定義一個八位的標志變量,當作寄存器來使用 //捕獲完成標志 [7]: 0 代表還沒有進行一次捕獲,1 表示已經進行到一次捕獲,已經得到相應的值了 //捕獲高電平標志 [6]: 0 表示沒有捕捉到高電平,1 表示捕捉到高電平 //[5,0] 表示計數器溢出次數 u32 TIM5CH1_CAPTURE_VAL; //輸入捕獲值(TIM2/TIM5是32位) //定時器計數溢出中斷函數 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { //溢出中斷可能發生在捕獲到高電平前也可以在捕獲到低電平之后、 //溢出中斷計數只有在捕捉到低電平之后,沒有捕捉到低電平之前有效 if((TIM5CH1_CAPTURE_STA&0X80)==0)//判斷[7]是否等於0,0的話表示沒有捕獲完成 { if(TIM5CH1_CAPTURE_STA&0X40)//判斷有沒有捕捉到高電平,[5] =1,表式捕獲到高電平 { if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了 { TIM5CH1_CAPTURE_STA|=0X80; //標記成功捕獲了一次,不再計數了,強制結束捕獲 TIM5CH1_CAPTURE_VAL=0XFFFFFFFF; } else TIM5CH1_CAPTURE_STA++; //如果計數沒有超過范圍,就計數加1 } } } //定時器輸入捕獲中斷處理回調函數,該函數在HAL_TIM_IRQHandler中會被調用 //當捕捉到上升沿或者下降沿觸發中斷 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕獲中斷發生時執行 { //首先要判斷一下,這個沒有發生一次捕獲捕獲,[7]=0 if((TIM5CH1_CAPTURE_STA&0x80)==0) { //再次進入中斷的時候已經是下降沿觸發了 //這個地方再確認一下是不是之前已經捕獲到高電平了,如果是,就表明值有效 if(TIM5CH1_CAPTURE_STA&0X40)//如果之前捕獲到高電平了 { //如果之前捕獲到高電平,表示已經成功捕獲一次了,所以把TIM5CH1_CAPTURE_STA最高位置1 TIM5CH1_CAPTURE_STA|=0x80; TIM5CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);//獲取當前的捕獲值. TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1); //一定要先清除原來的設置 TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//配置TIM5通道1上升沿捕獲 } else //初始狀態因為還沒有進行捕獲,所以[7]=0,第一次捕捉高電平,所以之前是沒有捕獲到高電平,[6]也是0 { //現在是第一次捕捉到高電平 //[7]: 捕獲剛開始,明顯是等於0的,捕獲到低電平才是1 //[5-0] 從0開始計數 TIM5CH1_CAPTURE_STA=0; //[6]:已經捕獲到高電平了,應該置1 TIM5CH1_CAPTURE_STA|=0X40; //VAL計數置0 TIM5CH1_CAPTURE_VAL=0; //定時器5需要重置,先關掉定時器,配置下降沿觸發 __HAL_TIM_DISABLE(&htim5); __HAL_TIM_SET_COUNTER(&htim5,0); TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1); //一定要先清除原來的設置!! TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);//定時器5通道1設置為下降沿捕獲 __HAL_TIM_ENABLE(&htim5);//使能定時器5 } } }
//main extern u8 TIM5CH1_CAPTURE_STA; //輸入捕獲狀態 extern u32 TIM5CH1_CAPTURE_VAL; //輸入捕獲值 /***********************************************/ //同時開啟定時器輸入捕獲通道和使能捕獲中斷 HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1); //也可以下邊兩個函數分開使用 //HAL_TIM_IC_Start(&htim5,TIM_CHANNEL_1);//開啟輸入捕獲 //__HAL_TIM_ENABLE_IT(&htim5,TIM_IT_UPDATE);//開啟使能捕獲中斷 while (1) { /* USER CODE END WHILE */ if(TIM5CH1_CAPTURE_STA&0X80) //成功捕獲到了一次高電平 { temp=TIM5CH1_CAPTURE_STA&0X3F; temp*=0XFFFFFFFF; //溢出時間總和 temp+=TIM5CH1_CAPTURE_VAL; //得到總的高電平時間 printf("HIGH:%lld us\r\n",temp);//打印總的高點平時間 TIM5CH1_CAPTURE_STA=0; //開啟下一次捕獲 } /* USER CODE BEGIN 3 */ }
ADC采集
//adc.c //獲得ADC值 //ch: 通道值 0~16,取值范圍為:ADC_CHANNEL_0~ADC_CHANNEL_16 //返回值:轉換結果 u16 GET_ADC(ADC_HandleTypeDef hadc,u32 ch) { ADC_ChannelConfTypeDef ADC_ChanConf; ADC_ChanConf.Channel=ch; //通道 ADC_ChanConf.Rank=1; //第1個序列,序列1 ADC_ChanConf.SamplingTime=ADC_SAMPLETIME_480CYCLES; //采樣時間 ADC_ChanConf.Offset=0; HAL_ADC_ConfigChannel(&hadc,&ADC_ChanConf); //通道配置 HAL_ADC_Start(&hadc); //開啟ADC HAL_ADC_PollForConversion(&hadc,10); //輪詢轉換 return (u16)HAL_ADC_GetValue(&hadc); //返回最近一次ADC1規則組的轉換結果 } //獲取指定通道的轉換值,取times次,然后平均 //times:獲取次數 //返回值:通道ch的times次轉換結果平均值 u16 GET_ADC_AVERAGE(ADC_HandleTypeDef hadc,u32 ch,u8 times) { u32 temp_val=0; u8 t; for(t=0;t<times;t++) { temp_val+=GET_ADC(hadc,ch); HAL_Delay(5); } return temp_val/times; }
//main while (1) { /* USER CODE END WHILE */ AD_Value=GET_ADC_AVERAGE(hadc1,ADC_CHANNEL_0,20); AD_Value=AD_Value*3.3/4096; printf("\r\n %f \r\n",AD_Value); }
SYS.C
#include "sys.h" //時鍾系統配置函數 //Fvco=Fs*(plln/pllm); //SYSCLK=Fvco/pllp=Fs*(plln/(pllm*pllp)); //Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq)); //Fvco:VCO頻率 //SYSCLK:系統時鍾頻率 //Fusb:USB,SDIO,RNG等的時鍾頻率 //Fs:PLL輸入時鍾頻率,可以是HSI,HSE等. //plln:主PLL倍頻系數(PLL倍頻),取值范圍:64~432. //pllm:主PLL和音頻PLL分頻系數(PLL之前的分頻),取值范圍:2~63. //pllp:系統時鍾的主PLL分頻系數(PLL之后的分頻),取值范圍:2,4,6,8.(僅限這4個值!) //pllq:USB/SDIO/隨機數產生器等的主PLL分頻系數(PLL之后的分頻),取值范圍:2~15. //外部晶振為25M的時候,推薦值:plln=360,pllm=25,pllp=2,pllq=8. //得到:Fvco=25*(360/25)=360Mhz // SYSCLK=360/2=180Mhz // Fusb=360/8=45Mhz //返回值:0,成功;1,失敗 void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq) { HAL_StatusTypeDef ret = HAL_OK; RCC_OscInitTypeDef RCC_OscInitStructure; RCC_ClkInitTypeDef RCC_ClkInitStructure; __HAL_RCC_PWR_CLK_ENABLE(); //使能PWR時鍾 //下面這個設置用來設置調壓器輸出電壓級別,以便在器件未以最大頻率工作 //時使性能與功耗實現平衡,此功能只有STM32F42xx和STM32F43xx器件有, __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);//設置調壓器輸出電壓級別1 RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //時鍾源為HSE RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打開HSE RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;//打開PLL RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;//PLL時鍾源選擇HSE RCC_OscInitStructure.PLL.PLLM=pllm; //主PLL和音頻PLL分頻系數(PLL之前的分頻),取值范圍:2~63. RCC_OscInitStructure.PLL.PLLN=plln; //主PLL倍頻系數(PLL倍頻),取值范圍:64~432. RCC_OscInitStructure.PLL.PLLP=pllp; //系統時鍾的主PLL分頻系數(PLL之后的分頻),取值范圍:2,4,6,8.(僅限這4個值!) RCC_OscInitStructure.PLL.PLLQ=pllq; //USB/SDIO/隨機數產生器等的主PLL分頻系數(PLL之后的分頻),取值范圍:2~15. ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化 if(ret!=HAL_OK) while(1); ret=HAL_PWREx_EnableOverDrive(); //開啟Over-Driver功能 if(ret!=HAL_OK) while(1); //選中PLL作為系統時鍾源並且配置HCLK,PCLK1和PCLK2 RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2); RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//設置系統時鍾時鍾源為PLL RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;//AHB分頻系數為1 RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV4; //APB1分頻系數為4 RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV2; //APB2分頻系數為2 ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_5);//同時設置FLASH延時周期為5WS,也就是6個CPU周期。 if(ret!=HAL_OK) while(1); } #ifdef USE_FULL_ASSERT //當編譯提示出錯的時候此函數用來報告錯誤的文件和所在行 //file:指向源文件 //line:指向在文件中的行數 void assert_failed(uint8_t* file, uint32_t line) { while (1) { } } #endif //THUMB指令不支持匯編內聯 //采用如下方法實現執行匯編指令WFI __asm void WFI_SET(void) { WFI; } //關閉所有中斷(但是不包括fault和NMI中斷) __asm void INTX_DISABLE(void) { CPSID I BX LR } //開啟所有中斷 __asm void INTX_ENABLE(void) { CPSIE I BX LR } //設置棧頂地址 //addr:棧頂地址 __asm void MSR_MSP(u32 addr) { MSR MSP, r0 //set Main Stack value BX r14 }
SYS.H
#ifndef _SYS_H #define _SYS_H #include "stm32f4xx.h" //0,不支持os //1,支持os #define SYSTEM_SUPPORT_OS 0 //定義系統文件夾是否支持OS /////////////////////////////////////////////////////////////////////////////////// //定義一些常用的數據類型短關鍵字 typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; typedef const int32_t sc32; typedef const int16_t sc16; typedef const int8_t sc8; typedef __IO int32_t vs32; typedef __IO int16_t vs16; typedef __IO int8_t vs8; typedef __I int32_t vsc32; typedef __I int16_t vsc16; typedef __I int8_t vsc8; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef const uint32_t uc32; typedef const uint16_t uc16; typedef const uint8_t uc8; typedef __IO uint32_t vu32; typedef __IO uint16_t vu16; typedef __IO uint8_t vu8; typedef __I uint32_t vuc32; typedef __I uint16_t vuc16; typedef __I uint8_t vuc8; //位帶操作,實現51類似的GPIO控制功能 //具體實現思想,參考<<CM3權威指南>>第五章(87頁~92頁).M4同M3類似,只是寄存器地址變了. //IO口操作宏定義 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) //IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014 #define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414 #define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814 #define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14 #define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014 #define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414 #define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814 #define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14 #define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014 #define GPIOJ_ODR_ADDr (GPIOJ_BASE+20) //0x40022414 #define GPIOK_ODR_ADDr (GPIOK_BASE+20) //0x40022814 #define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010 #define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410 #define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810 #define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10 #define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010 #define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410 #define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810 #define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10 #define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010 #define GPIOJ_IDR_Addr (GPIOJ_BASE+16) //0x40022410 #define GPIOK_IDR_Addr (GPIOK_BASE+16) //0x40022810 //IO口操作,只對單一的IO口! //確保n的值小於16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //輸入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //輸出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //輸入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //輸出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //輸入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //輸出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //輸入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //輸出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //輸入 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //輸出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //輸入 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //輸出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //輸入 #define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //輸出 #define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //輸入 #define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //輸出 #define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //輸入 #define PJout(n) BIT_ADDR(GPIOJ_ODR_Addr,n) //輸出 #define PJin(n) BIT_ADDR(GPIOJ_IDR_Addr,n) //輸入 #define PKout(n) BIT_ADDR(GPIOK_ODR_Addr,n) //輸出 #define PKin(n) BIT_ADDR(GPIOK_IDR_Addr,n) //輸入 void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq);//時鍾系統配置 //以下為匯編函數 void WFI_SET(void); //執行WFI指令 void INTX_DISABLE(void);//關閉所有中斷 void INTX_ENABLE(void); //開啟所有中斷 void MSR_MSP(u32 addr); //設置堆棧地址 #endif
DELAY.C
#include "delay.h" #include "sys.h" ////////////////////////////////////////////////////////////////////////////////// //如果使用ucos,則包括下面的頭文件即可. #if SYSTEM_SUPPORT_OS #include "includes.h" //ucos 使用 #endif ////////////////////////////////////////////////////////////////////////////////// static u32 fac_us=0; //us延時倍乘數 #if SYSTEM_SUPPORT_OS static u16 fac_ms=0; //ms延時倍乘數,在os下,代表每個節拍的ms數 #endif #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS定義了,說明要支持OS了(不限於UCOS). //當delay_us/delay_ms需要支持OS的時候需要三個與OS相關的宏定義和函數來支持 //首先是3個宏定義: //delay_osrunning:用於表示OS當前是否正在運行,以決定是否可以使用相關函數 //delay_ostickspersec:用於表示OS設定的時鍾節拍,delay_init將根據這個參數來初始哈systick //delay_osintnesting:用於表示OS中斷嵌套級別,因為中斷里面不可以調度,delay_ms使用該參數來決定如何運行 //然后是3個函數: //delay_osschedlock:用於鎖定OS任務調度,禁止調度 //delay_osschedunlock:用於解鎖OS任務調度,重新開啟調度 //delay_ostimedly:用於OS延時,可以引起任務調度. //本例程僅作UCOSII和UCOSIII的支持,其他OS,請自行參考着移植 //支持UCOSII #ifdef OS_CRITICAL_METHOD //OS_CRITICAL_METHOD定義了,說明要支持UCOSII #define delay_osrunning OSRunning //OS是否運行標記,0,不運行;1,在運行 #define delay_ostickspersec OS_TICKS_PER_SEC //OS時鍾節拍,即每秒調度次數 #define delay_osintnesting OSIntNesting //中斷嵌套級別,即中斷嵌套次數 #endif //支持UCOSIII #ifdef CPU_CFG_CRITICAL_METHOD //CPU_CFG_CRITICAL_METHOD定義了,說明要支持UCOSIII #define delay_osrunning OSRunning //OS是否運行標記,0,不運行;1,在運行 #define delay_ostickspersec OSCfg_TickRate_Hz //OS時鍾節拍,即每秒調度次數 #define delay_osintnesting OSIntNestingCtr //中斷嵌套級別,即中斷嵌套次數 #endif //us級延時時,關閉任務調度(防止打斷us級延遲) void delay_osschedlock(void) { #ifdef CPU_CFG_CRITICAL_METHOD //使用UCOSIII OS_ERR err; OSSchedLock(&err); //UCOSIII的方式,禁止調度,防止打斷us延時 #else //否則UCOSII OSSchedLock(); //UCOSII的方式,禁止調度,防止打斷us延時 #endif } //us級延時時,恢復任務調度 void delay_osschedunlock(void) { #ifdef CPU_CFG_CRITICAL_METHOD //使用UCOSIII OS_ERR err; OSSchedUnlock(&err); //UCOSIII的方式,恢復調度 #else //否則UCOSII OSSchedUnlock(); //UCOSII的方式,恢復調度 #endif } //調用OS自帶的延時函數延時 //ticks:延時的節拍數 void delay_ostimedly(u32 ticks) { #ifdef CPU_CFG_CRITICAL_METHOD OS_ERR err; OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err); //UCOSIII延時采用周期模式 #else OSTimeDly(ticks); //UCOSII延時 #endif } //systick中斷服務函數,使用OS時用到 void SysTick_Handler(void) { HAL_IncTick(); if(delay_osrunning==1) //OS開始跑了,才執行正常的調度處理 { OSIntEnter(); //進入中斷 OSTimeTick(); //調用ucos的時鍾服務程序 OSIntExit(); //觸發任務切換軟中斷 } } #endif //初始化延遲函數 //當使用ucos的時候,此函數會初始化ucos的時鍾節拍 //SYSTICK的時鍾固定為AHB時鍾 //SYSCLK:系統時鍾頻率 void delay_init(u8 SYSCLK) { #if SYSTEM_SUPPORT_OS //如果需要支持OS. u32 reload; #endif HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick頻率為HCLK fac_us=SYSCLK; //不論是否使用OS,fac_us都需要使用 #if SYSTEM_SUPPORT_OS //如果需要支持OS. reload=SYSCLK; //每秒鍾的計數次數 單位為K reload*=1000000/delay_ostickspersec; //根據delay_ostickspersec設定溢出時間 //reload為24位寄存器,最大值:16777216,在180M下,約合0.745s左右 fac_ms=1000/delay_ostickspersec; //代表OS可以延時的最少單位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//開啟SYSTICK中斷 SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中斷一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //開啟SYSTICK #else #endif } #if SYSTEM_SUPPORT_OS //如果需要支持OS. //延時nus //nus:要延時的us數. //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5) void delay_us(u32 nus) { u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的節拍數 delay_osschedlock(); //阻止OS調度,防止打斷us延時 told=SysTick->VAL; //剛進入時的計數器值 while(1) { tnow=SysTick->VAL; if(tnow!=told) { if(tnow<told)tcnt+=told-tnow; //這里注意一下SYSTICK是一個遞減的計數器就可以了. else tcnt+=reload-tnow+told; told=tnow; if(tcnt>=ticks)break; //時間超過/等於要延遲的時間,則退出. } }; delay_osschedunlock(); //恢復OS調度 } //延時nms //nms:要延時的ms數 //nms:0~65535 void delay_ms(u16 nms) { if(delay_osrunning&&delay_osintnesting==0)//如果OS已經在跑了,並且不是在中斷里面(中斷里面不能任務調度) { if(nms>=fac_ms) //延時的時間大於OS的最少時間周期 { delay_ostimedly(nms/fac_ms); //OS延時 } nms%=fac_ms; //OS已經無法提供這么小的延時了,采用普通方式延時 } delay_us((u32)(nms*1000)); //普通方式延時 } #else //不用ucos時 //延時nus //nus為要延時的us數. //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5) void delay_us(u32 nus) { u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的節拍數 told=SysTick->VAL; //剛進入時的計數器值 while(1) { tnow=SysTick->VAL; if(tnow!=told) { if(tnow<told)tcnt+=told-tnow; //這里注意一下SYSTICK是一個遞減的計數器就可以了. else tcnt+=reload-tnow+told; told=tnow; if(tcnt>=ticks)break; //時間超過/等於要延遲的時間,則退出. } }; } //延時nms //nms:要延時的ms數 void delay_ms(u16 nms) { u32 i; for(i=0;i<nms;i++) delay_us(1000); } #endif
DELAY.H
#ifndef _DELAY_H #define _DELAY_H #include <sys.h> void delay_init(u8 SYSCLK); void delay_ms(u16 nms); void delay_us(u32 nus); #endif
IIC.C
#include "i2c.h" #include "gpio.h" I2C_HandleTypeDef hi2c2; /* I2C2 init function */ void MX_I2C2_Init(void) { hi2c2.Instance = I2C2; hi2c2.Init.ClockSpeed = 100000; hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c2.Init.OwnAddress1 = 0; hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c2.Init.OwnAddress2 = 0; hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c2); } void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) { GPIO_InitTypeDef GPIO_InitStruct; if(hi2c->Instance==I2C2) { /* USER CODE BEGIN I2C2_MspInit 0 */ /* USER CODE END I2C2_MspInit 0 */ /**I2C2 GPIO Configuration PH4 ------> I2C2_SCL PH5 ------> I2C2_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C2; HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); /* Peripheral clock enable */ __HAL_RCC_I2C2_CLK_ENABLE(); /* USER CODE BEGIN I2C2_MspInit 1 */ /* USER CODE END I2C2_MspInit 1 */ } } void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c) { if(hi2c->Instance==I2C2) { /* USER CODE BEGIN I2C2_MspDeInit 0 */ /* USER CODE END I2C2_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_I2C2_CLK_DISABLE(); /**I2C2 GPIO Configuration PH4 ------> I2C2_SCL PH5 ------> I2C2_SDA */ HAL_GPIO_DeInit(GPIOH, GPIO_PIN_4|GPIO_PIN_5); } /* USER CODE BEGIN I2C2_MspDeInit 1 */ /* USER CODE END I2C2_MspDeInit 1 */ } /* USER CODE BEGIN 1 */ //產生IIC起始信號 void IIC_Start(void) { SDA_OUT(); //sda線輸出 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0;//鉗住I2C總線,准備發送或接收數據 } //產生IIC停止信號 void IIC_Stop(void) { SDA_OUT();//sda線輸出 IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; delay_us(4); IIC_SDA=1;//發送I2C總線結束信號 } //等待應答信號到來 //返回值:1,接收應答失敗 // 0,接收應答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA設置為輸入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0;//時鍾輸出0 return 0; } //產生ACK應答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //不產生ACK應答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC發送一個字節 //返回從機有無應答 //1,有應答 //0,無應答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低時鍾開始數據傳輸 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); //對TEA5767這三個延時都是必須的 IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //讀1個字節,ack=1時,發送ACK,ack=0,發送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA設置為輸入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck();//發送nACK else IIC_Ack(); //發送ACK return receive; } /* USER CODE END 1 */
IIC.H
#ifndef __i2c_H #define __i2c_H #ifdef __cplusplus extern "C" { #endif #include "stm32f4xx_hal.h" #include "sys.h" #include "delay.h" extern I2C_HandleTypeDef hi2c2; void MX_I2C2_Init(void); //IO方向設置 #define SDA_IN() {GPIOH->MODER&=~(3<<(5*2));GPIOH->MODER|=0<<5*2;} //PH5輸入模式 #define SDA_OUT() {GPIOH->MODER&=~(3<<(5*2));GPIOH->MODER|=1<<5*2;} //PH5輸出模式 //IO操作 #define IIC_SCL PHout(4) //SCL #define IIC_SDA PHout(5) //SDA #define READ_SDA PHin(5) //輸入SDA //IIC所有操作函數 void IIC_Start(void); //發送IIC開始信號 void IIC_Stop(void); //發送IIC停止信號 void IIC_Send_Byte(u8 txd); //IIC發送一個字節 u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個字節 u8 IIC_Wait_Ack(void); //IIC等待ACK信號 void IIC_Ack(void); //IIC發送ACK信號 void IIC_NAck(void); //IIC不發送ACK信號 void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data); u8 IIC_Read_One_Byte(u8 daddr,u8 addr); /* USER CODE END Prototypes */ #ifdef __cplusplus } #endif #endif /*__ i2c_H */
IIC主函數
while (1) { AT24CXX_Write(0,(u8*)buf,len); HAL_GPIO_WritePin(LED_RED_GPIO_Port,LED_RED_Pin,GPIO_PIN_RESET); HAL_Delay(300); HAL_GPIO_WritePin(LED_RED_GPIO_Port,LED_RED_Pin,GPIO_PIN_SET); HAL_Delay(300); AT24CXX_Read(0,data,len); HAL_UART_Transmit(&huart1,buf,len,1000); //發送數據 while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET); //等待發送結束 }