stm32f4外设学习篇(代码集合)(一)https://developer.aliyun.com/article/1472514
9、随机数发生器
.c文件
#include "rng.h" /** 不精准延时函数 **/ static void Delay(uint32_t time) { while (time--) { int i = 10000; for (; i > 0; i--) ; } } //初始化RNG uint8_t RNG_Init(void) { uint16_t retry = 0; RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE); //开启RNG时钟,来自PLL48CLK RNG_Cmd(ENABLE); //使能RNG while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET && retry < 10000) //等待随机数就绪 { retry++; Delay(100); } if (retry >= 10000) return 1; //随机数产生器工作不正常 return 0; } //得到随机数 //返回值:获取到的随机数 uint32_t RNG_Get_RandomNum(void) { while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) ; //等待随机数就绪 return RNG_GetRandomNumber(); } //生成[min,max]范围的随机数 int RNG_Get_RandomRange(int min, int max) { return RNG_Get_RandomNum() % (max - min + 1) + min; }
.h文件
#ifndef __RNG_H #define __RNG_H #include "stm32f4xx.h" uint8_t RNG_Init(void); //RNG初始化 uint32_t RNG_Get_RandomNum(void); //得到随机数 int RNG_Get_RandomRange(int min, int max); //生成[min,max]范围的随机数 #endif
10、ADC
.h文件
#ifndef __ADC_H #define __ADC_H #include "stm32f4xx.h" #define RHEOSTAT_NOFCHANEL 5 // ADC1 IO ADC2 IO ADC3 IO // 通道0 PA0 通道0 PA0 通道0 PA0 // 通道1 PA1 通道1 PA1 通道1 PA1 // 通道2 PA2 通道2 PA2 通道2 PA2 // 通道3 PA3 通道3 PA3 通道3 PA3 // 通道4 PA4 通道4 PA4 通道4 PF6 // 通道5 PA5 通道5 PA5 通道5 PF7 // 通道6 PA6 通道6 PA6 通道6 PF8 // 通道7 PA7 通道7 PA7 通道7 PF9 // 通道8 PB0 通道8 PB0 通道8 PF10 // 通道9 PB1 通道9 PB1 通道9 PF3 // 通道10 PC0 通道10 PC0 通道10 PC0 // 通道11 PC1 通道11 PC1 通道11 PC1 // 通道12 PC2 通道12 PC2 通道12 PC2 // 通道13 PC3 通道13 PC3 通道13 PC3 // 通道14 PC4 通道14 PC4 通道14 PF4 // 通道15 PC5 通道15 PC5 通道15 PF5 // 通道16 连接内部温度传感器 通道16 连接内部VSS 通道16 连接内部VSS // 通道17 连接内部Vrefint 通道17 连接内部VSS 通道17 连接内部VSS /*=====================通道1 IO======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT1 GPIOB #define RHEOSTAT_ADC_GPIO_PIN1 GPIO_Pin_0 #define RHEOSTAT_ADC_GPIO_CLK1 RCC_AHB1Periph_GPIOB #define RHEOSTAT_ADC_CHANNEL1 ADC_Channel_8 /*=====================通道2 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT2 GPIOB #define RHEOSTAT_ADC_GPIO_PIN2 GPIO_Pin_1 #define RHEOSTAT_ADC_GPIO_CLK2 RCC_AHB1Periph_GPIOB #define RHEOSTAT_ADC_CHANNEL2 ADC_Channel_9 /*=====================通道3 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT3 GPIOA #define RHEOSTAT_ADC_GPIO_PIN3 GPIO_Pin_6 #define RHEOSTAT_ADC_GPIO_CLK3 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL3 ADC_Channel_6 /*=====================通道4 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT4 GPIOA #define RHEOSTAT_ADC_GPIO_PIN4 GPIO_Pin_0 #define RHEOSTAT_ADC_GPIO_CLK4 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL4 ADC_Channel_0 /*=====================通道5 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT5 GPIOA #define RHEOSTAT_ADC_GPIO_PIN5 GPIO_Pin_5 #define RHEOSTAT_ADC_GPIO_CLK5 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL5 ADC_Channel_5 // ADC 序号宏定义 #define RHEOSTAT_ADC ADC1 #define RHEOSTAT_ADC_CLK RCC_APB2Periph_ADC1 // ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里 #define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1 + 0x4c) // ADC DMA 通道宏定义,这里我们使用DMA传输 #define RHEOSTAT_ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define RHEOSTAT_ADC_DMA_CHANNEL DMA_Channel_0 #define RHEOSTAT_ADC_DMA_STREAM DMA2_Stream0 void Rheostat_Init(void); #endif /* __BSP_ADC_H */
.c文件
#include "adc.h" __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL] = {0}; static void Rheostat_ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*=====================通道1======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK1, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT1, &GPIO_InitStructure); /*=====================通道2======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK2, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT2, &GPIO_InitStructure); /*=====================通道3=======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK3, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT3, &GPIO_InitStructure); /*=====================通道4======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK4, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT4, &GPIO_InitStructure); /*=====================通道5=======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK5, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT5, &GPIO_InitStructure); } static void Rheostat_ADC_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_DMA_CLK, ENABLE); // 外设基址为:ADC 数据寄存器地址 DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR; // 存储器地址,实际上就是一个内部SRAM的变量 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_ConvertedValue; // 数据传输方向为外设到存储器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 缓冲区大小为,指一次传输的数据量 DMA_InitStructure.DMA_BufferSize = RHEOSTAT_NOFCHANEL; // 外设寄存器只有一个,地址不用递增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 存储器地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // // 外设数据大小为半字,即两个字节 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 存储器数据大小也为半字,跟外设数据大小相同 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 循环传输模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 禁止DMA FIFO ,使用直连模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO 大小,FIFO模式禁止时,这个不用配置 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 选择 DMA 通道,通道存在于流中 DMA_InitStructure.DMA_Channel = RHEOSTAT_ADC_DMA_CHANNEL; //初始化DMA流,流相当于一个大的管道,管道里面有很多通道 DMA_Init(RHEOSTAT_ADC_DMA_STREAM, &DMA_InitStructure); // 使能DMA流 DMA_Cmd(RHEOSTAT_ADC_DMA_STREAM, ENABLE); // 开启ADC时钟 RCC_APB2PeriphClockCmd(RHEOSTAT_ADC_CLK, ENABLE); // -------------------ADC Common 结构体 参数 初始化------------------------ // 独立ADC模式 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; // 时钟为fpclk x分频 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 禁止DMA直接访问模式 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // 采样时间间隔 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // -------------------ADC Init 结构体 参数 初始化-------------------------- ADC_StructInit(&ADC_InitStructure); // ADC 分辨率 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 扫描模式,多通道采集需要 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发通道,本例子使用软件触发,此值随便赋值即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //数据右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //转换通道 1个 ADC_InitStructure.ADC_NbrOfConversion = RHEOSTAT_NOFCHANEL; ADC_Init(RHEOSTAT_ADC, &ADC_InitStructure); //--------------------------------------------------------------------------- // 配置 ADC 通道转换顺序和采样时间周期 ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL1, 1, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL3, 3, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL4, 4, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL5, 5, ADC_SampleTime_3Cycles); // 使能DMA请求 after last transfer (Single-ADC mode) ADC_DMARequestAfterLastTransferCmd(RHEOSTAT_ADC, ENABLE); // 使能ADC DMA ADC_DMACmd(RHEOSTAT_ADC, ENABLE); // 使能ADC ADC_Cmd(RHEOSTAT_ADC, ENABLE); //开始adc转换,软件触发 ADC_SoftwareStartConv(RHEOSTAT_ADC); } // 初始化 void Rheostat_Init(void) { Rheostat_ADC_GPIO_Config(); Rheostat_ADC_Mode_Config(); }
11、DAC
.h文件
#ifndef __DAC_H #define __DAC_H #include "stm32f4xx.h" #define DAC_CLK RCC_APB1Periph_DAC #define DAC_DMA_CLK RCC_AHB1Periph_DMA1 #define DAC_CHANNEL DMA_Channel_7 #define DAC_DMA_STREAM DMA1_Stream5 #define DAC_CH1_GPIO_CLK RCC_AHB1Periph_GPIOA #define DAC_CH1_GPIO_PORT GPIOA #define DAC_CH1_GPIO_PIN GPIO_Pin_4 #define DAC_CH1_CHANNEL DAC_Channel_1 #define DAC_CH2_GPIO_CLK RCC_AHB1Periph_GPIOA #define DAC_CH2_GPIO_PORT GPIOA #define DAC_CH2_GPIO_PIN GPIO_Pin_5 #define DAC_CH2_CHANNEL DAC_Channel_2 #define DAC_CHANNEL1 1 #define DAC_CHANNEL2 2 void DAC_CH1_Configuration(void); void DAC_CH2_Configuration(void); void DAC_Set_V(unsigned char dac_channel, float V); #endif /* __DAC_H */
.c文件
#include "dac.h" //DAC1配置 void DAC_CH1_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitType; RCC_AHB1PeriphClockCmd(DAC_CH1_GPIO_CLK, ENABLE); //使能GPIOA时钟 RCC_APB1PeriphClockCmd(DAC_CLK, ENABLE); //使能DAC时钟 GPIO_InitStructure.GPIO_Pin = DAC_CH1_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(DAC_CH1_GPIO_PORT, &GPIO_InitStructure); //初始化 DAC_InitType.DAC_Trigger = DAC_Trigger_None; //不使用触发功能 TEN1=0 DAC_InitType.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生 DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; //屏蔽、幅值设置 DAC_InitType.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //DAC1输出缓存关闭 BOFF1=1 DAC_Init(DAC_CH1_CHANNEL, &DAC_InitType); //初始化DAC通道1 DAC_Cmd(DAC_CH1_CHANNEL, ENABLE); //使能DAC通道1 DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 } //DAC1配置 void DAC_CH2_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitType; RCC_AHB1PeriphClockCmd(DAC_CH2_GPIO_CLK, ENABLE); //使能GPIOA时钟 RCC_APB1PeriphClockCmd(DAC_CLK, ENABLE); //使能DAC时钟 GPIO_InitStructure.GPIO_Pin = DAC_CH2_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(DAC_CH2_GPIO_PORT, &GPIO_InitStructure); //初始化 DAC_InitType.DAC_Trigger = DAC_Trigger_None; //不使用触发功能 TEN1=0 DAC_InitType.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生 DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; //屏蔽、幅值设置 DAC_InitType.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //DAC1输出缓存关闭 BOFF1=1 DAC_Init(DAC_CH2_CHANNEL, &DAC_InitType); //初始化DAC通道2 DAC_Cmd(DAC_CH2_CHANNEL, ENABLE); //使能DAC通道2 DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 } //DAC通道和输出电压设置 : single-precision operand implicitly converted to double-precision add 4096.0f and 3.3f void DAC_Set_V(unsigned char dac_channel, float V) { if (dac_channel == DAC_CHANNEL1) { DAC_SetChannel1Data(DAC_Align_12b_R, V * 4096.0f / 3.3f); //12位右对齐数据格式设置DAC值 } else if (dac_channel == DAC_CHANNEL2) { DAC_SetChannel2Data(DAC_Align_12b_R, V * 4096.0f / 3.3f); //12位右对齐数据格式设置DAC值 } }
12、DMA
.h文件
#ifndef __DMA_H #define __DMA_H #include "stm32f4xx.h" void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, uint32_t chx, uint32_t par, uint32_t mar, uint16_t ndtr); //配置DMAx_CHx void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, uint16_t ndtr); //使能一次DMA传输 #endif
.c文件
#include "dma.h" //DMAx的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8位数据宽度/存储器增量模式 //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7 //par:外设地址 //mar:存储器地址 //ndtr:数据传输量 void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, uint32_t chx, u32 par, uint32_t mar, uint16_t ndtr) { DMA_InitTypeDef DMA_InitStructure; if ((u32)DMA_Streamx > (u32)DMA2) //得到当前stream是属于DMA2还是DMA1 { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //DMA2时钟使能 } else { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能 } DMA_DeInit(DMA_Streamx); while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE) { } //等待DMA可配置 /* 配置 DMA Stream */ DMA_InitStructure.DMA_Channel = chx; //通道选择 DMA_InitStructure.DMA_PeripheralBaseAddr = par; //DMA外设地址 DMA_InitStructure.DMA_Memory0BaseAddr = mar; //DMA 存储器0地址 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式 DMA_InitStructure.DMA_BufferSize = ndtr; //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输 DMA_Init(DMA_Streamx, &DMA_InitStructure); //初始化DMA Stream } //开启一次DMA传输 //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //ndtr:数据传输量 void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, uint16_t ndtr) { DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输 while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE) { } //确保DMA可以被设置 DMA_SetCurrDataCounter(DMA_Streamx, ndtr); //数据传输量 DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA传输 }
13、IIC
.h文件
#ifndef __I2C_H #define __I2C_H #include "stm32f4xx.h" #include <inttypes.h> #define EEPROM_I2C_WR 0 /* 写控制bit */ #define EEPROM_I2C_RD 1 /* 读控制bit */ /* 定义I2C总线连接的GPIO端口 */ #define EEPROM_I2C_GPIO_PORT GPIOB /* GPIO端口 */ #define EEPROM_I2C_GPIO_CLK RCC_AHB1Periph_GPIOB /* GPIO端口时钟 */ #define EEPROM_I2C_SCL_PIN GPIO_Pin_8 /* 连接到SCL时钟线的GPIO */ #define EEPROM_I2C_SDA_PIN GPIO_Pin_9 /* 连接到SDA数据线的GPIO */ /* 定义读写SCL和SDA的宏 */ #if 1 /* 条件编译: 1 选择GPIO的库函数实现IO读写 */ #define EEPROM_I2C_SCL_1() GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN) /* SCL = 1 */ #define EEPROM_I2C_SCL_0() GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN) /* SCL = 0 */ #define EEPROM_I2C_SDA_1() GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* SDA = 1 */ #define EEPROM_I2C_SDA_0() GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* SDA = 0 */ #define EEPROM_I2C_SDA_READ() GPIO_ReadInputDataBit(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* 读SDA口线状态 */ #else /* 这个分支选择直接寄存器操作实现IO读写 */ /* 注意:如下写法,在IAR最高级别优化时,会被编译器错误优化 */ #define EEPROM_I2C_SCL_1() EEPROM_I2C_GPIO_PORT->BSRRL = EEPROM_I2C_SCL_PIN /* SCL = 1 */ #define EEPROM_I2C_SCL_0() EEPROM_I2C_GPIO_PORT->BSRRH = EEPROM_I2C_SCL_PIN /* SCL = 0 */ #define EEPROM_I2C_SDA_1() EEPROM_I2C_GPIO_PORT->BSRRL = EEPROM_I2C_SDA_PIN /* SDA = 1 */ #define EEPROM_I2C_SDA_0() EEPROM_I2C_GPIO_PORT->BSRRH = EEPROM_I2C_SDA_PIN /* SDA = 0 */ #define EEPROM_I2C_SDA_READ() ((EEPROM_I2C_GPIO_PORT->IDR & EEPROM_I2C_SDA_PIN) != 0) /* 读SDA口线状态 */ #endif /* * AT24C02 2kb = 2048bit = 2048/8 B = 256 B * 32 pages of 8 bytes each * * Device Address * 1 0 1 0 A2 A1 A0 R/W * 1 0 1 0 0 0 0 0 = 0XA0 * 1 0 1 0 0 0 0 1 = 0XA1 */ /* AT24C01/02 every page have 8 bit * AT24C04/08A/16A every page have 16 bit */ #define EEPROM_DEV_ADDR 0xA0 /* 24xx02 address */ #define EEPROM_PAGE_SIZE 8 /* 24xx02 page size*/ #define EEPROM_SIZE 256 /* 24xx02 all size */ void i2c_Start(void); void i2c_Stop(void); void i2c_SendByte(uint8_t _ucByte); uint8_t i2c_ReadByte(void); uint8_t i2c_WaitAck(void); void i2c_Ack(void); void i2c_NAck(void); uint8_t i2c_CheckDevice(uint8_t _Address); uint8_t ee_Test(void); #endif
.c文件
#include "i2c.h" /*在访问I2C设备前,请先调用 i2c_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO*/ static void i2c_Config(void); /* ********************************************************************************************************* * 函 数 名: i2c_Delay * 功能说明: I2C总线位延迟,最快400KHz * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void i2c_Delay(void) { uint8_t i; /* 可用逻辑分析仪测量I2C通讯时的频率 工作条件:CPU主频168MHz ,MDK编译环境,1级优化 经测试,循环次数为20~250时都能通讯正常 */ for (i = 0; i < 40; i++) ; } /* ********************************************************************************************************* * 函 数 名: i2c_Start * 功能说明: CPU发起I2C总线启动信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Start(void) { /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */ EEPROM_I2C_SDA_1(); EEPROM_I2C_SCL_1(); i2c_Delay(); EEPROM_I2C_SDA_0(); i2c_Delay(); EEPROM_I2C_SCL_0(); i2c_Delay(); } /* ********************************************************************************************************* * 函 数 名: i2c_Start * 功能说明: CPU发起I2C总线停止信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Stop(void) { /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */ EEPROM_I2C_SDA_0(); EEPROM_I2C_SCL_1(); i2c_Delay(); EEPROM_I2C_SDA_1(); } /* ********************************************************************************************************* * 函 数 名: i2c_SendByte * 功能说明: CPU向I2C总线设备发送8bit数据 * 形 参:_ucByte : 等待发送的字节 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_SendByte(uint8_t _ucByte) { uint8_t i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) { if (_ucByte & 0x80) { EEPROM_I2C_SDA_1(); } else { EEPROM_I2C_SDA_0(); } i2c_Delay(); EEPROM_I2C_SCL_1(); i2c_Delay(); EEPROM_I2C_SCL_0(); if (i == 7) { EEPROM_I2C_SDA_1(); // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ i2c_Delay(); } } /* ********************************************************************************************************* * 函 数 名: i2c_ReadByte * 功能说明: CPU从I2C总线设备读取8bit数据 * 形 参:无 * 返 回 值: 读到的数据 ********************************************************************************************************* */ uint8_t i2c_ReadByte(void) { uint8_t i; uint8_t value; /* 读到第1个bit为数据的bit7 */ value = 0; for (i = 0; i < 8; i++) { value <<= 1; EEPROM_I2C_SCL_1(); i2c_Delay(); if (EEPROM_I2C_SDA_READ()) { value++; } EEPROM_I2C_SCL_0(); i2c_Delay(); } return value; } /* ********************************************************************************************************* * 函 数 名: i2c_WaitAck * 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号 * 形 参:无 * 返 回 值: 返回0表示正确应答,1表示无器件响应 ********************************************************************************************************* */ uint8_t i2c_WaitAck(void) { uint8_t re; EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */ i2c_Delay(); EEPROM_I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (EEPROM_I2C_SDA_READ()) /* CPU读取SDA口线状态 */ { re = 1; } else { re = 0; } EEPROM_I2C_SCL_0(); i2c_Delay(); return re; } /* ********************************************************************************************************* * 函 数 名: i2c_Ack * 功能说明: CPU产生一个ACK信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Ack(void) { EEPROM_I2C_SDA_0(); /* CPU驱动SDA = 0 */ i2c_Delay(); EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); EEPROM_I2C_SCL_0(); i2c_Delay(); EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */ } /* ********************************************************************************************************* * 函 数 名: i2c_NAck * 功能说明: CPU产生1个NACK信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_NAck(void) { EEPROM_I2C_SDA_1(); /* CPU驱动SDA = 1 */ i2c_Delay(); EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); EEPROM_I2C_SCL_0(); i2c_Delay(); } /* ********************************************************************************************************* * 函 数 名: i2c_CfgGpio * 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void i2c_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(EEPROM_I2C_GPIO_CLK, ENABLE); /* 打开GPIO时钟 */ GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; /* 开漏输出 */ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(EEPROM_I2C_GPIO_PORT, &GPIO_InitStructure); /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */ i2c_Stop(); } /* ********************************************************************************************************* * 函 数 名: i2c_CheckDevice * 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在 * 形 参:_Address:设备的I2C总线地址 * 返 回 值: 返回值 0 表示正确, 返回1表示未探测到 ********************************************************************************************************* */ uint8_t i2c_CheckDevice(uint8_t _Address) { uint8_t ucAck; i2c_Config(); /* 配置GPIO */ i2c_Start(); /* 发送启动信号 */ /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */ i2c_SendByte(_Address | EEPROM_I2C_WR); ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */ i2c_Stop(); /* 发送停止信号 */ return ucAck; }
14、SPI
.h文件
#ifndef __SPI_H #define __SPI_H #include "stm32f4xx.h" void SPI1_Init(void); //初始化SPI1口 void SPI1_SetSpeed(uint8_t SpeedSet); //设置SPI1速度 uint8_t SPI1_ReadWriteByte(uint8_t TxData); //SPI1总线读写一个字节 #endif
.c文件
#include "spi.h" //以下是SPI模块的初始化代码,配置成主机模式 //SPI口初始化 //这里针是对SPI1的初始化 void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //使能SPI1时钟 //GPIOFB3,4,5初始化设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; //PB3~5复用功能输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); //PB3复用为 SPI1 GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1); //PB4复用为 SPI1 GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1); //PB5复用为 SPI1 //这里只针对SPI口初始化 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE); //复位SPI1 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE); //停止复位SPI1 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式 SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_Cmd(SPI1, ENABLE); //使能SPI外设 SPI1_ReadWriteByte(0xff); //启动传输 } //SPI1速度设置函数 //SPI速度=fAPB2/分频系数 //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256 //fAPB2时钟一般为84Mhz: void SPI1_SetSpeed(uint8_t SPI_BaudRatePrescaler) { assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler)); //判断有效性 SPI1->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率 SPI1->CR1 |= SPI_BaudRatePrescaler; //设置SPI1速度 SPI_Cmd(SPI1, ENABLE); //使能SPI1 } //SPI1 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 uint8_t SPI1_ReadWriteByte(uint8_t TxData) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) { } // 等待发送区空 SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) { } //等待接收完一个byte return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 }
stm32f4外设学习篇(代码集合)(三)https://developer.aliyun.com/article/1472517