IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于野火霸道的 STM32F103 代码集合 -> 正文阅读

[嵌入式]基于野火霸道的 STM32F103 代码集合

固件库模板

GPIO 输入输出

	
	GPIO_InitTypeDef GPIO_InitStruct;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	// 打开 GPIOB 的时钟
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;		// 推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;				// PB0 端口引脚
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;		// 50MHZ速度
    
    GPIO_Init(GPIOB, &GPIO_InitStruct);					// 初始化
	
	GPIO_SetBits(GPIOB, GPIO_Pin_0);	// 高电平
	GPIO_ResetBits(GPIOB, GPIO_Pin_0);	// 低电平
    

按键

以野火STM32F103ZET6为例,按键有硬件消抖功能,故不用延时

//按下高电平
void KEY_GPIO_Config()
{
	GPIO_InitTypeDef GPIO_InitStruct;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;		// 浮空输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ;					// PA0
    // 按键输入没有规定速度
    
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}


// 按键判断函数 Key(GPIOA, GPIO_Pin_0)
void Key(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 1)
	{
		·······
		//松手检测
		while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 1);
	}
	else
	{
		······
	}
}

位带操作

// sys.h

#ifndef __SYS_H
#define __SYS_H

#include "stm32f10x.h"

//定义GPIO结构体位域
typedef struct
{
   volatile unsigned short bit0 : 1;
   volatile unsigned short bit1 : 1;
   volatile unsigned short bit2 : 1;
   volatile unsigned short bit3 : 1;
   volatile unsigned short bit4 : 1;
   volatile unsigned short bit5 : 1;
   volatile unsigned short bit6 : 1;
   volatile unsigned short bit7 : 1;
   volatile unsigned short bit8 : 1;
   volatile unsigned short bit9 : 1;
   volatile unsigned short bit10 : 1;
   volatile unsigned short bit11 : 1;
   volatile unsigned short bit12 : 1;
   volatile unsigned short bit13 : 1;
   volatile unsigned short bit14 : 1;
   volatile unsigned short bit15 : 1;
 
} GPIO_Bit_TypeDef;


#define PORTA_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOA->ODR)))
#define PORTA_IN     ((GPIO_Bit_TypeDef *)(&(GPIOA->IDR)))
#define PORTB_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOB->ODR)))
#define PORTB_IN     ((GPIO_Bit_TypeDef *)(&(GPIOB->IDR)))
#define PORTC_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOC->ODR)))
#define PORTC_IN     ((GPIO_Bit_TypeDef *)(&(GPIOC->IDR)))
#define PORTD_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOD->ODR)))
#define PORTD_IN     ((GPIO_Bit_TypeDef *)(&(GPIOD->IDR)))
#define PORTE_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOE->ODR)))
#define PORTE_IN     ((GPIO_Bit_TypeDef *)(&(GPIOE->IDR)))
#define PORTF_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOF->ODR)))
#define PORTF_IN     ((GPIO_Bit_TypeDef *)(&(GPIOF->IDR)))
#define PORTG_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOG->ODR)))
#define PORTG_IN     ((GPIO_Bit_TypeDef *)(&(GPIOG->IDR)))

#define PAout(n) (PORTA_OUT->bit##n)
#define PAin(n)  (PORTA_IN->bit##n)
#define PBout(n) (PORTB_OUT->bit##n)
#define PBin(n)  (PORTB_IN->bit##n)
#define PCout(n) (PORTC_OUT->bit##n)
#define PCin(n)  (PORTC_IN->bit##n)
#define PDout(n) (PORTD_OUT->bit##n)
#define PDin(n)  (PORTD_IN->bit##n)
#define PEout(n) (PORTE_OUT->bit##n)
#define PEin(n)  (PORTE_IN->bit##n)
#define PFout(n) (PORTF_OUT->bit##n)
#define PFin(n)  (PORTF_IN->bit##n)
#define PGout(n) (PORTG_OUT->bit##n)
#define PGin(n)  (PORTG_IN->bit##n)

#define CLK_PA	 	RCC_APB2Periph_GPIOA
#define CLK_PB	 	RCC_APB2Periph_GPIOB
#define CLK_PC	 	RCC_APB2Periph_GPIOC
#define CLK_PD	 	RCC_APB2Periph_GPIOD
#define CLK_PE	 	RCC_APB2Periph_GPIOE
#define CLK_PF	 	RCC_APB2Periph_GPIOF
#define CLK_PG	 	RCC_APB2Periph_GPIOG

#define HIGH		1
#define LOW			0

void Pin_out(uint32_t Periph_CLK, uint16_t GPIO_Pin, uint16_t elec_level);

#endif

// sys.c

#include "sys.h"

void Pin_out(uint32_t Periph_CLK, uint16_t GPIO_Pin, uint16_t elec_level)
{
	
	GPIO_InitTypeDef GPIO_InitStruct;
    
    RCC_APB2PeriphClockCmd(Periph_CLK, ENABLE);
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
	if(Periph_CLK == CLK_PA)
	{
		GPIO_Init(GPIOA, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOA, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOA, GPIO_Pin);
		}	
	}
	else if(Periph_CLK == CLK_PB)
	{
		GPIO_Init(GPIOB, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOB, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOB, GPIO_Pin);
		}
	}
	else if(Periph_CLK == CLK_PC)
	{
		GPIO_Init(GPIOC, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOC, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOC, GPIO_Pin);
		}
	}
	else if(Periph_CLK == CLK_PD)
	{
		GPIO_Init(GPIOD, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOD, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOD, GPIO_Pin);
		}
	}
	else if(Periph_CLK == CLK_PE)
	{
		GPIO_Init(GPIOE, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOE, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOE, GPIO_Pin);
		}
	}
	else if(Periph_CLK == CLK_PF)
	{
		GPIO_Init(GPIOF, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOF, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOF, GPIO_Pin);
		}
	}
	else if(Periph_CLK == CLK_PG)
	{
		GPIO_Init(GPIOG, &GPIO_InitStruct);
		if(elec_level)
		{
			GPIO_SetBits(GPIOG, GPIO_Pin);
		}
		else
		{
			GPIO_ResetBits(GPIOG, GPIO_Pin);
		}
	}
}

//************************************************************

位带操作例题:

main.c

#include "stm32f10x.h"
#include "sys.h"



int main()
{
	GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	// 打开 GPIOB 的时钟
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;			// 推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;					// PB0 端口引脚
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;			// 50MHZ速度11
    GPIO_Init(GPIOB, &GPIO_InitStruct);						// 初始化
	
	PBout(0) = 1;	// 初始化端口之后才可以用

	//Pin_out(CLK_PB, GPIO_Pin_0, HIGH);	// 即一个函数直接 PB0 输出高电平
	return 0;
}

系统时钟

请添加图片描述

// BSP_RCC_clkconfig.h

#ifndef __BSP_RCC_CLKCONFIG_H
#define __BSP_RCC_CLKCONFIG_H

#include "stm32f10x.h"

void HSE_SetSysClk(uint32_t RCC_PLLMul_x);
void MCO_GPIO_Config();

#endif

// BSP_RCC_clkconfig.c

 #include "BSP_RCC_clkconfig.h"

void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
	ErrorStatus HSEStatus;   //变量初始化定义
	
	//把RCC寄存器复位成复位值
	RCC_DeInit();
	
	//打开外部高速时钟晶振HSE
	RCC_HSEConfig(RCC_HSE_ON);	//使能外部的高速时钟
	
	//等待外部高速时钟晶振工作,因为HSE是8M的无源晶振,配备了谐振电容,启动需要时间
	HSEStatus = RCC_WaitForHSEStartUp();  //   ^_^ 等~
	
	if(HSEStatus == SUCCESS)	//HSE启动成功    ^o^
	{
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);	//使能预取指
		
		//SYSCLK 周期与闪存访问时间的比例设置,这里统一设置为 2
		FLASH_SetLatency(FLASH_Latency_2);
		/*设置为 2 的时候,SYSCLK低于48M也可以工作,如果设置成0或1的时候,
		如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了*/
		
		//AHB预分频因子设置为1分频
		RCC_HCLKConfig(RCC_SYSCLK_Div1);	//HCLK = SYSCLK     72MHZ
		
		//APB1预分频因子设置为2分频
		RCC_PCLK1Config(RCC_HCLK_Div2);		//PCLK1 = HCLK/2	36MHZ
		
		//APB2预分频因子设置为1分频
		RCC_PCLK2Config(RCC_HCLK_Div1);		//PCLK2 =  HCLK		72MHZ
		
		//配置锁相环   PLLCLK = HSE * RCC_PLLMul_x
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);	//倍频
		
		//使能PLLCLK
		RCC_PLLCmd(ENABLE);   //使用PLL锁相环
		
		//等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
		
		//PLL稳定之后,选择系统时钟
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);	//选择PLL作为系统时钟
		
		while(RCC_GetSYSCLKSource() != 0x08);		//等待使用PLL作为系统时钟源
	/** 	- 返回值为下列其中之一:
      *     - 0x00: HSI用作系统时钟
      *     - 0x04: HSE用作系统时钟
      *     - 0x08: PLL用作系统时钟
	  */
	
	/*-------------打开相应外设时钟----------------------
	
	使能APB2外设的GPIOx 的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx,ENABLE);
	
	在我们给GPIO端口使能之前就已经默认设置好时钟为72MHZ 
	
	-----------------------------------------------------*/
	}
	else
	{
		/*如果HSE启动失败,用户可以在这里添加处理错误代码*/
	}
}

void MCO_GPIO_Config()
{
	
	GPIO_InitTypeDef GPIO_InitStruct;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOB, &GPIO_InitStruct);
	
}

main.c

#include "stm32f10x.h"
#include "BSP_RCC_clkconfig.h"
#include "sys.h"

void Delay(uint32_t t)
{
	while(t--);
}

int main()
{
	//配置时钟频率
	HSE_SetSysClk(RCC_PLLMul_2); //RCC_PLLMul_x  (x = 1~16)
	
	MCO_GPIO_Config();				// PA8  外接示波器
	RCC_MCOConfig(RCC_MCO_SYSCLK);	//在示波器上显示波形图,显示频率
	
	while(1)
	{ 
		Pin_out(CLK_PB, GPIO_Pin_0, HIGH);
		Delay(0xFFFFF);
		Pin_out(CLK_PB, GPIO_Pin_0, LOW);
		Delay(0xFFFFF);
	}
	return 0;
}

中断

中断/事件线

请添加图片描述

EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0和EXTI15,还有4个用于特点的外设事件。它们如下:

EXTI0中断/事件线:输入由PA0~PI0等组成。
EXTI1中断/事件线:输入由PA1~PI1等组成。
EXTI2中断/事件线:输入由PA2~PI2等组成。
EXTI3中断/事件线:输入由PA3~PI3等组成。
EXTI4中断/事件线:输入由PA4~PI4等组成。
EXTI5中断/事件线:输入由PA5~PI5等组成。
EXTI6中断/事件线:输入由PA6~PI6等组成。
EXTI7中断/事件线:输入由PA7~PI7等组成。
EXTI8中断/事件线:输入由PA8~PI8等组成。
EXTI9中断/事件线:输入由PA9~PI9等组成。
EXTI10中断/事件线:输入由PA10~PI10等组成。
EXTI11中断/事件线:输入由PA11~PI11等组成。
EXTI12中断/事件线:输入由PA12~PI12等组成。
EXTI13中断/事件线:输入由PA13~PI13等组成。
EXTI14中断/事件线:输入由PA14~PI15等组成。
EXTI15中断/事件线:输入由PA15~PI15等组成。
EXTI16中断/事件线:PVD输出。
EXTI17中断/事件线:RTC闹钟事件。
EXTI18中断/事件线:USB唤醒事件。
EXTI19中断/事件线:以太网唤醒事件(只适用互联型)。

// BSP_Exti.h

#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H

#include "stm32f10x.h"

void EXTI_key_Config();
void Delay_ms(uint32_t t);
#endif

// BSP_Exti.c

/*************************************************************************************************************************

中断编程配置一般有3个要点:
1)使能外设某个中断,这个具体由每个每个外设的相关中断使能位控制。(配置NVIC_Config()函数)
2)初始化 NVIC_InitTypDef结构体,配置中断优先优先极分组,设置抢占优先级和子优先级,使能中断请求。(配置EXTI_Config()函数)
3)编写中断服务函数。(编写中断服务函数)



1、初始化要连接到EXTI的GPIO
2、初始化EXTI用于产生中断/事件
3、初始化NVIC,用于处理中断
4、编写中断服务函数
5、main()函数

**************************************************************************************************************************/


#include "BSP_Exti.h"


void Delay_ms(uint32_t t)
{
	uint32_t i = 0;
	SysTick_Config(72000); //改为72就是 us 级的定时
	for(i = 0; i < t; i++)
	{
		while( !((SysTick->CTRL)  & (1 << 16)));
	}
	SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);	//关闭定时器
}


void EXTI_NVIC_Config()
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);	//抢占优先级可选 0~1,响应优先级可选 0~7
   /**																  主         子
	*第0组:所有4位用于指定响应优先级                                 0		    0~15
	*第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级	  0~1		0~7
	*第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级	  0~3		0~3
	*第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级	  0~7		0~1
	*第4组:所有4位用于指定抢占式优先级								  0~15      0
    **/
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; /*引脚0~4的对应输入 EXTIx_IRQn(x:0~4)    引脚5~9输入EXTI9_5_IRQn    引脚10~15输入EXTI15_10_IRQn */	
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;	 //抢占优先级为 1
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;			 //响应优先级为 1
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;			 //使能EXTI0中断
	
	NVIC_Init(&NVIC_InitStruct);	//初始化以上参数
}


void EXTI_key_Config()
{	
	GPIO_InitTypeDef GPIO_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
    
	//配置中断优先级
	EXTI_NVIC_Config(); 
	  
	//1、初始化要连接到 EXTI 的GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入,就是GPIO默认电平由外部决定
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//2、初始化EXTI用于产生中断/事件
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);	//打开时钟, 为了就是把GPIO作为中断输入源, 中断必须打开AFIO时钟
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);	//选择信号源 PA0 
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;				 // 中断线: Pin_0 引脚
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;	 //EXTI0 为中断模式     选用产生中断或者产生事件
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;	 //上升沿中断,即PA0端口为高电平触发中断
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;				 //使能中断
	EXTI_Init(&EXTI_InitStruct);
	/*
	EXTI_Line:EXTI中断/事件线选择,可选 EXTI0 至 EXTI19,可参考表 18-1 选择。
	
	EXTI_Mode:EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。
	
	EXTI_Trigger:EXTI 边沿触发事件,可选上升沿触发(EXTI_Trigger_Rising)、下降 沿 触 发 ( EXTI_Trigger_Falling) 
	或 者 上 升 沿 和 下 降 沿 都 触 发( EXTI_Trigger_Rising_Falling)。
	
	EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用(DISABLE)。
	*/

	/********中断服务函数命名类型*********************

	引脚0 ~ 4
	void EXTI0_IRQHandler()   
	void EXTI1_IRQHandler()      
	void EXTI2_IRQHandler()      
	void EXTI3_IRQHandler()    
	void EXTI4_IRQHandler()   
	
	//引脚5 ~ 9
	void EXTI9_5_IRQHandler() 
	
	//引脚10 ~ 15
	void EXTI15_10_IRQHandler()  		
	
	************************************************/
	
}

1)在 stm32f10x_it.c 中编写中断服务函数

中断服务函数命名类型:
引脚0 ~ 4
void EXTI0_IRQHandler()
void EXTI1_IRQHandler()
void EXTI2_IRQHandler()
void EXTI3_IRQHandler()
void EXTI4_IRQHandler()
//引脚5 ~ 9
void EXTI9_5_IRQHandler()
//引脚10 ~ 15
void EXTI15_10_IRQHandler()

void EXTI0_IRQHandler()		//中断服务函数有名字要求 “ void PPP_IRQHandler(void) ”
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
		GPIOB->ODR ^= GPIO_Pin_0;
		Delay_ms(300);
    }
    GPIO_SetBits(GPIOB, GPIO_Pin_0);
    EXTI_ClearITPendingBit(EXTI_Line0);  //清除中断
}

系统滴答定时器

// BSP_Systick.h

#ifndef __BSP_SYSTICK_H
#define __BSP_SYSTICK_H

#include "stm32f10x.h"
#include "core_cm3.h"

void Systick_Delay_us(uint32_t us);
void Systick_Delay_ms(uint32_t ms);

#endif

// BSP_Systick.c

#include "BSP_Systick.h"

void Systick_Delay_us(uint32_t us)
{
	uint32_t i = 0;
	//t = reload * (1 / clk)     reload = sysclk = 72MHZ
	SysTick_Config(72);	//Clk = 72M时,t = (72)*(1/72M) = 1us   即reload = 72  
	
	for(i = 0; i < us; i++)
	{
		while( !((SysTick->CTRL) & (1 << 16)) );
	}
	SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);
}

void Systick_Delay_ms(uint32_t ms)    //如果编译错误就添加 "core_cm3.h" 头文件
{
	uint32_t i = 0;
	//t = reload * (1 / clk)     reload = sysclk = 72MHZ       t 不能大于 2^24 = 16777216
	SysTick_Config(72000);	//Clk = 72000M时,t = (72000)*(1/72M) = 1ms   即reload = 72000    就接受72000个脉冲用时1ms
	
	
	for(i = 0; i < ms; i++)
	{
		while( !((SysTick->CTRL) & (1 << 16)) );
	}
	SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);	//关闭定时器,一定要记得关闭
}


//=*********************SysTick_Config()函数的原型****************************

//					          一下内容不编译				
	
#if 0	
	static __INLINE uint32_t SysTick_Config(uint32_t ticks)
	{ 
	  
	  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);           
	 /* 因为ticks这个参数是脉冲计数值,要被保存到重载寄存器 STK_LOAD 寄存器中,
	  再由硬件把STK_LOAD值加载到当前计数值寄存器STK_VAL使用的,
	  因为STK_LOAD和STK_VAL都是24位,所以在输入ticks大于可存储的最大值时,由这行代码检查出错误返回。*/

	  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 
	 /* 当ticks的值没有错误后,就把ticks-1赋值到STK_LOAD寄存器中(注意是减1),
	  当STK_VAL从ticks-1向下计数到0,则就经过了ticks个脉冲。
	  (这里面用到的SysTick_LOAD_RELOAD_Msk宏(在keil环境下跟踪查看定义),
	  这个宏是用来指示寄存器的“特定位置”或者是用来“位屏蔽”,所以叫位指示宏或位屏蔽宏,
	  这个在操作寄存器代码中经常用到,通常都是用作控制寄存器某些位(“或”和“与”操作))*/


	  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
	  // 设置SysTick 中断优先级,默认为最低的优先级(1 << 4) - 1 = 16 - 1 = 15
	 /* 调用了NVIC_SetPriority()函数配置了SysTick中断,
	  所以我们就不需要再去手动配置。配置完后把STK_VAL寄存器重新赋值为0。*/  
	  
	  
	  SysTick->VAL    = 0;                                        
	  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |  //使能定时器,就是打开定时器				
					   SysTick_CTRL_TICKINT_Msk   | 
					   SysTick_CTRL_ENABLE_Msk;               
	  /*向STK_CTRL写入SysTick timer的控制参数,配置为时钟,使能为计数至0引起中断,使能SysTick,
	  接着SysTick就开始运行,做脉冲计数。
	  (如果想使用AHB/8作为时钟,可以调用库函数SysTick_CLKSourceConfig()或者修改SysTick->CTRL的使能配置)*/

	  return (0);                                                 
	}

//******************************************************************************
#endif 	

串口通讯 USART1

在这里插入图片描述
勾选上 Use McroLIB 才可以用 printf() 函数请添加图片描述

// bsp_usart.h

#ifndef __USART_H
#define __USART_H


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

/**
  * 串口宏定义,不同的串口挂载的总线不一样,移植时需要修改这几个宏
  */
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd

#define  DEBUG_USART_TX_GPIO_PORT       GPIOA
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

void USART_Config(void);
void Usart_SendByte( USART_TypeDef *pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef *pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef *pUSARTx, uint16_t ch);

#endif /* __USART_H */

// bsp_usart.c

#include "bsp_usart.h"

/**
 * @brief  USART GPIO 配置,工作参数配置
 * @param  无
 * @retval 无
 */
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口GPIO的时钟
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);  // PA时钟

    // 打开串口外设的时钟
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);  // RCC_APB2Periph_USART1  打开串口1的时钟

    // 将USART Tx的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;   // Pin_9引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); // GPIOA 使能

    // 将USART Rx的GPIO配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;      // Pin_10引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;       // 浮空输入
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);   // GPIOA 使能

    // 配置串口的工作参数
    // 配置波特率
    USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;   //115200 波特率
    // 指定帧中发送或接收的数据比特数
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;  //8位或者9位
    // 配置停止位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;      //停止位 :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_USARTx, &USART_InitStructure);    //USART1 完成串口1的初始化

    // 使能串口
    USART_Cmd(DEBUG_USARTx, ENABLE);                   // 启用USART1 的外设
}


/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef *pUSARTx, uint8_t ch)
{
    /* 发送一个字节数据到USART */
    USART_SendData(pUSARTx, ch);

    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef *pUSARTx, char *str)
{
    unsigned int k = 0;
    do
    {
        Usart_SendByte( pUSARTx, *(str + k) );
        k++;
    }
    while(*(str + k) != '\0');

    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET)
    {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef *pUSARTx, uint16_t ch)
{
    uint8_t temp_h, temp_l;

    /* 取出高八位 */
    temp_h = (ch & 0XFF00) >> 8;
    /* 取出低八位 */
    temp_l = ch & 0XFF;

    /* 发送高八位 */
    USART_SendData(pUSARTx, temp_h);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

    /* 发送低八位 */
    USART_SendData(pUSARTx, temp_l);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
    /* 发送一个字节数据到串口 */
    USART_SendData(DEBUG_USARTx, (uint8_t) ch);

    /* 等待发送完毕 */
    while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);

    return (ch);
}

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

    return (int)USART_ReceiveData(DEBUG_USARTx);
}

// main 函数

#include "stm32f10x.h"
#include "bsp_led.h" 
#include "bsp_usart.h" 

static void Show_Message(void);

int main(void)
{	
  char ch;
  
  /* 初始化RGB彩灯 */
  LED_GPIO_Config();
  
  /* 初始化USART 配置模式为 115200 8-N-1 */
  USART_Config();
	
  /* 打印指令输入提示信息 */
  Show_Message();
  while(1)
	{	
    /* 获取字符指令 */
    ch=getchar();
    printf("接收到字符:%c\n",ch);
    
    /* 根据字符指令控制RGB彩灯颜色 */
    switch(ch)
    {
      case '1':
        LED_RED;
      break;
      case '2':
        LED_GREEN;
      break;
      case '3':
        LED_BLUE;
      break;
      case '4':
        LED_YELLOW;
      break;
      case '5':
        LED_PURPLE;
      break;
      case '6':
        LED_CYAN;
      break;
      case '7':
        LED_WHITE;
      break;
      case '8':
        LED_RGBOFF;
      break;
      default:
        /* 如果不是指定指令字符,打印提示信息 */
        Show_Message();
        break;      
    }   
	}	
}


/**
  * @brief  打印指令输入提示信息
  * @param  无
  * @retval 无
  */
static void Show_Message(void)
{
  printf("\r\n   这是一个通过串口通信指令控制RGB彩灯实验 \n");
  printf("使用  USART  参数为:%d 8-N-1 \n",DEBUG_USART_BAUDRATE);
  printf("开发板接到指令后控制RGB彩灯颜色,指令对应如下:\n");
  printf("   指令   ------ 彩灯颜色 \n");
  printf("     1    ------    红 \n");
  printf("     2    ------    绿 \n");
  printf("     3    ------    蓝 \n");
  printf("     4    ------    黄 \n");
  printf("     5    ------    紫 \n");
  printf("     6    ------    青 \n");
  printf("     7    ------    白 \n");
  printf("     8    ------    灭 \n");  
}

/*********************************************END OF FILE**********************/

DMA (直接存储器访问)

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。
DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。
在这里插入图片描述

在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。

请求
CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。
响应
DMA控制器对DMA请求判别优先级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。
传输
DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。
在DMA控制器的控制下,在存储器和外部设备之间直接进行数据传送,在传送过程中不需要中央处理器的参与。开始时需提供要传送的数据的起始位置和数据长度。
结束
当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停 止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。
由此可见,DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU的效率大为提高。

// bsp_usart_dma.h

#ifndef __USARTDMA_H
#define	__USARTDMA_H


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


// 串口工作参数宏定义
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

// 串口对应的DMA请求通道
#define  USART_TX_DMA_CHANNEL     DMA1_Channel4
// 外设寄存器地址
#define  USART_DR_ADDRESS        (USART1_BASE+0x04)  
// 一次发送的数据量
#define  SENDBUFF_SIZE            5000


void USART_Config(void);
void USARTx_DMA_Config(void);

#endif /* __USARTDMA_H */

// bsp_usart_dma.c

#include "bsp_usart_dma.h"

uint8_t SendBuff[SENDBUFF_SIZE];

/**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口GPIO的时钟
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

    // 打开串口外设的时钟
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

    // 将USART Tx的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

    // 将USART Rx的GPIO配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

    // 配置串口的工作参数
    // 配置波特率
    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_USARTx, &USART_InitStructure);

    // 使能串口
    USART_Cmd(DEBUG_USARTx, ENABLE);
}


/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef *pUSARTx, uint8_t ch)
{
    /* 发送一个字节数据到USART */
    USART_SendData(pUSARTx, ch);

    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef *pUSARTx, uint8_t *array, uint16_t num)
{
    uint8_t i;

    for(i = 0; i < num; i++)
    {
        /* 发送一个字节数据到USART */
        Usart_SendByte(pUSARTx, array[i]);

    }
    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef *pUSARTx, char *str)
{
    unsigned int k = 0;
    do
    {
        Usart_SendByte( pUSARTx, *(str + k) );
        k++;
    }
    while(*(str + k) != '\0');

    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET)
    {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef *pUSARTx, uint16_t ch)
{
    uint8_t temp_h, temp_l;

    /* 取出高八位 */
    temp_h = (ch & 0XFF00) >> 8;
    /* 取出低八位 */
    temp_l = ch & 0XFF;

    /* 发送高八位 */
    USART_SendData(pUSARTx, temp_h);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

    /* 发送低八位 */
    USART_SendData(pUSARTx, temp_l);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
    /* 发送一个字节数据到串口 */
    USART_SendData(DEBUG_USARTx, (uint8_t) ch);

    /* 等待发送完毕 */
    while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);

    return (ch);
}

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

    return (int)USART_ReceiveData(DEBUG_USARTx);
}

/**
  * @brief  USARTx TX DMA 配置,内存到外设(USART1->DR)
  * @param  无
  * @retval 无
  */
void USARTx_DMA_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;   //定义变量结构体

    // 开启DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    // 设置DMA源地址:串口数据寄存器地址*/
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;   //USART_DR 数据寄存器的地址 即外设地址
    // 内存地址(要传输的变量的指针)
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;    //数组指针,就是存储器的地址
    // 方向:从内存到外设
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //仅有外设到存储器和存储器到外设
    // 传输大小
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;  //缓冲区的大小 即 5000
    // 外设地址不增
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    // 内存地址自增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    // 外设数据单位
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //单位为 :字节
    // 内存数据单位                                                        //外设数据单位和内存数据单位必须一致
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;        //单位为 :字节
    // DMA模式,一次或者循环模式
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;      //模式是正常,即一次模式, 一般都选一次模式
    //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 这里的为循环模式
    // 优先级:中
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  //可以是: 非常高、高、中、低
    // 禁止内存到内存的传输
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //不是存储器到存储器就关掉
    // 配置DMA通道
    DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);  //完成初始化
    // 使能DMA
    DMA_Cmd (USART_TX_DMA_CHANNEL, ENABLE);				 //进行使能
}

// main.c


// DMA 存储器到外设(串口)数据传输实验

#include "stm32f10x.h"
#include "bsp_usart_dma.h"
#include "bsp_led.h"

extern uint8_t SendBuff[SENDBUFF_SIZE];
static void Delay(__IO u32 nCount); 

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
  uint16_t i;
  /* 初始化USART */
  USART_Config(); 

  /* 配置使用DMA模式 */
  USARTx_DMA_Config();
  
  /* 配置RGB彩色灯 */
  LED_GPIO_Config();

  //printf("\r\n USART1 DMA TX 测试 \r\n");
  
  /*填充将要发送的数据*/
  for(i=0;i<SENDBUFF_SIZE;i++)
  {
    SendBuff[i]	 = 'L';      //把字节 'P' 赋值数组,即放在缓存空间内
  }

  /*为演示DMA持续运行而CPU还能处理其它事情,持续使用DMA发送数据,量非常大,
  *长时间运行可能会导致电脑端串口调试助手会卡死,鼠标乱飞的情况,
  *或把DMA配置中的循环模式改为单次模式*/		
  
  /* USART1 向 DMA发出TX请求 */
  USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE); //发起请求之后自动在串口上显示数据

  /* 此时CPU是空闲的,可以干其他的事情 */  
  //例如同时控制LED
  while(1)
  {
    LED1_TOGGLE
    Delay(0xFFFFF);
  }
}

static void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}
/*********************************************END OF FILE**********************/

IIC协议

例题:I2C—读写EEPROM

// bsp_i2c_ee.h

#ifndef __I2C_EE_H
#define	__I2C_EE_H


#include "stm32f10x.h"


/**************************I2C参数定义,I2C1或I2C2********************************/
#define             EEPROM_I2Cx                                I2C1
#define             EEPROM_I2C_APBxClock_FUN                   RCC_APB1PeriphClockCmd
#define             EEPROM_I2C_CLK                             RCC_APB1Periph_I2C1
#define             EEPROM_I2C_GPIO_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             EEPROM_I2C_GPIO_CLK                        RCC_APB2Periph_GPIOB     
#define             EEPROM_I2C_SCL_PORT                        GPIOB   
#define             EEPROM_I2C_SCL_PIN                         GPIO_Pin_6
#define             EEPROM_I2C_SDA_PORT                        GPIOB 
#define             EEPROM_I2C_SDA_PIN                         GPIO_Pin_7



/* STM32 I2C 快速模式 */
#define I2C_Speed              400000  //*

/* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
#define I2Cx_OWN_ADDRESS7      0X0A   

/* AT24C01/02每页有8个字节 */
#define I2C_PageSize           8

/* AT24C04/08A/16A每页有16个字节 */
//#define I2C_PageSize           16	

/*等待超时时间*/
#define I2CT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT         ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))


/*信息输出*/
#define EEPROM_DEBUG_ON         0

#define EEPROM_INFO(fmt,arg...)           printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
#define EEPROM_ERROR(fmt,arg...)          printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...)          do{\
                                          if(EEPROM_DEBUG_ON)\
                                          printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)


/* 
 * 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 
 */

/* EEPROM Addresses defines */
#define EEPROM_Block0_ADDRESS 0xA0   /* E2 = 0 */
//#define EEPROM_Block1_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_Block2_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_Block3_ADDRESS 0xA6 /* E2 = 0 */


void I2C_EE_Init(void);
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite);
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr);
uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite);
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead);
void I2C_EE_WaitEepromStandbyState(void);


#endif /* __I2C_EE_H */

// bsp_i2c_ee.c

#include "./i2c/bsp_i2c_ee.h"
#include "./usart/bsp_usart.h"

uint16_t EEPROM_ADDRESS;

static __IO uint32_t  I2CTimeout = I2CT_LONG_TIMEOUT;

static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);

/**
  * @brief  I2C I/O配置
  * @param  无
  * @retval 无
  */
static void I2C_GPIO_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    /* 使能与 I2C 有关的时钟 */
    EEPROM_I2C_APBxClock_FUN ( EEPROM_I2C_CLK, ENABLE );  //I2c1 挂载在APB1上
    EEPROM_I2C_GPIO_APBxClock_FUN ( EEPROM_I2C_GPIO_CLK, ENABLE ); //GPIOB时钟


    /* I2C_SCL、I2C_SDA*/
    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN;	   //PB6
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;	       // 开漏输出
    GPIO_Init(EEPROM_I2C_SCL_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN;	   //PB7
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;	       // 开漏输出
    GPIO_Init(EEPROM_I2C_SDA_PORT, &GPIO_InitStructure);


}

/**
  * @brief  I2C 工作模式配置
  * @param  无
  * @retval 无
  */
static void I2C_Mode_Configu(void)
{
    I2C_InitTypeDef  I2C_InitStructure;

    /* I2C 配置 */
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;

    /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;

    I2C_InitStructure.I2C_OwnAddress1 = I2Cx_OWN_ADDRESS7;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;

    /* I2C的寻址模式 */
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

    /* 通信速率 */
    I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;

    /* I2C 初始化 */
    I2C_Init(EEPROM_I2Cx, &I2C_InitStructure);

    /* 使能 I2C */
    I2C_Cmd(EEPROM_I2Cx, ENABLE);
}


/**
  * @brief  I2C 外设(EEPROM)初始化
  * @param  无
  * @retval 无
  */
void I2C_EE_Init(void)
{
	//配置PB6, PB7端口,实际就是配置SCL、SDA I2C两条总线
    I2C_GPIO_Config();
	//配置I2C的工作模式
    I2C_Mode_Configu();

    /* 根据头文件i2c_ee.h中的定义来选择EEPROM的设备地址 */
#ifdef EEPROM_Block0_ADDRESS
    /* 选择 EEPROM Block0 来写入 */
    EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;
#endif

#ifdef EEPROM_Block1_ADDRESS
    /* 选择 EEPROM Block1 来写入 */
    EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;
#endif

#ifdef EEPROM_Block2_ADDRESS
    /* 选择 EEPROM Block2 来写入 */
    EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;
#endif

#ifdef EEPROM_Block3_ADDRESS
    /* 选择 EEPROM Block3 来写入 */
    EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;
#endif
}

/**
  * @brief   将缓冲区中的数据写到I2C EEPROM中
  * @param
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址
  *     @arg NumByteToWrite:写的字节数
  * @retval  无
  */
void I2C_EE_BufferWrite(u8 *pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{
    u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;

    Addr = WriteAddr % I2C_PageSize;
    count = I2C_PageSize - Addr;
    NumOfPage =  NumByteToWrite / I2C_PageSize;
    NumOfSingle = NumByteToWrite % I2C_PageSize;

    /* If WriteAddr is I2C_PageSize aligned  */
    if(Addr == 0)
    {
        /* If NumByteToWrite < I2C_PageSize */
        if(NumOfPage == 0)
        {
            I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            I2C_EE_WaitEepromStandbyState();
        }
        /* If NumByteToWrite > I2C_PageSize */
        else
        {
            while(NumOfPage--)
            {
                I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
                I2C_EE_WaitEepromStandbyState();
                WriteAddr +=  I2C_PageSize;
                pBuffer += I2C_PageSize;
            }

            if(NumOfSingle != 0)
            {
                I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
                I2C_EE_WaitEepromStandbyState();
            }
        }
    }
    /* If WriteAddr is not I2C_PageSize aligned  */
    else
    {
        /* If NumByteToWrite < I2C_PageSize */
        if(NumOfPage == 0)
        {
            I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            I2C_EE_WaitEepromStandbyState();
        }
        /* If NumByteToWrite > I2C_PageSize */
        else
        {
            NumByteToWrite -= count;
            NumOfPage =  NumByteToWrite / I2C_PageSize;
            NumOfSingle = NumByteToWrite % I2C_PageSize;

            if(count != 0)
            {
                I2C_EE_PageWrite(pBuffer, WriteAddr, count);
                I2C_EE_WaitEepromStandbyState();
                WriteAddr += count;
                pBuffer += count;
            }

            while(NumOfPage--)
            {
                I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
                I2C_EE_WaitEepromStandbyState();
                WriteAddr +=  I2C_PageSize;
                pBuffer += I2C_PageSize;
            }
            if(NumOfSingle != 0)
            {
                I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
                I2C_EE_WaitEepromStandbyState();
            }
        }
    }
}

/**
  * @brief   写一个字节到I2C EEPROM中
  * @param
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址
  * @retval  无
  */
uint32_t I2C_EE_ByteWrite(u8 *pBuffer, u8 WriteAddr)
{
    /* Send STRAT condition */
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV5 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
    }

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Send EEPROM address for write */
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    /* Test on EV6 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
    }
    /* Send the EEPROM's internal address to write to */
    I2C_SendData(EEPROM_I2Cx, WriteAddr);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV8 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
    }

    /* Send the byte to be written */
    I2C_SendData(EEPROM_I2Cx, *pBuffer);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV8 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
    }

    /* Send STOP condition */
    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);

    return 1;
}

/**
  * @brief   在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数
  *          不能超过EEPROM页的大小,AT24C02每页有8个字节
  * @param
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址
  *     @arg NumByteToWrite:写的字节数
  * @retval  无
  */
uint32_t I2C_EE_PageWrite(u8 *pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
    I2CTimeout = I2CT_LONG_TIMEOUT;

    while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
    }

    /* Send START condition */
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV5 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
    }

    /* Send EEPROM address for write */
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV6 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
    }

    /* Send the EEPROM's internal address to write to */
    I2C_SendData(EEPROM_I2Cx, WriteAddr);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV8 and clear it */
    while(! I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
    }

    /* While there is data to be written */
    while(NumByteToWrite--)
    {
        /* Send the current byte */
        I2C_SendData(EEPROM_I2Cx, *pBuffer);

        /* Point to the next byte to be written */
        pBuffer++;

        I2CTimeout = I2CT_FLAG_TIMEOUT;

        /* Test on EV8 and clear it */
        while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        {
            if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
        }
    }

    /* Send STOP condition */
    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);

    return 1;
}


/**
  * @brief   从EEPROM里面读取一块数据
  * @param
  *		@arg pBuffer:存放从EEPROM读取的数据的缓冲区指针
  *		@arg WriteAddr:接收数据的EEPROM的地址
  *     @arg NumByteToWrite:要从EEPROM读取的字节数
  * @retval  无
  */
uint32_t I2C_EE_BufferRead(u8 *pBuffer, u8 ReadAddr, u16 NumByteToRead)
{

    I2CTimeout = I2CT_LONG_TIMEOUT;

    //*((u8 *)0x4001080c) |=0x80;
    while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
    }

    /* Send START condition */
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
    //*((u8 *)0x4001080c) &=~0x80;

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV5 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
    }

    /* Send EEPROM address for write */
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV6 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
    }

    /* Clear EV6 by setting again the PE bit */
    I2C_Cmd(EEPROM_I2Cx, ENABLE);

    /* Send the EEPROM's internal address to write to */
    I2C_SendData(EEPROM_I2Cx, ReadAddr);


    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV8 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
    }

    /* Send STRAT condition a second time */
    I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV5 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
    }

    /* Send EEPROM address for read */
    I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Receiver);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Test on EV6 and clear it */
    while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
    }

    /* While there is data to be read */
    while(NumByteToRead)
    {
        if(NumByteToRead == 1)
        {
            /* Disable Acknowledgement */
            I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE);

            /* Send STOP Condition */
            I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
        }

        /* Test on EV7 and clear it */
        I2CTimeout = I2CT_LONG_TIMEOUT;

        while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) == 0)
        {
            if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
        }
        {
            /* Read a byte from the EEPROM */
            *pBuffer = I2C_ReceiveData(EEPROM_I2Cx);

            /* Point to the next location where the byte read will be saved */
            pBuffer++;

            /* Decrement the read bytes counter */
            NumByteToRead--;
        }
    }

    /* Enable Acknowledgement to be ready for another reception */
    I2C_AcknowledgeConfig(EEPROM_I2Cx, ENABLE);

    return 1;
}


/**
  * @brief  Wait for EEPROM Standby state
  * @param  无
  * @retval 无
  */
void I2C_EE_WaitEepromStandbyState(void)
{
    vu16 SR1_Tmp = 0;

    do
    {
        /* Send START condition */
        I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
        /* Read I2C1 SR1 register */
        SR1_Tmp = I2C_ReadRegister(EEPROM_I2Cx, I2C_Register_SR1);
        /* Send EEPROM address for write */
        I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);
    }
    while(!(I2C_ReadRegister(EEPROM_I2Cx, I2C_Register_SR1) & 0x0002));

    /* Clear AF flag */
    I2C_ClearFlag(EEPROM_I2Cx, I2C_FLAG_AF);
    /* STOP condition */
    I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
}




/**
  * @brief  Basic management of the timeout situation.
  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.
  * @retval 返回0,表示IIC读取失败.
  */
static  uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
    /* Block communication and all processes */
    EEPROM_ERROR("I2C 等待超时!errorCode = %d", errorCode);

    return 0;
}
/*********************************************END OF FILE**********************/

// mian.c

#include "stm32f10x.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./i2c/bsp_i2c_ee.h"
#include <string.h>

#define  EEP_Firstpage      0x00
uint8_t I2c_Buf_Write[256];
uint8_t I2c_Buf_Read[256];
uint8_t I2C_Test(void);

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
    LED_GPIO_Config();

    LED_BLUE;
    /* 串口初始化 */
    USART_Config();

    printf("\r\n 这是一个I2C外设(AT24C02)读写测试例程 \r\n");

    /* I2C 外设初(AT24C02)始化 */
    I2C_EE_Init();

    //EEPROM 读写测试
    if(I2C_Test() == 1)
    {
        LED_GREEN;
    }
    else
    {
        LED_RED;
    }

    while (1)
    {
    }
}

/**
  * @brief  I2C(AT24C02)读写测试
  * @param  无
  * @retval 正常返回1,异常返回0
  */
uint8_t I2C_Test(void)
{
    uint16_t i;

    printf("写入的数据\n\r");

    for ( i = 0; i <= 255; i++ ) //填充缓冲
    {
        I2c_Buf_Write[i] = i;

        printf("0x%02X ", I2c_Buf_Write[i]);
        if(i % 16 == 15)
            printf("\n\r");
    }

    //将I2c_Buf_Write中顺序递增的数据写入EERPOM中
    I2C_EE_BufferWrite( I2c_Buf_Write, EEP_Firstpage, 256);

    EEPROM_INFO("\n\r写成功\n\r");

    EEPROM_INFO("\n\r读出的数据\n\r");
    //将EEPROM读出数据顺序保持到I2c_Buf_Read中
    I2C_EE_BufferRead(I2c_Buf_Read, EEP_Firstpage, 256);

    //将I2c_Buf_Read中的数据通过串口打印
    for (i = 0; i < 256; i++)
    {
        if(I2c_Buf_Read[i] != I2c_Buf_Write[i])
        {
            EEPROM_ERROR("0x%02X ", I2c_Buf_Read[i]);
            EEPROM_ERROR("错误:I2C EEPROM写入与读出的数据不一致\n\r");
            return 0;
        }
        printf("0x%02X ", I2c_Buf_Read[i]);
        if(i % 16 == 15)
            printf("\n\r");

    }
    EEPROM_INFO("I2C(AT24C02)读写测试成功\n\r");

    return 1;
}
/*********************************************END OF FILE**********************/

SPI协议

什么是SPI
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。

SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。

SPI主从模式
SPI分为主、从两种模式,一个SPI通讯系统需要包含一个(且只能是一个)主设备,一个或多个从设备。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起。当存在多个从设备时,通过各自的片选信号进行管理。

SPI是全双工且SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps

SPI信号线
SPI接口一般使用四条信号线通信:
SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)

MISO: 主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
MOSI: 主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
SCLK:串行时钟信号,由主设备产生。
CS/SS:从设备片选信号,由主设备控制。它的功能是用来作为“片选引脚”,也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。

在这里插入图片描述

// bsp_spi_flash.h

#ifndef __SPI_FLASH_H
#define __SPI_FLASH_H

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

//#define  sFLASH_ID              0xEF3015   //W25X16
//#define  sFLASH_ID              0xEF4015	 //W25Q16
//#define  sFLASH_ID              0XEF4018   //W25Q128
#define  sFLASH_ID              0XEF4017    //W25Q64

#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256

/*命令定义-开头*******************************/
#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

/* WIP(busy)标志,FLASH内部正在写入 */
#define WIP_Flag                  0x01
#define Dummy_Byte                0xFF
/*命令定义-结尾*******************************/


/*SPI接口定义-开头****************************/
#define      FLASH_SPIx                        SPI1
#define      FLASH_SPI_APBxClock_FUN           RCC_APB2PeriphClockCmd
#define      FLASH_SPI_CLK                     RCC_APB2Periph_SPI1

//CS(NSS)引脚 片选选普通GPIO即可
#define      FLASH_SPI_CS_APBxClock_FUN        RCC_APB2PeriphClockCmd
#define      FLASH_SPI_CS_CLK                  RCC_APB2Periph_GPIOA
#define      FLASH_SPI_CS_PORT                 GPIOA
#define      FLASH_SPI_CS_PIN                  GPIO_Pin_4

//SCK引脚
#define      FLASH_SPI_SCK_APBxClock_FUN       RCC_APB2PeriphClockCmd
#define      FLASH_SPI_SCK_CLK                 RCC_APB2Periph_GPIOA
#define      FLASH_SPI_SCK_PORT                GPIOA
#define      FLASH_SPI_SCK_PIN                 GPIO_Pin_5
//MISO引脚
#define      FLASH_SPI_MISO_APBxClock_FUN      RCC_APB2PeriphClockCmd
#define      FLASH_SPI_MISO_CLK                RCC_APB2Periph_GPIOA
#define      FLASH_SPI_MISO_PORT               GPIOA
#define      FLASH_SPI_MISO_PIN                GPIO_Pin_6
//MOSI引脚
#define      FLASH_SPI_MOSI_APBxClock_FUN      RCC_APB2PeriphClockCmd
#define      FLASH_SPI_MOSI_CLK                RCC_APB2Periph_GPIOA
#define      FLASH_SPI_MOSI_PORT               GPIOA
#define      FLASH_SPI_MOSI_PIN                GPIO_Pin_7

#define  	 SPI_FLASH_CS_LOW()     		   GPIO_ResetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN )
#define  	 SPI_FLASH_CS_HIGH()    		   GPIO_SetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN )

/*SPI接口定义-结尾****************************/

/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))

/*信息输出*/
#define FLASH_DEBUG_ON         1

#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\
                                          if(FLASH_DEBUG_ON)\
                                          printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

void SPI_FLASH_Init(void);
void SPI_FLASH_SectorErase(u32 SectorAddr);
void SPI_FLASH_BulkErase(void);
void SPI_FLASH_PageWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite);
void SPI_FLASH_BufferWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite);
void SPI_FLASH_BufferRead(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead);
u32 SPI_FLASH_ReadID(void);
u32 SPI_FLASH_ReadDeviceID(void);
void SPI_FLASH_StartReadSequence(u32 ReadAddr);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);


u8 SPI_FLASH_ReadByte(void);
u8 SPI_FLASH_SendByte(u8 byte);
u16 SPI_FLASH_SendHalfWord(u16 HalfWord);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_WaitForWriteEnd(void);


#endif /* __SPI_FLASH_H */


// bsp_spi_flash.c

#include "./flash/bsp_spi_flash.h"

static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

/**
  * @brief  SPI_FLASH初始化
  * @param  无
  * @retval 无
  */
void SPI_FLASH_Init(void)
{
/**  SPI1:  
  * 
  *  NSS  --> PA4
  *	 CLK  --> PA5
  *  MISO --> PA6 
  *	 MOSI --> PA7
***/	
	
    SPI_InitTypeDef  SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 使能SPI时钟 */
    FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );  //SPI1 挂载在APB2上

    /* 使能SPI引脚相关的时钟      打开CS(NSS)、SCK、MISO、MOSI四个引脚的时钟     都在APB2上    */
    FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK | FLASH_SPI_SCK_CLK |
                                 FLASH_SPI_MISO_PIN | FLASH_SPI_MOSI_PIN, ENABLE );

    /* 配置SPI的 CS(NSS)引脚,普通IO即可 */
    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;		// PA4
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	//推挽输出
    GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);  

    /* 配置SPI的 SCK引脚*/
    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;	//PA5
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		//复用推挽输出
    GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);

    /* 配置SPI的 MISO引脚*/
    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;	//PA6
    GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);

    /* 配置SPI的 MOSI引脚*/
    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;	//PA7
    GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);

    /* 停止信号 FLASH: CS引脚高电平*/
    SPI_FLASH_CS_HIGH();  //CS(NSS) --> 高电平   通讯停止信号

    /* SPI 模式配置 */
    // FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	 //指定SPI单向或双向数据模式, 全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;						 //SPI工作模式, 主机模式
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;				     //指定SPI数据大小, 8 bit
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;							 // CPOL --> 1  SCK空闲为高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;						 // CPHA --> 0  偶数边沿采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;	//指定NSS信号是否由硬件(NSS引脚)或通过软件使用SSI位,选择软件
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;   //指定波特率预分配器的值用于配置发送和接收SCK时钟, 即4分频, 72/4 = 18MHZ
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;				 	 //指定数据传输是从MSB位还是从LSB位开始。 默认MSB先行
    SPI_InitStructure.SPI_CRCPolynomial = 7;							 //指定用于CRC计算的多项式。
    SPI_Init(FLASH_SPIx , &SPI_InitStructure);							 //参数写入寄存器

    /* 使能 SPI  */
    SPI_Cmd(FLASH_SPIx , ENABLE);										 //开启SPI外设

}
/**
 * @brief  擦除FLASH扇区
 * @param  SectorAddr:要擦除的扇区地址
 * @retval 无
 */
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
    /* 发送FLASH写使能命令 */
    SPI_FLASH_WriteEnable();
    SPI_FLASH_WaitForWriteEnd();
    /* 擦除扇区 */
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();
    /* 发送扇区擦除指令*/
    SPI_FLASH_SendByte(W25X_SectorErase);
    /*发送擦除扇区地址的高位*/
    SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    /* 发送擦除扇区地址的中位 */
    SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    /* 发送擦除扇区地址的低位 */
    SPI_FLASH_SendByte(SectorAddr & 0xFF);
    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();
    /* 等待擦除完毕*/
    SPI_FLASH_WaitForWriteEnd();
}

/**
 * @brief  擦除FLASH扇区,整片擦除
 * @param  无
 * @retval 无
 */
void SPI_FLASH_BulkErase(void)
{
    /* 发送FLASH写使能命令 */
    SPI_FLASH_WriteEnable();

    /* 整块 Erase */
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();
    /* 发送整块擦除指令*/
    SPI_FLASH_SendByte(W25X_ChipErase);
    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();

    /* 等待擦除完毕*/
    SPI_FLASH_WaitForWriteEnd();
}

/**
 * @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
 * @param	pBuffer,要写入数据的指针
 * @param WriteAddr,写入地址
 * @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
 * @retval 无
 */
void SPI_FLASH_PageWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
    /* 发送FLASH写使能命令 */
    SPI_FLASH_WriteEnable();

    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();
    /* 写页写指令*/
    SPI_FLASH_SendByte(W25X_PageProgram);
    /*发送写地址的高位*/
    SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /*发送写地址的中位*/
    SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /*发送写地址的低位*/
    SPI_FLASH_SendByte(WriteAddr & 0xFF);

    if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
    {
        NumByteToWrite = SPI_FLASH_PerWritePageSize;
        FLASH_ERROR("SPI_FLASH_PageWrite too large!");
    }

    /* 写入数据*/
    while (NumByteToWrite--)
    {
        /* 发送当前要写入的字节数据 */
        SPI_FLASH_SendByte(*pBuffer);
        /* 指向下一字节数据 */
        pBuffer++;
    }

    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();

    /* 等待写入完毕*/
    SPI_FLASH_WaitForWriteEnd();
}

/**
 * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
 * @param	pBuffer,要写入数据的指针
 * @param  WriteAddr,写入地址
 * @param  NumByteToWrite,写入数据长度
 * @retval 无
 */
void SPI_FLASH_BufferWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
    u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

    /*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
    Addr = WriteAddr % SPI_FLASH_PageSize;

    /*差count个数据值,刚好可以对齐到页地址*/
    count = SPI_FLASH_PageSize - Addr;
    /*计算出要写多少整数页*/
    NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
    /*mod运算求余,计算出剩余不满一页的字节数*/
    NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

    /* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
    if (Addr == 0)
    {
        /* NumByteToWrite < SPI_FLASH_PageSize */
        if (NumOfPage == 0)
        {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
        }
        else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
            /*先把整数页都写了*/
            while (NumOfPage--)
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr +=  SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            /*若有多余的不满一页的数据,把它写完*/
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        }
    }
    /* 若地址与 SPI_FLASH_PageSize 不对齐  */
    else
    {
        /* NumByteToWrite < SPI_FLASH_PageSize */
        if (NumOfPage == 0)
        {
            /*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
            if (NumOfSingle > count)
            {
                temp = NumOfSingle - count;
                /*先写满当前页*/
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

                WriteAddr +=  count;
                pBuffer += count;
                /*再写剩余的数据*/
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
            }
            else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
            }
        }
        else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
            /*地址不对齐多出的count分开处理,不加入这个运算*/
            NumByteToWrite -= count;
            NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

            /* 先写完count个数据,为的是让下一次要写的地址对齐 */
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

            /* 接下来就重复地址对齐的情况 */
            WriteAddr +=  count;
            pBuffer += count;
            /*把整数页都写了*/
            while (NumOfPage--)
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr +=  SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            /*若有多余的不满一页的数据,把它写完*/
            if (NumOfSingle != 0)
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            }
        }
    }
}

/**
 * @brief  读取FLASH数据
 * @param 	pBuffer,存储读出数据的指针
 * @param   ReadAddr,读取地址
 * @param   NumByteToRead,读取数据长度
 * @retval 无
 */
void SPI_FLASH_BufferRead(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();

    /* 发送 读 指令 */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* 发送 读 地址高位 */
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* 发送 读 地址中位 */
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* 发送 读 地址低位 */
    SPI_FLASH_SendByte(ReadAddr & 0xFF);

    /* 读取数据 */
    while (NumByteToRead--) /* while there is data to be read */
    {
        /* 读取一个字节*/
        *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
        /* 指向下一个字节缓冲区 */
        pBuffer++;
    }

    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();
}

/**
 * @brief  读取FLASH ID
 * @param 	无
 * @retval FLASH ID
 */
u32 SPI_FLASH_ReadID(void)
{
    u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

    /* 开始通讯:CS低电平 */
    SPI_FLASH_CS_LOW();

    /* 发送JEDEC指令,读取ID */
    SPI_FLASH_SendByte(W25X_JedecDeviceID);

    /* 读取一个字节数据 */
    Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

    /* 读取一个字节数据 */
    Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

    /* 读取一个字节数据 */
    Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

    /* 停止通讯:CS高电平 */
    SPI_FLASH_CS_HIGH();

    /*把数据组合起来,作为函数的返回值*/
    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;
}
/**
 * @brief  读取FLASH Device ID
 * @param 	无
 * @retval FLASH Device ID
 */
u32 SPI_FLASH_ReadDeviceID(void)
{
    u32 Temp = 0;

    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "RDID " instruction */
    SPI_FLASH_SendByte(W25X_DeviceID);
    SPI_FLASH_SendByte(Dummy_Byte);
    SPI_FLASH_SendByte(Dummy_Byte);
    SPI_FLASH_SendByte(Dummy_Byte);

    /* Read a byte from the FLASH */
    Temp = SPI_FLASH_SendByte(Dummy_Byte);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    return Temp;
}
/*******************************************************************************
* Function Name  : SPI_FLASH_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(u32 ReadAddr)
{
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* Send the 24-bit address of the address to read from -----------------------*/
    /* Send ReadAddr high nibble address byte */
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte */
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte */
    SPI_FLASH_SendByte(ReadAddr & 0xFF);
}


/**
 * @brief  使用SPI读取一个字节的数据
 * @param  无
 * @retval 返回接收到的数据
 */
u8 SPI_FLASH_ReadByte(void)
{
    return (SPI_FLASH_SendByte(Dummy_Byte));
}

/**
 * @brief  使用SPI发送一个字节的数据
 * @param  byte:要发送的数据
 * @retval 返回接收到的数据
 */
u8 SPI_FLASH_SendByte(u8 byte)
{
    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 等待发送缓冲区为空,TXE事件 */
    while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
    {
        if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
    }

    /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
    SPI_I2S_SendData(FLASH_SPIx , byte);

    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 等待接收缓冲区非空,RXNE事件 */
    while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
    {
        if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
    }

    /* 读取数据寄存器,获取接收缓冲区数据 */
    return SPI_I2S_ReceiveData(FLASH_SPIx );
}

/**
 * @brief  使用SPI发送两个字节的数据
 * @param  byte:要发送的数据
 * @retval 返回接收到的数据
 */
u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
{
    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 等待发送缓冲区为空,TXE事件 */
    while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
    {
        if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);
    }

    /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
    SPI_I2S_SendData(FLASH_SPIx , HalfWord);

    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 等待接收缓冲区非空,RXNE事件 */
    while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
    {
        if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);
    }
    /* 读取数据寄存器,获取接收缓冲区数据 */
    return SPI_I2S_ReceiveData(FLASH_SPIx );
}

/**
 * @brief  向FLASH发送 写使能 命令
 * @param  none
 * @retval none
 */
void SPI_FLASH_WriteEnable(void)
{
    /* 通讯开始:CS低 */
    SPI_FLASH_CS_LOW();

    /* 发送写使能命令*/
    SPI_FLASH_SendByte(W25X_WriteEnable);

    /*通讯结束:CS高 */
    SPI_FLASH_CS_HIGH();
}

/* WIP(busy)标志,FLASH内部正在写入 */
#define WIP_Flag                  0x01

/**
 * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
 * @param  none
 * @retval none
 */
void SPI_FLASH_WaitForWriteEnd(void)
{
    u8 FLASH_Status = 0;

    /* 选择 FLASH: CS 低 */
    SPI_FLASH_CS_LOW();

    /* 发送 读状态寄存器 命令 */
    SPI_FLASH_SendByte(W25X_ReadStatusReg);

    /* 若FLASH忙碌,则等待 */
    do
    {
        /* 读取FLASH芯片的状态寄存器 */
        FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
    }
    while ((FLASH_Status & WIP_Flag) == SET);  /* 正在写入标志 */

    /* 停止信号  FLASH: CS 高 */
    SPI_FLASH_CS_HIGH();
}


//进入掉电模式
void SPI_Flash_PowerDown(void)
{
    /* 通讯开始:CS低 */
    SPI_FLASH_CS_LOW();

    /* 发送 掉电 命令 */
    SPI_FLASH_SendByte(W25X_PowerDown);

    /*通讯结束:CS高 */
    SPI_FLASH_CS_HIGH();
}

//唤醒
void SPI_Flash_WAKEUP(void)
{
    /*选择 FLASH: CS 低 */
    SPI_FLASH_CS_LOW();

    /* 发送 上电 命令 */
    SPI_FLASH_SendByte(W25X_ReleasePowerDown);

    /* 停止信号 FLASH: CS 高 */
    SPI_FLASH_CS_HIGH();
}


/**
  * @brief  等待超时回调函数
  * @param  None.
  * @retval None.
  */
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
    /* 等待超时后的处理,输出错误信息 */
    FLASH_ERROR("SPI 等待超时!errorCode = %d", errorCode);
    return 0;
}

/*********************************************END OF FILE**********************/

// main.c

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./led/bsp_led.h"
#include "./flash/bsp_spi_flash.h"


typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;

/* 获取缓冲区的长度 */
#define TxBufferSize1   (countof(TxBuffer1) - 1)
#define RxBufferSize1   (countof(TxBuffer1) - 1)
#define countof(a)      (sizeof(a) / sizeof(*(a)))
#define  BufferSize (countof(Tx_Buffer)-1)

#define  FLASH_WriteAddress     0x00000
#define  FLASH_ReadAddress      FLASH_WriteAddress
#define  FLASH_SectorToErase    FLASH_WriteAddress



/* 发送缓冲区初始化 */
uint8_t Tx_Buffer[] = "感谢您选用野火stm32开发板\r\n";
uint8_t Rx_Buffer[BufferSize];

__IO uint32_t DeviceID = 0;
__IO uint32_t FlashID = 0;
__IO TestStatus TransferStatus1 = FAILED;

// 函数原型声明
void Delay(__IO uint32_t nCount);
TestStatus Buffercmp(uint8_t *pBuffer1, uint8_t *pBuffer2, uint16_t BufferLength);

/*
 * 函数名:main
 * 描述  :主函数
 * 输入  :无
 * 输出  :无
 */
int main(void)
{
    LED_GPIO_Config();
    LED_BLUE;

    /* 配置串口为:115200 8-N-1 */
    USART_Config();
    printf("\r\n 这是一个8Mbyte串行flash(W25Q64)实验 \r\n");

    /* 8M串行flash W25Q64初始化 */
    SPI_FLASH_Init();

    /* 获取 Flash Device ID */
    DeviceID = SPI_FLASH_ReadDeviceID();
    Delay( 200 );

    /* 获取 SPI Flash ID */
    FlashID = SPI_FLASH_ReadID();
    printf("\r\n FlashID is 0x%X,\
	Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);

    /* 检验 SPI Flash ID */
    if (FlashID == sFLASH_ID)
    {
        printf("\r\n 检测到串行flash W25Q64 !\r\n");

        /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
        // 这里擦除4K,即一个扇区,擦除的最小单位是扇区
        SPI_FLASH_SectorErase(FLASH_SectorToErase);

        /* 将发送缓冲区的数据写到flash中 */
        // 这里写一页,一页的大小为256个字节
        SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
        printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);

        /* 将刚刚写入的数据读出来放到接收缓冲区中 */
        SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
        printf("\r\n 读出的数据为:%s \r\n", Rx_Buffer);

        /* 检查写入的数据与读出的数据是否相等 */
        TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);

        if( PASSED == TransferStatus1 )
        {
            LED_GREEN;
            printf("\r\n 8M串行flash(W25Q64)测试成功!\n\r");
        }
        else
        {
            LED_RED;
            printf("\r\n 8M串行flash(W25Q64)测试失败!\n\r");
        }
    }// if (FlashID == sFLASH_ID)
    else// if (FlashID == sFLASH_ID)
    {
        LED_RED;
        printf("\r\n 获取不到 W25Q64 ID!\n\r");
    }

    while(1);
}

/*
 * 函数名:Buffercmp
 * 描述  :比较两个缓冲区中的数据是否相等
 * 输入  :-pBuffer1     src缓冲区指针
 *         -pBuffer2     dst缓冲区指针
 *         -BufferLength 缓冲区长度
 * 输出  :无
 * 返回  :-PASSED pBuffer1 等于   pBuffer2
 *         -FAILED pBuffer1 不同于 pBuffer2
 */
TestStatus Buffercmp(uint8_t *pBuffer1, uint8_t *pBuffer2, uint16_t BufferLength)
{
    while(BufferLength--)
    {
        if(*pBuffer1 != *pBuffer2)
        {
            return FAILED;
        }

        pBuffer1++;
        pBuffer2++;
    }
    return PASSED;
}

void Delay(__IO uint32_t nCount)
{
    for(; nCount != 0; nCount--);
}
/*********************************************END OF FILE**********************/

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-23 12:39:15  更:2021-10-23 12:39:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:31:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码