? ? ? ? 每次在做项目的时候用不同芯片时候移植代码特别不方便,不是要重新敲代码就是忘记之前的代码在哪里了,所以我打算将做过的常用的代码封装总结一下,绝大部分是自己编写和优化的,有大量的注释,实测是可以使用的。
? ? ? ? 做这个的目的:?一是方便自己移植,二是希望可以帮助有需要的人。我会持续更新,可以收藏保存一下。
目录
?
?
?
功能函数类
?
软件延时1us(大概)
/* 简易的us级别的延迟函数 我的芯片时钟是48Mhz 一个时钟周期是1/48M=0.020833us,一个机器周期包含12个时钟周期,所以一个机器周期是12*0.020833=0.25us 所以这里i++运行一次是0.25us,1us大概是运行4次 */ void delay_us(uint16_t num) { for(uint16_t j=0;j<num;j++) { for(uint16_t i=0;i<4;i++){}//根据不同的芯片周期修改i的数值 } }
?
中断中的按键检测(无松手检测,不卡程序)
先定义声明一下
#define key_input HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3) // 按键输入口 #define key_state_0 0 #define key_state_1 1 #define key_state_2 2
?
下面是按键检测函数,放到中断函数里面
char read_key(void) { static char key_state = 0; char key_press, key_return = 0; key_press = key_input; // 读按键I/O电平 switch (key_state) { case key_state_0: // 按键初始态 if (!key_press) key_state = key_state_1; // 键被按下,状态转换到键确认态 break; case key_state_1: // 按键确认态 if (!key_press) { key_state = key_state_2; // 按键仍按下, 状态转换到键释放态 } else key_state = key_state_0; // 按键已抬起,转换到按键初始态 break; case key_state_2: if (key_press) { key_state = key_state_0; //按键已释放,转换到按键初始态 key_return = 1; // 按键有按下,并且已经释放,按键确认输出为“1” } break; } return key_return; }
????????我的定时器中断是10ms,如果需要1ms的中断函数中使用的话建议将按键状态修改前加入累加计数,计到10再进行状态切换,以此来达到按键消抖的目的。
?
?
模块封装函数类
?
CS100A芯片
CS100A.c
#include "CS100A.h" TIM_HandleTypeDef TimHandle; uint16_t CS100A_count=0; //定时器中断中累加计数 float CS100A_distance=0; //计算超声波测得的距离,单位为cm float err=15/15.98; //调整误差系数,由于各种原因会出现误差,这个用于调整误差系数,若不需要调整则设置为1 /* //下面是main函数里面的代码初始化部分 //超声波引脚初始化 CS100A_INIT(); //超声波定时器计数初始化 CS100A_START(); //下面是main函数while(1)里面的超声波获取代码部分 printf("%f",CS100A_GET()); //串口打印超声波测距 HAL_Delay(500); */ /* 简易的us级别的延迟函数 我的芯片时钟是48Mhz 一个时钟周期是1/48M=0.020833us,一个机器周期包含12个时钟周期,所以一个机器周期是12*0.020833=0.25us 所以这里i++运行一次是0.25us,1us大概是运行4次 */ void delay_us(uint16_t num) { for(uint16_t j=0;j<num;j++) { for(uint16_t i=0;i<4;i++){}//根据不同的芯片周期修改i的数值 } } //超声波模块的引脚初始化函数 void CS100A_INIT(void) { GPIO_InitTypeDef Trig = {.Pin = CS100A_Trig_PIN,.Mode = CS100A_Trig_MODE,}; CS100A_Trig_CLK; HAL_GPIO_Init(CS100A_Trig, &Trig); HAL_GPIO_WritePin(CS100A_Trig, CS100A_Trig_PIN,CS100A_Trig_ST); GPIO_InitTypeDef Echo = {.Pin = CS100A_Echo_PIN,.Mode = CS100A_Echo_MODE,}; CS100A_Echo_CLK;; HAL_GPIO_Init(CS100A_Echo, &Echo); } /* 给超声波模块发送开始信号 将Trig引脚拉高至少10us */ void CS100A_START(void) { CS100A_Trig_SET; //将Trig引脚拉高 delay_us(20); //延迟大概20us CS100A_Trig_RST; //将Trig引脚拉低 } /* 超声波Echo引脚读取高电平时间的定时器计数初始化 这里放的是合宙AIR001芯片的初始化部分 自行根据自己的芯片修改 我配置的定时器中断周期是10us */ void CS100A_TIM_INIT(void) { TimHandle.Instance = TIM1; /* 选择TIM1 */ TimHandle.Init.Period = 32 - 1; /* 自动重装载值 */ TimHandle.Init.Prescaler = 10 - 1; /* 预分频为1-1 */ TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; /* 时钟不分频 */ TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 向上计数 */ TimHandle.Init.RepetitionCounter = 1 - 1; /* 不重复计数 */ TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; /* 自动重装载寄存器没有缓冲 */ /* TIM1初始化 */ if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK) { while(1); } } //更新中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { CS100A_count++; } //超声波获取数据 float CS100A_GET(void) { CS100A_START(); //给超声波模块发送开始信号 while(CS100A_Erig_READ==0); //等待高电平信号 /* TIM1使能启动,并使能中断 */ HAL_TIM_Base_Start_IT(&TimHandle); while(CS100A_Erig_READ==1); //等待低电平信号 /* TIM1使能关闭,并关闭中断 */ HAL_TIM_Base_Stop_IT(&TimHandle); CS100A_distance=CS100A_count*TIM_tim*0.034/2*err; CS100A_count=0; //清除计数 return CS100A_distance; }
??
CS100A.h
#ifndef _CS100A_H #define _CS100A_H #include "main.h" //定义定时器中断时间,单位是us,这里定义的是100us #define TIM_tim 10 //定义Trig引脚为推挽输出,初始化为低电平 #define CS100A_Trig GPIOA #define CS100A_Trig_PIN GPIO_PIN_5 #define CS100A_Trig_CLK __HAL_RCC_GPIOA_CLK_ENABLE(); #define CS100A_Trig_MODE GPIO_MODE_OUTPUT_PP #define CS100A_Trig_ST 0 //定义Echo引脚为浮空输入 #define CS100A_Echo GPIOA #define CS100A_Echo_PIN GPIO_PIN_7 #define CS100A_Echo_CLK __HAL_RCC_GPIOA_CLK_ENABLE(); #define CS100A_Echo_MODE GPIO_MODE_INPUT //设置Trig引脚为高电平 #define CS100A_Trig_SET HAL_GPIO_WritePin(CS100A_Trig, CS100A_Trig_PIN,1) //设置Trig引脚为低电平 #define CS100A_Trig_RST HAL_GPIO_WritePin(CS100A_Trig, CS100A_Trig_PIN,0) //读取Erig引脚的电平状态 #define CS100A_Erig_READ HAL_GPIO_ReadPin(CS100A_Echo,CS100A_Echo_PIN) void delay_us(uint16_t num); void CS100A_INIT(void); void CS100A_START(void); void CS100A_TIM_INIT(void); float CS100A_GET(void); #endif
?
?
?
MCP4725芯片
I2C.c
#include "I2C.h" #include "CS100A.h" /*引脚配置*/ #define MCP4725_SCL_SET HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,1) #define MCP4725_SCL_RST HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,0) #define MCP4725_SDA_SET HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,1) #define MCP4725_SDA_RST HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,0) #define MCP4725_SDAIN HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_1) //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef SCL = {.Pin = GPIO_PIN_0,.Mode = GPIO_MODE_OUTPUT_PP,}; __HAL_RCC_GPIOF_CLK_ENABLE(); HAL_GPIO_Init(GPIOF, &SCL); HAL_GPIO_WritePin(GPIOF, GPIO_PIN_0,1); GPIO_InitTypeDef SDA = {.Pin = GPIO_PIN_1,.Mode = GPIO_MODE_OUTPUT_PP,}; __HAL_RCC_GPIOF_CLK_ENABLE(); HAL_GPIO_Init(GPIOF, &SDA); HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1,1); } //SDA设置为输入 void SDA_IN (void) { GPIO_InitTypeDef SDA = {.Pin = GPIO_PIN_1,.Mode = GPIO_MODE_INPUT,}; __HAL_RCC_GPIOF_CLK_ENABLE(); HAL_GPIO_Init(GPIOF, &SDA); } //SDA设置为输出 void SDA_OUT(void) { GPIO_InitTypeDef SDA = {.Pin = GPIO_PIN_1,.Mode = GPIO_MODE_OUTPUT_PP,}; __HAL_RCC_GPIOF_CLK_ENABLE(); HAL_GPIO_Init(GPIOF, &SDA); } //产生IIC起始信号 void IIC_Start(void) { SDA_OUT(); //SDA线设置为输出 MCP4725_SDA_SET; MCP4725_SCL_SET; delay_us(10); MCP4725_SDA_RST; delay_us(10); MCP4725_SCL_RST; //钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void IIC_Stop(void) { SDA_OUT(); //SDA线输出 MCP4725_SCL_RST; MCP4725_SDA_RST; delay_us(10); MCP4725_SCL_SET; MCP4725_SDA_SET; //发送I2C总线结束信号 delay_us(10); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 uint8_t IIC_Wait_Ack(void) { uint8_t ucErrTime=0; SDA_IN(); //SDA设置为输入 MCP4725_SDA_SET; delay_us(2); MCP4725_SCL_SET; delay_us(2); while(MCP4725_SDAIN) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } MCP4725_SCL_RST;//时钟输出0 return 0; } //产生ACK应答 void IIC_Ack(void) { MCP4725_SCL_RST; SDA_OUT(); MCP4725_SDA_RST; delay_us(5); MCP4725_SCL_SET; delay_us(5); MCP4725_SCL_RST; } //不产生ACK应答 void IIC_NAck(void) { MCP4725_SCL_RST; SDA_OUT(); MCP4725_SDA_SET; delay_us(5); MCP4725_SCL_SET; delay_us(5); MCP4725_SCL_RST; } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte(uint8_t txd) { uint8_t t; SDA_OUT(); MCP4725_SCL_RST;//拉低时钟开始数据传输 for(t=0;t<8;t++) //开始准备信号线 { if((txd&0x80)>>7) MCP4725_SDA_SET; else MCP4725_SDA_RST; txd<<=1; delay_us(5); MCP4725_SCL_SET; delay_us(5); MCP4725_SCL_RST; delay_us(5); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK uint8_t IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { MCP4725_SCL_RST; delay_us(5); MCP4725_SCL_SET; receive<<=1; if(MCP4725_SDAIN) {receive++; } delay_us(4); } if (!ack) IIC_NAck();//发送nACK else IIC_Ack(); //发送ACK return receive; }
I2C.h
#ifndef __I2C_H #define __I2C_H #include "main.h" //IIC所有操作函数 void IIC_Init(void); //初始化IIC的IO口 void IIC_Start(void); //发送IIC开始信号 void IIC_Stop(void); //发送IIC停止信号 void IIC_Send_Byte(uint8_t txd); //IIC发送一个字节 uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节 uint8_t IIC_Wait_Ack(void); //IIC等待ACK信号 void IIC_Ack(void); //IIC发送ACK信号 void IIC_NAck(void); //IIC不发送ACK信号 void SDA_IN(void); void SDA_OUT(void); void IIC_Write_One_Byte(uint8_t daddr,uint8_t addr,uint8_t data); uint8_t IIC_Read_One_Byte(uint8_t daddr,uint8_t addr); #endif
MCP4725.c
#include "MCP4725.h" //初始化IIC接口 void MCP4725_Init(void) { IIC_Init(); } //使用快速模式写命令写DAC寄存器 void MCP4725_WriteData_Voltage(uint16_t Vout) //电压单位mV { uint8_t temp; uint16_t Dn; Dn = ( 4096 * Vout) / VREF_in; //这里的VREF宏定输入电压 temp = (0x0F00 & Dn) >> 8; //12位数据。0000XXXX XXXXXXXX IIC_Start(); IIC_Send_Byte(0XC0); //器件寻址,器件代吗:1100; 地址位A2,A1,A0为 0 , 0 , 1;-> 1100 0010 //这个地址0XC0或0XC1,根据自己购买的模块决定 IIC_Wait_Ack(); IIC_Send_Byte(temp); //将高8位送到DAC寄存器 IIC_Wait_Ack(); IIC_Send_Byte(Dn); //将低8位送到DAC寄存器 IIC_Wait_Ack(); IIC_Stop();//产生一个停止条件 HAL_Delay(10); } void MCP4725_WriteData_Digital(uint16_t data) //12位数字量 { uint8_t data_H=0,data_L=0; data_H = ( 0x0F00 & data) >> 8; data_L = 0X00FF & data ; IIC_Start(); IIC_Send_Byte(0XC0); //器件寻址,器件代吗:1100; 地址位A2,A1,A0为 0 , 0 , 0;-> 1100 0010 IIC_Wait_Ack(); IIC_Send_Byte(data_H); IIC_Wait_Ack(); IIC_Send_Byte(data_L); IIC_Wait_Ack(); IIC_Stop();//产生一个停止条件 HAL_Delay(10); }
MCP4725.h
#ifndef __MCP4725_H #define __MCP4725_H #include "I2C.h" #include "main.h" #define VREF_in 3300//这个数值根据自己给模块的供电电压而定 void MCP4725_Init(void); void MCP4725_WriteData_Digital(uint16_t data); void MCP4725_WriteData_Voltage(uint16_t Vout); #endif
? ? ? ? 主函数中需要初始化一下:MCP4725_Init();
? ? ? ? 然后调用这个函数即可:MCP4725_WriteData_Voltage(1000);
?
?
?
?
?
?
?