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