HAL常用函數積累


重定向

//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);        //等待發送結束
  }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM