zoukankan      html  css  js  c++  java
  • 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++)=''; /*有必要吗?很有必要*/  
        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("
    您发送的消息为:%s
    ",temp);
                        if(strcmp((const char *)temp,"78")==0)
                     {
                      printf("
    相等
    ");
                     }
                        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("
    您发送的消息为:
    ");
                HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);    //发送接收到的数据
                while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);        //等待发送结束
                printf("
    
    ");//插入换行
                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
    ",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("
     %f 
    ",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);        //等待发送结束
      }
  • 相关阅读:
    改善ERP的用户体验,个性化用户界面(Jquery 提供源码)
    一个美国富人在洗手间里陪女佣孩子吃晚餐的故事
    easyui datagrid 批量 提交 json 数据到服务器
    XML基础
    Asp.Net 上传大文件专题(1)概述:上传大文件的难点
    Asp.Net 上传大文件专题(3)从请求流中获取数据并保存为文件[下]
    Asp.Net上传大文件系列
    Asp.Net 上传大文件专题(3)从请求流中获取数据并保存为文件[上]
    XM操作类
    Asp.Net 上传大文件专题(4)利用ajax技术显示上传进度
  • 原文地址:https://www.cnblogs.com/CaiFengYuanXing/p/13632429.html
Copyright © 2011-2022 走看看