我是靠谱客的博主 美丽狗,最近开发中收集的这篇文章主要介绍stm32f4外设学习篇(代码集合)1、串口12、串口2(RS485)3、定时器(多路)4、PWM5、窗口看门狗6、独立看门狗7、GPIO(LED)8、外部中断(EXTI)9、随机数发生器10、ADC11、DAC12、DMA13、IIC14、SPI15、24C0216、W25QXX完整工程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

  • 1、串口1
  • 2、串口2(RS485)
  • 3、定时器(多路)
  • 4、PWM
  • 5、窗口看门狗
  • 6、独立看门狗
  • 7、GPIO(LED)
  • 8、外部中断(EXTI)
  • 9、随机数发生器
  • 10、ADC
  • 11、DAC
  • 12、DMA
  • 13、IIC
  • 14、SPI
  • 15、24C02
  • 16、W25QXX
  • 完整工程

芯片:STM32F407(理论支持F4所有芯片)
开发环境:KEIL5-ARM
目的:单独的文件除了24c02和W25QXX会需要依赖iic和spi其余文件都是可以直接拷贝到工程使用的
日期:2021-8-12
有一些外设并没有经过测试所以在使用过程中自行调试

1、串口1

.c文件

#include "debug_usart.h"
/**
  * @brief  DEBUG_USART GPIO 配置,工作模式配置。115200 8-N-1
  * @param  无
  * @retval 无
  */
void Debug_USART_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK | DEBUG_USART_TX_GPIO_CLK, ENABLE);
  /* 使能 UART 时钟 */
  RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE);
  /* 连接 PXx 到 USARTx_Tx*/
  GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT, DEBUG_USART_RX_SOURCE, DEBUG_USART_RX_AF);
  /*  连接 PXx 到 USARTx__Rx*/
  GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT, DEBUG_USART_TX_SOURCE, DEBUG_USART_TX_AF);

  /* 配置Tx引脚为复用功能  */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  /* 配置Rx引脚为复用功能 */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
  GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

  /* 配置串DEBUG_USART 模式 */
  USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(DEBUG_USART, &USART_InitStructure);
  USART_Cmd(DEBUG_USART, ENABLE);
}

u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA = 0; //接收状态标记

void USART1_IRQHandler(void) //串口1中断服务程序
{
  u8 Res;
  if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
  {
    Res = USART_ReceiveData(USART1); //(USART1->DR);	//读取接收到的数据

    if ((USART_RX_STA & 0x8000) == 0) //接收未完成
    {
      if (USART_RX_STA & 0x4000) //接收到了0x0d
      {
        if (Res != 0x0a)
          USART_RX_STA = 0; //接收错误,重新开始
        else
          USART_RX_STA |= 0x8000; //接收完成了
      }
      else //还没收到0X0D
      {
        if (Res == 0x0d)
          USART_RX_STA |= 0x4000;
        else
        {
          USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res;
          USART_RX_STA++;
          if (USART_RX_STA > (USART_REC_LEN - 1))
            USART_RX_STA = 0; //接收数据错误,重新开始接收
        }
      }
    }
  }
}
//buf:接收缓存首地址
//len:读到的数据长度
void get_uart_buff(uint8_t *buf, uint8_t *len)
{
  u8 rxlen = USART_RX_STA;
  u8 i = 0;
  *len = 0;                           //默认为0
  _uart_delay(0xff);                  //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
  if (rxlen == USART_RX_STA && rxlen) //接收到了数据,且接收完成了
  {
    for (i = 0; i < rxlen; i++)
    {
      buf[i] = USART_RX_BUF[i];
    }
    *len = USART_RX_STA; //记录本次数据长度
    USART_RX_STA = 0;    //清零
  }
}

//重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
  /* 发送一个字节数据到串口DEBUG_USART */
  USART_SendData(DEBUG_USART, (uint8_t)ch);
  /* 等待发送完毕 */
  while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET)
    ;
  return (ch);
}

//重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
  /* 等待串口输入数据 */
  while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE) == RESET)
    ;
  return (int)USART_ReceiveData(DEBUG_USART);
}
/*********************************************END OF FILE**********************/

.h文件

#ifndef __DEBUG_USART_H
#define	__DEBUG_USART_H

#include "stm32f4xx.h"
#include <stdio.h>

//引脚定义
/*******************************************************/
#define DEBUG_USART                             USART1
#define DEBUG_USART_CLK                         RCC_APB2Periph_USART1

#define DEBUG_USART_RX_GPIO_PORT                GPIOA
#define DEBUG_USART_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART_RX_PIN                      GPIO_Pin_10
#define DEBUG_USART_RX_AF                       GPIO_AF_USART1
#define DEBUG_USART_RX_SOURCE                   GPIO_PinSource10

#define DEBUG_USART_TX_GPIO_PORT                GPIOA
#define DEBUG_USART_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART_TX_PIN                      GPIO_Pin_9
#define DEBUG_USART_TX_AF                       GPIO_AF_USART1
#define DEBUG_USART_TX_SOURCE                   GPIO_PinSource9
#define USART_REC_LEN  													200  	//定义最大接收字节数 200
	  	
extern uint8_t  USART_RX_BUF[USART_REC_LEN]; 						//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern uint16_t USART_RX_STA;         									//接收状态标记	
/************************************************************/

// 不精确的延时
static void _uart_delay(__IO u32 nCount)
{
  for (; nCount != 0; nCount--)
    ;
}

//串口波特率
#define DEBUG_USART_BAUDRATE                    115200

void Debug_USART_Config(void);
void get_uart_buff(uint8_t *buf, uint8_t *len);
int fputc(int ch, FILE *f);

#endif /* __USART1_H */

2、串口2(RS485)

.c文件

#include "rs485.h"

// 配置USART接收中断
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  /* Configure the NVIC Preemption Priority Bits */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

  /* Enable the USARTy Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = _485_INT_IRQ;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
/*
 * 函数名:_485_Config
 * 描述  :USART GPIO 配置,工作模式配置
 * 输入  :无
 * 输出  : 无
 * 调用  :外部调用
 */
void _485_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  USART_ClockInitTypeDef USART_ClockInitStruct;

  RCC_AHB1PeriphClockCmd(_485_USART_RX_GPIO_CLK | _485_USART_TX_GPIO_CLK | _485_RE_GPIO_CLK, ENABLE);
  RCC_APB1PeriphClockCmd(_485_USART_CLK, ENABLE);
  USART_DeInit(_485_USART);

  USART_StructInit(&USART_InitStructure);
  USART_ClockStructInit(&USART_ClockInitStruct);

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Pin = _485_USART_TX_PIN;
  GPIO_Init(_485_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  GPIO_PinAFConfig(_485_USART_TX_GPIO_PORT, GPIO_PinSource3, GPIO_AF_USART2);

  GPIO_InitStructure.GPIO_Pin = _485_USART_RX_PIN;
  GPIO_Init(_485_USART_RX_GPIO_PORT, &GPIO_InitStructure);

  GPIO_PinAFConfig(_485_USART_RX_GPIO_PORT, GPIO_PinSource2, GPIO_AF_USART2);

  USART_ClockInit(_485_USART, &USART_ClockInitStruct);

  USART_InitStructure.USART_BaudRate = _485_USART_BAUDRATE; //波特率设置
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No; //无校验
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(_485_USART, &USART_InitStructure);

  USART_ITConfig(_485_USART, USART_IT_RXNE, ENABLE); //接收中断使能
  USART_ClearITPendingBit(_485_USART, USART_IT_TC);  //清除中断TC位
  USART_Cmd(_485_USART, ENABLE);                     //使能串口

  USART_ClearFlag(_485_USART, USART_FLAG_TC);

  /***********************************GPIOA 1,RS485方向控制******************************/
  GPIO_InitStructure.GPIO_Pin = _485_RE_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(_485_RE_GPIO_PORT, &GPIO_InitStructure);

  NVIC_Configuration();
  /* 使能串口接收中断 */
  USART_ITConfig(_485_USART, USART_IT_RXNE, ENABLE);
  GPIO_ResetBits(_485_RE_GPIO_PORT, _485_RE_PIN); //默认进入接收模式
}

/***************** 发送一个字符  **********************/
//使用单字节数据发送前要使能发送引脚,发送后要使能接收引脚。
void _485_SendByte(uint8_t ch)
{
  /* 发送一个字节数据到USART1 */
  USART_SendData(_485_USART, ch);
  /* 等待发送完毕 */
  while (USART_GetFlagStatus(_485_USART, USART_FLAG_TXE) == RESET)
    ;
}
/*****************  发送指定长度的字符串 **********************/
void _485_SendStr_length(uint8_t *str, uint32_t strlen)
{
  unsigned int k = 0;
  _485_TX_EN(); //	使能发送数据
  do
  {
    _485_SendByte(*(str + k));
    k++;
  } while (k < strlen);
  _485_delay(0xfff);
  _485_RX_EN(); //	使能接收数据
}

/*****************  发送字符串 **********************/
void _485_SendString(uint8_t *str)
{
  unsigned int k = 0;
  _485_TX_EN(); //	使能发送数据
  do
  {
    _485_SendByte(*(str + k));
    k++;
  } while (*(str + k) != '');
  _485_delay(0xff);
  _485_RX_EN(); //	使能接收数据
}

//中断缓存串口数据
#define UART_BUFF_SIZE 64
//接收缓存区
u8 RS485_RX_BUF[64]; //接收缓冲,最大64个字节.
//接收到的数据长度
u8 RS485_RX_CNT = 0;

void bsp_485_IRQHandler(void)
{
  u8 res;
  if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
  {
    res = USART_ReceiveData(USART2); //;读取接收到的数据USART2->DR
    if (RS485_RX_CNT < UART_BUFF_SIZE)
    {
      RS485_RX_BUF[RS485_RX_CNT] = res; //记录接收到的值
      RS485_RX_CNT = RS485_RX_CNT + 1;  //接收数据增加1
      if (RS485_RX_CNT >= UART_BUFF_SIZE)
      {
        RS485_RX_CNT = 0;
      }
    }
    else
    {
      RS485_RX_CNT = 0;
      RS485_RX_BUF[RS485_RX_CNT] = res; //记录接收到的值
    }
  }
}

//RS485查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf, u8 *len)
{
  u8 rxlen = RS485_RX_CNT;
  u8 i = 0;
  *len = 0;                           //默认为0
  _485_delay(0xff);                   //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
  if (rxlen == RS485_RX_CNT && rxlen) //接收到了数据,且接收完成了
  {
    for (i = 0; i < rxlen; i++)
    {
      buf[i] = RS485_RX_BUF[i];
    }
    *len = RS485_RX_CNT; //记录本次数据长度
    RS485_RX_CNT = 0;    //清零
  }
}

// crc16 modbus
const uint16_t polynom = 0xA001;
//uint16_t ccr_data_con = crc16bitbybit(rs485buf,length-2);
//rs485buf[length-1] = ccr_data_con&0XFF;  //低八位
//rs485buf[length-2] = ccr_data_con>>8;    //高八位

uint16_t crc16bitbybit(uint8_t *ptr, uint16_t len)
{
  uint8_t i;
  uint16_t crc = 0xffff;
  uint8_t ch;
  if (len == 0)
  {
    len = 1;
  }
  while (len--)
  {
    ch = *ptr;
    crc ^= ch;
    for (i = 0; i < 8; i++)
    {
      if (crc & 1)
      {
        crc >>= 1;
        crc ^= polynom;
      }

      else
      {
        crc >>= 1;
      }
    }
    ptr++;
  }
  return crc;
}

.h文件

#ifndef __485_H
#define __485_H

#include "stm32f4xx.h"
#include "stdio.h"

#define _485_USART USART2
#define _485_USART_CLK RCC_APB1Periph_USART2
#define _485_USART_BAUDRATE 9600

#define _485_USART_RX_GPIO_PORT GPIOA
#define _485_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define _485_USART_RX_PIN GPIO_Pin_2
#define _485_USART_RX_AF GPIO_AF_USART2
#define _485_USART_RX_SOURCE GPIO_PinSource2

#define _485_USART_TX_GPIO_PORT GPIOA
#define _485_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define _485_USART_TX_PIN GPIO_Pin_3
#define _485_USART_TX_AF GPIO_AF_USART2
#define _485_USART_TX_SOURCE GPIO_PinSource3

#define _485_RE_GPIO_PORT GPIOA
#define _485_RE_GPIO_CLK RCC_AHB1Periph_GPIOA
#define _485_RE_PIN GPIO_Pin_1

#define _485_INT_IRQ USART2_IRQn
#define _485_IRQHandler USART2_IRQHandler

/// 不精确的延时
static void _485_delay(__IO u32 nCount)
{
  for (; nCount != 0; nCount--)
    ;
}

/*控制收发引脚*/
//进入接收模式,必须要有延时等待485处理完数据
#define _485_RX_EN()                              
  _485_delay(1000);                               
  GPIO_ResetBits(_485_RE_GPIO_PORT, _485_RE_PIN); 
  _485_delay(1000);
//进入发送模式,必须要有延时等待485处理完数据
#define _485_TX_EN()                            
  _485_delay(1000);                             
  GPIO_SetBits(_485_RE_GPIO_PORT, _485_RE_PIN); 
  _485_delay(1000);

void _485_Config(void);
void _485_SendByte(uint8_t ch);
void _485_SendStr_length(uint8_t *str, uint32_t strlen);
void _485_SendString(uint8_t *str);
void rs485_modbus_print(uint8_t *rs485_write_buff, uint8_t len);
uint16_t crc16bitbybit(uint8_t *ptr, uint16_t len);

void bsp_485_IRQHandler(void);
void RS485_Receive_Data(u8 *buf, u8 *len);

#endif /* __485_H */

3、定时器(多路)

.c文件

#include "timer.h"

//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz

//通用定时器3中断初始化
void TIM3_Int_Init(u16 arr, u16 psc)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); ///使能TIM3时钟

  TIM_TimeBaseInitStructure.TIM_Period = arr;                     //自动重装载值
  TIM_TimeBaseInitStructure.TIM_Prescaler = psc;                  //定时器分频
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //初始化TIM3

  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //允许定时器3更新中断
  TIM_Cmd(TIM3, ENABLE);                     //使能定时器3

  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;              //定时器3中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;        //子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

//通用定时器2中断初始化
void TIM2_Int_Init(u16 arr, u16 psc)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); ///使能TIM3时钟

  TIM_TimeBaseInitStructure.TIM_Period = arr;                     //自动重装载值
  TIM_TimeBaseInitStructure.TIM_Prescaler = psc;                  //定时器分频
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //初始化TIM3

  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //允许定时器3更新中断
  TIM_Cmd(TIM2, ENABLE);                     //使能定时器3

  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;              //定时器3中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        //子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

//通用定时器4中断初始化
void TIM4_Int_Init(u16 arr, u16 psc)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); ///使能TIM4时钟

  TIM_TimeBaseInitStructure.TIM_Period = arr;                     //自动重装载值
  TIM_TimeBaseInitStructure.TIM_Prescaler = psc;                  //定时器分频
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure); //初始化TIM4

  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //允许定时器4更新中断
  TIM_Cmd(TIM4, ENABLE);                     //使能定时器4

  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;              //定时器4中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x04;        //子优先级4
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

//定时器2中断服务函数
void TIM2_IRQHandler(void)
{
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //溢出中断
  {
    // app程序
  }
  TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志位
}

//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
  if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //溢出中断
  {
    // app程序
  }
  TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除中断标志位
}

//定时器4中断服务函数
void TIM4_IRQHandler(void)
{
  if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) //溢出中断
  {
    // app程序
  }
  TIM_ClearITPendingBit(TIM4, TIM_IT_Update); //清除中断标志位
}

.h文件

#ifndef _TIMER_H
#define _TIMER_H

#include "stm32f4xx.h"

void TIM3_Int_Init(uint16_t arr, uint16_t psc);
void TIM2_Int_Init(uint16_t arr, uint16_t psc);
void TIM4_Int_Init(uint16_t arr, uint16_t psc);

#endif

4、PWM

.c文件

#include "pwm.h"

//TIM14 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM14_PWM_Init(u32 arr,u32 psc)
{		 					 
	//此部分需手动修改IO口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);  	//TIM14时钟使能    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 	//使能PORTF时钟	
	
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM14); //GPIOF9复用为定时器14
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;           //GPIOF9
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉
	GPIO_Init(GPIOC,&GPIO_InitStructure);              //初始化PF9
	  
	TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
	
	//初始化TIM14 Channel1 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
	TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM1 4OC1

	TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //使能TIM14在CCR1上的预装载寄存器
 
  TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能 
	
	TIM_Cmd(TIM14, ENABLE);  //使能TIM14							  
}  

.h文件

#ifndef _PWM_H
#define _PWM_H

#include "stm32f4xx.h"

//TIM_SetCompare1(TIM14,pwmval);// 可以通过这个函数占空比调节
void TIM14_PWM_Init(u32 arr,u32 psc);

#endif

5、窗口看门狗

.c文件

#include "wwdg.h"

//保存WWDG计数器的设置值,默认为最大.
uint8_t WWDG_CNT = 0X7F;
//初始化窗口看门狗
//tr   :T[6:0],计数器值
//wr   :W[6:0],窗口值
//fprer:分频系数(WDGTB),仅最低2位有效
//Fwwdg=PCLK1/(4096*2^fprer). 一般PCLK1=42Mhz
void WWDG_Init(u8 tr, u8 wr, u32 fprer)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //使能窗口看门狗时钟

  WWDG_CNT = tr & WWDG_CNT; //初始化WWDG_CNT.
  WWDG_SetPrescaler(fprer); //设置分频值
  WWDG_SetWindowValue(wr);  //设置窗口值
                            //	WWDG_SetCounter(WWDG_CNT);//设置计数值
  WWDG_Enable(WWDG_CNT);    //开启看门狗

  NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;              //窗口看门狗中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级为2
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;        //子优先级为3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //使能窗口看门狗
  NVIC_Init(&NVIC_InitStructure);

  WWDG_ClearFlag(); //清除提前唤醒中断标志位
  WWDG_EnableIT();  //开启提前唤醒中断
}

//窗口看门狗中断服务程序
void WWDG_IRQHandler(void)
{
  WWDG_SetCounter(WWDG_CNT); //重设窗口看门狗值
  WWDG_ClearFlag();          //清除提前唤醒中断标志位
}

.h文件

#ifndef _WWDG_H
#define _WWDG_H

#include "stm32f4xx.h"

// WWDG_Init(0x7F,0X5F,WWDG_Prescaler_8); 	//计数器值为7f,窗口寄存器为5f,分频数为8
void WWDG_Init(u8 tr, u8 wr, u32 fprer);
void WWDG_IRQHandler(void);

#endif

6、独立看门狗

.c文件

#include "iwdg.h"

//初始化独立看门狗
//prer:分频数:0~7(只有低3位有效!)
//rlr:自动重装载值,0~0XFFF.
//分频因子=4*2^prer.但最大值只能是256!
//rlr:重装载寄存器值:低11位有效.
//时间计算(大概):Tout=((4*2^prer)*rlr)/32 (ms).
void IWDG_Init(u8 prer, u16 rlr)
{
  IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能对IWDG->PR IWDG->RLR的写
  IWDG_SetPrescaler(prer);                      //设置IWDG分频系数
  IWDG_SetReload(rlr);                          //设置IWDG装载
  IWDG_ReloadCounter();                         //reload
  IWDG_Enable();                                //使能看门狗
}

//喂独立看门狗
void IWDG_Feed(void)
{
  IWDG_ReloadCounter(); //reload
}

.h文件

#ifndef _IWDG_H
#define _IWDG_H

#include "stm32f4xx.h"

//IWDG_Init(4,500); //与分频数为64,重载值为500,溢出时间为1s
//IWDG_Feed();			//1s内必须调用一次的喂狗函数

void IWDG_Init(u8 prer, u16 rlr); //IWDG初始化
void IWDG_Feed(void);             //喂狗函数

#endif

7、GPIO(LED)

.c文件

#include "led.h"

/**  不精准延时函数   **/
static void Delay(uint32_t time)
{
  while (time--)
  {
    int i = 10000;
    for (; i > 0; i--)
      ;
  }
}

//LED IO初始化
void LED_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_GPIO_X, ENABLE); //使能GPIOF时钟

  GPIO_InitStructure.GPIO_Pin = LED0_PIN;          //LED0
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;    //普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;   //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //2MHz:led灯低速就行
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;     //上拉
  GPIO_Init(LED0_PORT, &GPIO_InitStructure);       //初始化GPIO

  GPIO_InitStructure.GPIO_Pin = LED1_PIN;    //LED0
  GPIO_Init(LED1_PORT, &GPIO_InitStructure); //初始化GPIO

  GPIO_SetBits(LED0_PORT, LED0_PIN); //设置高,灯灭
  GPIO_SetBits(LED1_PORT, LED1_PIN); //设置高,灯灭
}

/* 主函数中调用这个函数即可看到LED实验效果 */
void LED_Demo(void)
{
  LED_Init();
  while (1)
  {
    LED0_ON;
    LED1_OFF;
    Delay(0xfff);
    LED0_OFF;
    LED1_ON;
    Delay(0xfff);
  }
}

.h文件

#ifndef __LED_H
#define __LED_H
#include "stm32f4xx.h"

/**   API    **/
#define LED0_ON GPIO_ResetBits(LED0_PORT, LED0_PIN); //设置低,灯亮
#define LED0_OFF GPIO_SetBits(LED0_PORT, LED0_PIN);  //设置高,灯灭

#define LED1_ON GPIO_ResetBits(LED1_PORT, LED1_PIN);
#define LED1_OFF GPIO_SetBits(LED1_PORT, LED1_PIN);

/**   PORT AND PIN    **/
#define LED0_PIN GPIO_Pin_9
#define LED0_PORT GPIOF
#define LED1_PIN GPIO_Pin_10
#define LED1_PORT GPIOF

/**   RCC时钟    **/
#define RCC_GPIO_X RCC_AHB1Periph_GPIOF

void LED_Init(void); //初始化
#endif

8、外部中断(EXTI)

.c文件

#include "exti.h"

//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
  // app程序
  EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
  // app程序
  EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE2上的中断标志位
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
  EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
//外部中断4服务程序
void EXTI4_IRQHandler(void)
{
  EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位
}

//外部中断初始化程序
//初始化PE2~4,PA0为中断输入.
void EXTIX_Init(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  EXTI_InitTypeDef EXTI_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能SYSCFG时钟

  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2); //PE2 连接到中断线2
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3); //PE3 连接到中断线3
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4); //PE4 连接到中断线4
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); //PA0 连接到中断线0

  /* 配置EXTI_Line0 */
  EXTI_InitStructure.EXTI_Line = EXTI_Line0;             //LINE0
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    //中断事件
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;              //使能LINE0
  EXTI_Init(&EXTI_InitStructure);                        //配置

  /* 配置EXTI_Line2,3,4 */
  EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;     //中断事件
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;               //中断线使能
  EXTI_Init(&EXTI_InitStructure);                         //配置

  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;             //外部中断0
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        //子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);                              //配置

  NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;             //外部中断2
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03; //抢占优先级3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        //子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);                              //配置

  NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;             //外部中断3
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        //子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);                              //配置

  NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;             //外部中断4
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        //子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);                              //配置
}

.h文件

#ifndef __EXTI_H
#define __EXIT_H

#include "stm32f4xx.h"

/** 外部中断初始化后可以直接在服务函数中写逻辑函数 **/
void EXTIX_Init(void); //外部中断初始化

#endif

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最近接收的数据
}

15、24C02

.h文件

#ifndef __24C02_H
#define __24C02_H

#include "stm32f4xx.h"

/* 
 * 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每页有8个字节 
 * AT24C04/08A/16A每页有16个字节 
 */

#define EEPROM_DEV_ADDR 0xA0 /* 24xx02的设备地址 */
#define EEPROM_PAGE_SIZE 8   /* 24xx02的页面大小 */
#define EEPROM_SIZE 256      /* 24xx02总容量 */

uint8_t ee_Init(void);
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize);
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize);
void ee_Erase(void);
uint8_t ee_Test(void);

#endif /* __24C02_H */

.c文件

#include "_24C02.h"
#include "i2c.h"
/*
*********************************************************************************************************
*	函 数 名: ee_Init()
*	功能说明: 判断串行EERPOM是否正常
*	形    参:无
*	返 回 值: 1 表示正常, 0 表示不正常
*********************************************************************************************************
*/
uint8_t ee_Init(void)
{
  if (i2c_CheckDevice(EEPROM_DEV_ADDR) == 0)
  {
    return 1;
  }
  else
  {
    /* 失败后,切记发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
  }
}

/*
*********************************************************************************************************
*	函 数 名: ee_ReadBytes
*	功能说明: 从串行EEPROM指定地址处开始读取若干数据
*	形    参:_usAddress : 起始地址
*			 _usSize : 数据长度,单位为字节
*			 _pReadBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
  uint16_t i;

  /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */

  /* 第1步:发起I2C总线启动信号 */
  i2c_Start();

  /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); /* 此处是写指令 */

  /* 第3步:等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail; /* EEPROM器件无应答 */
  }

  /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
  i2c_SendByte((uint8_t)_usAddress);

  /* 第5步:等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail; /* EEPROM器件无应答 */
  }

  /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
  i2c_Start();

  /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD); /* 此处是读指令 */

  /* 第8步:发送ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail; /* EEPROM器件无应答 */
  }

  /* 第9步:循环读取数据 */
  for (i = 0; i < _usSize; i++)
  {
    _pReadBuf[i] = i2c_ReadByte(); /* 读1个字节 */

    /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
    if (i != _usSize - 1)
    {
      i2c_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
    }
    else
    {
      i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
    }
  }
  /* 发送I2C总线停止信号 */
  i2c_Stop();
  return 1; /* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  /* 发送I2C总线停止信号 */
  i2c_Stop();
  return 0;
}

/*
*********************************************************************************************************
*	函 数 名: ee_WriteBytes
*	功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
*	形    参:_usAddress : 起始地址
*			 _usSize : 数据长度,单位为字节
*			 _pWriteBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{
  uint16_t i, m;
  uint16_t usAddr;

  /* 
		写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
		对于24xx02,page size = 8
		简单的处理方法为:按字节写操作模式,没写1个字节,都发送地址
		为了提高连续写的效率: 本函数采用page wirte操作。
	*/

  usAddr = _usAddress;
  for (i = 0; i < _usSize; i++)
  {
    /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
    if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
    {
      /* 第0步:发停止信号,启动内部写操作 */
      i2c_Stop();

      /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms 			
				CLK频率为200KHz时,查询次数为30次左右
			*/
      for (m = 0; m < 1000; m++)
      {
        /* 第1步:发起I2C总线启动信号 */
        i2c_Start();

        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); /* 此处是写指令 */

        /* 第3步:发送一个时钟,判断器件是否正确应答 */
        if (i2c_WaitAck() == 0)
        {
          break;
        }
      }
      if (m == 1000)
      {
        goto cmd_fail; /* EEPROM器件写超时 */
      }

      /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
      i2c_SendByte((uint8_t)usAddr);

      /* 第5步:等待ACK */
      if (i2c_WaitAck() != 0)
      {
        goto cmd_fail; /* EEPROM器件无应答 */
      }
    }

    /* 第6步:开始写入数据 */
    i2c_SendByte(_pWriteBuf[i]);

    /* 第7步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
      goto cmd_fail; /* EEPROM器件无应答 */
    }

    usAddr++; /* 地址增1 */
  }

  /* 命令执行成功,发送I2C总线停止信号 */
  i2c_Stop();
  return 1;

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  /* 发送I2C总线停止信号 */
  i2c_Stop();
  return 0;
}

void ee_Erase(void)
{
  uint16_t i;
  uint8_t buf[EEPROM_SIZE];

  /* 填充缓冲区 */
  for (i = 0; i < EEPROM_SIZE; i++)
  {
    buf[i] = 0xFF;
  }

  /* 写EEPROM, 起始地址 = 0,数据长度为 256 */
  if (ee_WriteBytes(buf, 0, EEPROM_SIZE) == 0)
  {
    //		printf("擦除eeprom出错!rn");
    return;
  }
  else
  {
    //		printf("擦除eeprom成功!rn");
  }
}

16、W25QXX

.h文件

#ifndef __W25QXX_H
#define __W25QXX_H

#include "stm32f4xx.h"

//W25X系列/Q系列芯片列表
//W25Q80  ID  0XEF13
//W25Q16  ID  0XEF14
//W25Q32  ID  0XEF15
//W25Q64  ID  0XEF16
//W25Q128 ID  0XEF17
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17

#define NM25Q80 0X5213
#define NM25Q16 0X5214
#define NM25Q32 0X5215
#define NM25Q64 0X5216
#define NM25Q128 0X5217
#define NM25Q256 0X5218

extern uint16_t W25QXX_TYPE; //定义W25QXX芯片型号

#define W25QXX_CS *((volatile unsigned long *)((((GPIOB_BASE + 20) & 0xF0000000) + 0x2000000 + (((GPIOB_BASE + 20) & 0xFFFFF) << 5) + (14 << 2)))) //W25QXX的片选信号

//
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F

void W25QXX_Init(void);
uint16_t W25QXX_ReadID(void);     //读取FLASH ID
uint8_t W25QXX_ReadSR(void);      //读取状态寄存器
void W25QXX_Write_SR(uint8_t sr); //写状态寄存器
void W25QXX_Write_Enable(void);   //写使能
void W25QXX_Write_Disable(void);  //写保护
void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);    //读取flash
void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //写入flash
void W25QXX_Erase_Chip(void);                                                     //整片擦除
void W25QXX_Erase_Sector(uint32_t Dst_Addr);                                      //扇区擦除
void W25QXX_Wait_Busy(void);                                                      //等待空闲
void W25QXX_PowerDown(void);                                                      //进入掉电模式
void W25QXX_WAKEUP(void);                                                         //唤醒

// 不精确的延时
static void _w25qxx_delay(__IO uint32_t nCount)
{
  for (; nCount != 0; nCount--)
    ;
}

#endif

.c文件

#include "w25qxx.h"
#include "spi.h"

u16 W25QXX_TYPE = W25Q128; //默认是W25Q128

//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q128
//容量为16M字节,共有128个Block,4096个Sector

//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); //使能GPIOG时钟

  //GPIOB14
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;         //PB14
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;      //输出
  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_InitStructure.GPIO_Pin = GPIO_Pin_7; //PG7
  GPIO_Init(GPIOG, &GPIO_InitStructure);    //初始化

  GPIO_SetBits(GPIOG, GPIO_Pin_7);        //PG7输出1,防止NRF干扰SPI FLASH的通信
  W25QXX_CS = 1;                          //SPI FLASH不选中
  SPI1_Init();                            //初始化SPI
  SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //设置为21M时钟
  W25QXX_TYPE = W25QXX_ReadID();          //读取FLASH ID.
}

//读取W25QXX的状态寄存器
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
u8 W25QXX_ReadSR(void)
{
  u8 byte = 0;
  W25QXX_CS = 0;                          //使能器件
  SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
  byte = SPI1_ReadWriteByte(0Xff);        //读取一个字节
  W25QXX_CS = 1;                          //取消片选
  return byte;
}
//写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void W25QXX_Write_SR(uint8_t sr)
{
  W25QXX_CS = 0;                           //使能器件
  SPI1_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
  SPI1_ReadWriteByte(sr);                  //写入一个字节
  W25QXX_CS = 1;                           //取消片选
}
//W25QXX写使能
//将WEL置位
void W25QXX_Write_Enable(void)
{
  W25QXX_CS = 0;                        //使能器件
  SPI1_ReadWriteByte(W25X_WriteEnable); //发送写使能
  W25QXX_CS = 1;                        //取消片选
}
//W25QXX写禁止
//将WEL清零
void W25QXX_Write_Disable(void)
{
  W25QXX_CS = 0;                         //使能器件
  SPI1_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
  W25QXX_CS = 1;                         //取消片选
}
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
u16 W25QXX_ReadID(void)
{
  u16 Temp = 0;
  W25QXX_CS = 0;
  SPI1_ReadWriteByte(0x90); //发送读取ID命令
  SPI1_ReadWriteByte(0x00);
  SPI1_ReadWriteByte(0x00);
  SPI1_ReadWriteByte(0x00);
  Temp |= SPI1_ReadWriteByte(0xFF) << 8;
  Temp |= SPI1_ReadWriteByte(0xFF);
  W25QXX_CS = 1;
  return Temp;
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  u16 i;
  W25QXX_CS = 0;                              //使能器件
  SPI1_ReadWriteByte(W25X_ReadData);          //发送读取命令
  SPI1_ReadWriteByte((u8)((ReadAddr) >> 16)); //发送24bit地址
  SPI1_ReadWriteByte((u8)((ReadAddr) >> 8));
  SPI1_ReadWriteByte((u8)ReadAddr);
  for (i = 0; i < NumByteToRead; i++)
  {
    pBuffer[i] = SPI1_ReadWriteByte(0XFF); //循环读数
  }
  W25QXX_CS = 1;
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  u16 i;
  W25QXX_Write_Enable();                       //SET WEL
  W25QXX_CS = 0;                               //使能器件
  SPI1_ReadWriteByte(W25X_PageProgram);        //发送写页命令
  SPI1_ReadWriteByte((u8)((WriteAddr) >> 16)); //发送24bit地址
  SPI1_ReadWriteByte((u8)((WriteAddr) >> 8));
  SPI1_ReadWriteByte((u8)WriteAddr);
  for (i = 0; i < NumByteToWrite; i++)
    SPI1_ReadWriteByte(pBuffer[i]); //循环写数
  W25QXX_CS = 1;                    //取消片选
  W25QXX_Wait_Busy();               //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  u16 pageremain;
  pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
  if (NumByteToWrite <= pageremain)
    pageremain = NumByteToWrite; //不大于256个字节
  while (1)
  {
    W25QXX_Write_Page(pBuffer, WriteAddr, pageremain);
    if (NumByteToWrite == pageremain)
      break; //写入结束了
    else     //NumByteToWrite>pageremain
    {
      pBuffer += pageremain;
      WriteAddr += pageremain;

      NumByteToWrite -= pageremain; //减去已经写入了的字节数
      if (NumByteToWrite > 256)
        pageremain = 256; //一次可以写入256个字节
      else
        pageremain = NumByteToWrite; //不够256个字节了
    }
  };
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  u32 secpos;
  u16 secoff;
  u16 secremain;
  u16 i;
  u8 *W25QXX_BUF;
  W25QXX_BUF = W25QXX_BUFFER;
  secpos = WriteAddr / 4096; //扇区地址
  secoff = WriteAddr % 4096; //在扇区内的偏移
  secremain = 4096 - secoff; //扇区剩余空间大小
  //printf("ad:%X,nb:%Xrn",WriteAddr,NumByteToWrite);//测试用
  if (NumByteToWrite <= secremain)
    secremain = NumByteToWrite; //不大于4096个字节
  while (1)
  {
    W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容
    for (i = 0; i < secremain; i++)               //校验数据
    {
      if (W25QXX_BUF[secoff + i] != 0XFF)
        break; //需要擦除
    }
    if (i < secremain) //需要擦除
    {
      W25QXX_Erase_Sector(secpos);    //擦除这个扇区
      for (i = 0; i < secremain; i++) //复制
      {
        W25QXX_BUF[i + secoff] = pBuffer[i];
      }
      W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); //写入整个扇区
    }
    else
      W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.
    if (NumByteToWrite == secremain)
      break; //写入结束了
    else     //写入未结束
    {
      secpos++;   //扇区地址增1
      secoff = 0; //偏移位置为0

      pBuffer += secremain;        //指针偏移
      WriteAddr += secremain;      //写地址偏移
      NumByteToWrite -= secremain; //字节数递减
      if (NumByteToWrite > 4096)
        secremain = 4096; //下一个扇区还是写不完
      else
        secremain = NumByteToWrite; //下一个扇区可以写完了
    }
  };
}
//擦除整个芯片
//等待时间超长...
void W25QXX_Erase_Chip(void)
{
  W25QXX_Write_Enable(); //SET WEL
  W25QXX_Wait_Busy();
  W25QXX_CS = 0;                      //使能器件
  SPI1_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
  W25QXX_CS = 1;                      //取消片选
  W25QXX_Wait_Busy();                 //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个山区的最少时间:150ms
void W25QXX_Erase_Sector(uint32_t Dst_Addr)
{
  //监视falsh擦除情况,测试用
  // 	printf("fe:%xrn",Dst_Addr);
  Dst_Addr *= 4096;
  W25QXX_Write_Enable(); //SET WEL
  W25QXX_Wait_Busy();
  W25QXX_CS = 0;                              //使能器件
  SPI1_ReadWriteByte(W25X_SectorErase);       //发送扇区擦除指令
  SPI1_ReadWriteByte((u8)((Dst_Addr) >> 16)); //发送24bit地址
  SPI1_ReadWriteByte((u8)((Dst_Addr) >> 8));
  SPI1_ReadWriteByte((u8)Dst_Addr);
  W25QXX_CS = 1;      //取消片选
  W25QXX_Wait_Busy(); //等待擦除完成
}
//等待空闲
void W25QXX_Wait_Busy(void)
{
  while ((W25QXX_ReadSR() & 0x01) == 0x01)
    ; // 等待BUSY位清空
}
//进入掉电模式
void W25QXX_PowerDown(void)
{
  W25QXX_CS = 0;                      //使能器件
  SPI1_ReadWriteByte(W25X_PowerDown); //发送掉电命令
  W25QXX_CS = 1;                      //取消片选
  _w25qxx_delay(0xff);                //等待TPD
}
//唤醒
void W25QXX_WAKEUP(void)
{
  W25QXX_CS = 0;                             //使能器件
  SPI1_ReadWriteByte(W25X_ReleasePowerDown); //  send W25X_PowerDown command 0xAB
  W25QXX_CS = 1;                             //取消片选
  _w25qxx_delay(0xff);                       //等待TRES1
}

完整工程

在这里插入图片描述
在这里插入图片描述

天翼云盘:下载链接:https://cloud.189.cn/t/uINbm2F7bYrm (访问码:5ptj)
码云:https://gitee.com/stylle/stm32f4-bsp-all/tree/master

最后

以上就是美丽狗为你收集整理的stm32f4外设学习篇(代码集合)1、串口12、串口2(RS485)3、定时器(多路)4、PWM5、窗口看门狗6、独立看门狗7、GPIO(LED)8、外部中断(EXTI)9、随机数发生器10、ADC11、DAC12、DMA13、IIC14、SPI15、24C0216、W25QXX完整工程的全部内容,希望文章能够帮你解决stm32f4外设学习篇(代码集合)1、串口12、串口2(RS485)3、定时器(多路)4、PWM5、窗口看门狗6、独立看门狗7、GPIO(LED)8、外部中断(EXTI)9、随机数发生器10、ADC11、DAC12、DMA13、IIC14、SPI15、24C0216、W25QXX完整工程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(82)

评论列表共有 0 条评论

立即
投稿
返回
顶部