重定向
//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); //等待发送结束
}