stm32f4外设学习篇(代码集合)(二)

简介: stm32f4外设学习篇(代码集合)

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

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
相关文章
|
1月前
【STM32】基于HAL库的360度编码器、摇杆代码编写
【STM32】基于HAL库的360度编码器、摇杆代码编写
|
1月前
|
传感器 芯片 内存技术
STM32F103标准外设库——认识STM32(一)
STM32F103标准外设库——认识STM32(一)
126 0
STM32F103标准外设库——认识STM32(一)
|
3天前
|
存储 数据安全/隐私保护 芯片
【STM32】详解嵌入式中FLASH闪存的特性和代码示例
【STM32】详解嵌入式中FLASH闪存的特性和代码示例
【STM32】详解独立看门狗的本质和使用步骤&代码
【STM32】详解独立看门狗的本质和使用步骤&代码
|
3天前
|
传感器 数据格式
【STM32】DHT11温湿度模块传感器详解&代码
【STM32】DHT11温湿度模块传感器详解&代码
|
9天前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
22 1
|
1月前
|
传感器
STM32循迹小车原理介绍和代码示例
STM32循迹小车原理介绍和代码示例
|
1月前
stm32f4外设学习篇(代码集合)(三)
stm32f4外设学习篇(代码集合)
|
1月前
|
芯片
stm32f4外设学习篇(代码集合)(一)
stm32f4外设学习篇(代码集合)
|
1月前
|
传感器 存储 缓存
STM32--MPU6050与I2C外设
STM32--MPU6050与I2C外设