SPI
本文展示了STM32 SPI Transfer
内容涉及 :
SPI字节数据模拟输出独写 缓存读写
USART串口的识别
IO口输入输出
按键的外部中断处理
32位数据通讯,字符串通讯,单字符通讯
完整代码 :
: GIT 源代码
文章目录
- 前言
- 一、 编程要点
- 二、使用步骤
- 1.理解原理图
- (注意)Keil 配置状态
- 2.建立主程序 main.c
- 3.建立SPI传输的 头文件 SPI_book.h
- 4.建立SPI传输的 头文件 SPI_book.c
- 5.建立I2C模拟传输的 头文件 I2C_soft_book.h
- 6.建立I2C模拟传输的 头文件 I2C_soft_book.c
- 7.建立I2C硬件传输的 头文件 I2C_book.h
- 8.建立I2C硬件传输的 头文件 I2C_book.c
- 9.建立USART传输的 头文件 USART_book.h
- 10.建立USART传输的C文件 USART_book.c
- 11.建立DMA传输的 头文件 DMA_book.h
- 12.建立DMA传输的C文件 DMA_book.c
- 13.建立EXIT的 头文件 Exit_book.h
- 14.建立EXIT的C文件 Exit_book.c
- 15.建立Key传输的 头文件 Key_book.h
- 16.建立Key的C文件 Key_book.c
- 17.建立LED 的头文件 Led_book.h
- 18.建立LED 的 文件 Led_book.c
- 19.建立 Systick传输的 头文件 Systick_book.h
- 20.建立 Systick的C文件 Systick_book.c
- 21.建立 头文件函数 头文件 PROJ_book.h
前言
STM32 的SPI简介 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设 备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间, 要求通讯速率较高的场合。根据 SCK 在空闲状态时的电平,分为两种情况。SCK 信号线在空闲状态为低电平时,CPOL=0;空闲状态为高电平时,CPOL=1。
无论 CPOL=0 还是=1,因为我们配置的时钟相位 CPHA=0,在图中可以看到,采样时 刻都是在 SCK 的奇数边沿。注意当 CPOL=0 的时候,时钟的奇数边沿是上升沿,而 CPOL=1 的时候,时钟的奇数边沿是下降沿。所以 SPI 的采样时刻不是由上升/下降沿决定 的。MOSI 和 MISO 数据线的有效信号在 SCK 的奇数边沿保持不变,数据信号将在 SCK 奇 数边沿时被采样,在非采样时刻,MOSI 和 MISO 的有效信号才发生切换。
SPI通信协议,从物理层上来看这是一种非常简洁明了的通信协议。本身一共就两条总线,一条SCL(时钟总线),一条SDA(数据总线)。通信原理是通过对SCL和SDA线高低电平时序的控制,来 产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平( 原文链接:ttps://blog.csdn.net/qq_42660303/article/details/81154995)
一、 编程要点
SPI:(1) 初始化通讯使用的目标引脚及端口时钟.
(2) 使能 SPI 外设的时钟.
(3) 配置 SPI 外设的模式、地址、速率等参数并使能 SPI 外设.
(4) 编写基本 SPI 按字节收发的函数;
(5) 编写对 FLASH 擦除及读写操作的的函数.
(6) 编写测试程序,对读写数据进行校验.
二、使用步骤
1.理解原理图
通讯过程
(注意)Keil 配置状态
我的博客这里有项目配置 设计;
点击链接
(https://blog.csdn.net/u012651389/article/details/119189949)
2.建立主程序 main.c
代码如下 :
/**
******************************************************************************
* @file GPIO/JTAG_Remap/main.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main program body
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "PROJ_book.h"
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program.
* @param None
* @retval None
*/
void fn_LED_Flash_Init(void);
void fn_usart_show_Init(void);
void fn_DMA_show_Init(void);
void fn_I2C_EE_Init(void);
void fn_I2C_EE_Soft_Init(void);
void fn_SPI_FLASH_Soft_Init(void);
#define countof(a) (sizeof(a) / sizeof(*(a)))
#define _I2C_BufferSize (countof(writeData)-1)
static uint8_t writeData[_I2C_PageSize]={4,5,6,7,8,9,10,11};
static uint8_t writeData2[_I2C_PageSize]={24,25,26,27,28,29,30,31};
static uint8_t ReadData[_I2C_BufferSize]={0};
#define _SPI_BufferSize SPI_PAGE_SIZE //(countof(write_SPI_Data)-1)
static uint8_t write_SPI_Data[_SPI_BufferSize]={0};
static uint8_t Read_SPI_Data[_SPI_BufferSize]={0};
int main(void)
{
fn_RCC_Init(); //CPU 倍频
fn_Led_Init(); //LED 输出初始化
fn_Key_Init(); //按键 输入初始化
fn_USART_Init(); //串口输出初始化
fn_LED_Flash_Init(); //RGB 输出测试
fn_usart_show_Init(); //串口输出测试
fn_EXTI_GPIO_Config(); //外部中断入口
fn_DMA_show_Init(); //初始化DMA数据链路
fn_I2C_EE_Init(); //初始化硬件I2C数据链路
fn_I2C_EE_Soft_Init(); //初始化软件I2C数据链路
fn_SPI_FLASH_Soft_Init(); //SPI测试通讯
while(1){
fn_LED_ALL_OFF();
fn_Systick_Delay(500,_Systick_ms);
__G_OUT__;
fn_Systick_Delay(500,_Systick_ms);
}
}
void fn_LED_Flash_Init(void){
uint16_t count_Init = 2;
printf("\n ---> LED开始运行 \n");
while(count_Init-->0){
fn_LED_ALL_OFF();
__R_OUT__;
fn_Systick_Delay(500,_Systick_ms);
fn_LED_ALL_OFF();
__G_OUT__;
fn_Systick_Delay(500,_Systick_ms);
fn_LED_ALL_OFF();
__B_OUT__;
fn_Systick_Delay(500,_Systick_ms);
fn_LED_ALL_OFF();
__R_OUT__;
}
}
void fn_usart_show_Init(void){
fn_Usart_Send_Byte(_DEBUG_USARTx,'\r');
printf("-->串口通信指测试完毕 \n");
fn_Usart_SendString(_DEBUG_USARTx," : wangqi \n");
}
void fn_DMA_show_Init(void){
printf("\n ---> DMA开始运行 \n");
_DMA_ROM_TO_RAM(Map_BUFFER_SIZE ,aSRC_Cont_Buffer , aDST_Buffer);
_DMA_RAM_TO_USART(Map_BUFFER_SIZE ,USART_Source_ADDR , aDST_Buffer);
printf("---> DMA运行完毕 \n");
}
void fn_I2C_EE_Init(void){
printf("\n-->I2C_函数写入开始 \n");
_I2C_EE_Init();
I2C_Write_fun(writeData ,EEP_Firstpage ,_I2C_BufferSize);
I2C_Read_fun(ReadData ,EEP_Firstpage ,_I2C_BufferSize);
printf("--->I2C_函数写入完毕\n\r");
}
void fn_I2C_EE_Soft_Init(void){
printf("\n-->I2C_软件函数写入开始 \n");
I2C_Soft_Init();
I2C_Soft_Write_fun(writeData2 ,EEP_Firstpage ,_I2C_BufferSize);
I2C_Soft_Read_fun(ReadData ,EEP_Firstpage ,_I2C_BufferSize);
printf("\n--->I2C_软件函数写入完毕\n\r");
}
void fn_SPI_FLASH_Soft_Init(void){
uint16_t i,FlashID;
printf("-->SPI通信指测试开始 \n");
SPI_FLASH_Init();
FlashID = SPI_Read_ID() ;
if(FlashID == _SPI_FLASH_ID){
printf("-->SPI 0x%x \n",FlashID);
}
SPI_Erase_Sector(0); //清除一个页的空间
printf("\n\n-->SPI清空开始 \n");
SPI_Read_Data(Read_SPI_Data , 0, SPI_PAGE_SIZE);
SPI_Show_Data(Read_SPI_Data , SPI_PAGE_SIZE);
printf("\n\n-->SPI清空完成 \n");
for(i=0 ; i < _SPI_BufferSize ; i++){
write_SPI_Data[i] = 0xA7;
}
SPI_Show_Data(write_SPI_Data , SPI_PAGE_SIZE);
SPI_BufferWrite_Data(write_SPI_Data ,0x000000,_SPI_BufferSize);
printf("\n\n-->SPI输入完成 \n");
SPI_Read_Data(Read_SPI_Data , 0x000000, _SPI_BufferSize);
SPI_Show_Data(Read_SPI_Data , _SPI_BufferSize);
printf("-->SPI通信指测试完毕 \n");
}
void delay(int x){
int y = 0xFFFFF;
while((x--)>0){
while((y--)>0){
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
}
}
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
3.建立SPI传输的 头文件 SPI_book.h
代码如下 :
#ifndef __SPI_BOOK_H_
#define __SPI_BOOK_H_
#include "stm32f10x.h"
//#define _SPI_FLASH_ID 0xEF3015 //W25X16
//#define _SPI_FLASH_ID 0xEF4015 //W25Q16
//#define _SPI_FLASH_ID 0XEF4018 //W25Q128
#define _SPI_FLASH_ID 0XEF4017 //W25Q64
//---------------- 这里封装了 I2C 通讯配置信息 -------------------
#define _FLASH_SPIx SPI1
#define _FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define _FLASH_SPI_CLK RCC_APB2Periph_SPI1
#define _FLASH_SPI_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define _FLASH_SPI_GPIO_CLK RCC_APB2Periph_GPIOA
#define _FLASH_SPI_SCL_PORT GPIOA
#define _FLASH_SPI_SCL_PIN GPIO_Pin_5
#define _FLASH_SPI_MISO_PORT GPIOA
#define _FLASH_SPI_MISO_PIN GPIO_Pin_6
#define _FLASH_SPI_MOSI_PORT GPIOA
#define _FLASH_SPI_MOSI_PIN GPIO_Pin_7
#define _FLASH_SPI_CSS_PORT GPIOA
#define _FLASH_SPI_CSS_PIN GPIO_Pin_4
//FLASH_SPI 引脚配置
#define _FLASH_CSS_HIGH() _FLASH_SPI_CSS_PORT->BSRR = _FLASH_SPI_CSS_PIN
#define _FLASH_CSS_LOW() _FLASH_SPI_CSS_PORT->BRR = _FLASH_SPI_CSS_PIN
/*通讯等待超时时间*/
#define FLASH_SPI_TIMEOUT ((uint32_t)0x6000)
#define FLASH_SPI_LONG_TIMEOUT ((uint32_t)(10*FLASH_SPI_TIMEOUT))
//信息输出
#define FLASH_ERROR(fmt,arg...) printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
//
#define SPI_PAGE_SIZE 4096
#define SPI_PAGE_Write_SIZE 256
//FLASH 指令
#define FLASH_SPI_DUMMY 0x00
#define FLASH_SPI_READ_JEDEC_ID 0x9f
#define FLASH_SPI_REASE_SECTOR 0x20
#define FLASH_SPI_READ_STATUS 0x05
#define FLASH_SPI_READ_DATA 0x03
#define FLASH_SPI_WRITE_ENABLE 0x06
#define FLASH_SPI_WRITE_DATA 0x02
#define FLASH_SPI_ChipErase 0xC7
void SPI_FLASH_Init(void);
uint32_t SPI_Read_ID(void);
uint32_t SPI_Read_DeviceID(void);
void SPI_Erase_Sector(uint32_t addr);
void SPI_FLASH_BulkErase(void);
void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint16_t numByteToRead );
void SPI_BufferRead_Data(uint8_t *writeBuffer , uint32_t WriteAddr ,uint16_t numByteToWrite );
void SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint16_t numByteToRead );
void SPI_BufferWrite_Data(uint8_t *writeBuffer , uint32_t WriteAddr ,uint16_t numByteToWrite );
void SPI_Show_Data(uint8_t *readBuffer , uint16_t numByteToRead);
#endif
4.建立SPI传输的 头文件 SPI_book.c
代码如下 :
#include "SPI_book.h"
#include "Systick_book.h"
static __IO uint32_t SPITimeout = FLASH_SPI_LONG_TIMEOUT;
/**
* @brief SPII/O配置
* @param 无
* @retval 无
*/
static void SPI_GPIO_Config(void){
GPIO_InitTypeDef GPIO_InitStructure;
//使能与SPI 有关的时钟
_FLASH_SPI_APBxClock_FUN(_FLASH_SPI_CLK , ENABLE);
_FLASH_SPI_GPIO_APBxClock_FUN(_FLASH_SPI_GPIO_CLK , ENABLE);
//MISO MOSI SCK
GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_MOSI_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
//初始化CSS引脚,使能软件控制,所以直接设置为推挽输出
GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_CSS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
_FLASH_CSS_HIGH();
}
/**
* @brief static void SPI_Mode_Config(void) 配置
* @param 无
* @retval 无
*/
static void SPI_Mode_Config(void){
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ; //波特率预分频值为 2
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ; //数据捕获于第二个时钟沿
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟悬空高
SPI_InitStructure.SPI_CRCPolynomial = 0; //不使用CRC功能,数值随便写
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI 发送接收 8 位帧结构
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; //双线全双工
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; //设置为主 SPI
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //内部 NSS 信号有 SSI 位控制
SPI_Init(_FLASH_SPIx , &SPI_InitStructure ); //写入配置到寄存器
SPI_Cmd(_FLASH_SPIx , ENABLE); //使能SPI
}
/**
* @brief void SPI_FLASH_Init(void) 初始化
* @param 无
* @retval 无
*/
void SPI_FLASH_Init(void){
SPI_GPIO_Config();
SPI_Mode_Config();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 通讯建立操作
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* @brief Basic management of the timeout situation.
* @param errorCode:错误代码,可以用来定位是哪个环节出错.
* @retval 返回0,表示SPI读取失败.
*/
static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* Block communication and all processes */
FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
return 0;
}
/**
* @brief uint8_t SPI_FLASH_Send_Byte(uint8_t data) 初始化
* @param 发送并且接收一个字节
* @retval 无
*/
static uint8_t SPI_FLASH_Send_Byte(uint8_t data){
SPITimeout = FLASH_SPI_TIMEOUT;
//检查并等待至TX缓冲区
while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){//发送缓存空标志位
if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(0);}
}
//判断程序已经为空
SPI_I2S_SendData(_FLASH_SPIx , data);
//判断接受缓存非空
SPITimeout = FLASH_SPI_TIMEOUT;
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 uint8_t SPI_FLASH_Send_Byte(uint8_t data) 初始化
* @param 发送并且接收一个字节
* @retval 无
*/
static uint8_t SPI_FLASH_SendHalf_Byte(uint16_t Halfdata){
SPITimeout = FLASH_SPI_TIMEOUT;
//检查并等待至TX缓冲区
while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){//发送缓存空标志位
if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(2);}
}
//判断程序已经为空
SPI_I2S_SendData(_FLASH_SPIx , Halfdata);
//判断接受缓存非空
SPITimeout = FLASH_SPI_TIMEOUT;
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 uint32_t SPI_Read_ID(void)
* @param 读取ID号
* @retval
*/
uint32_t SPI_Read_ID(void){
uint32_t flash_id;
//片选使能
_FLASH_CSS_LOW();
SPI_FLASH_Send_Byte(FLASH_SPI_READ_JEDEC_ID);
flash_id = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Memory typeID
flash_id<<=8;
flash_id|=SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
flash_id<<=8;
flash_id|=SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
_FLASH_CSS_HIGH();
return flash_id;
}
/**
* @brief uint32_t SPI_Read_ID(void)
* @param 读取ID号
* @retval
*/
//uint32_t SPI_Read_DeviceID(void){
// uint32_t flash_id;
// //片选使能
// _FLASH_CSS_LOW();
// SPI_FLASH_Send_Byte(FLASH_SPI_READ_JEDEC_ID);
// SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Memory typeID
// SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
// SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
// flash_id = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
// _FLASH_CSS_HIGH();
// return flash_id;
//}
/**
* @brief void SPI_Write_Enable(void)
* @param 写入使能
* @retval
*/
static void SPI_Write_Enable(void){
//片选使能
_FLASH_CSS_LOW();
SPI_FLASH_Send_Byte(FLASH_SPI_WRITE_ENABLE);
_FLASH_CSS_HIGH();
}
/**
* @brief static void SPI_WaitForWriteEnd(void);
* @param //等待FLASH内部时序操作完成
* @retval
*/
static SPI_WaitForWriteEnd(void){
uint8_t status_reg = 0; //判断最低位S0 erse or write in progress
// 片选指令
_FLASH_CSS_LOW();
SPI_FLASH_Send_Byte(FLASH_SPI_READ_STATUS);
do{
status_reg = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY); //想要读取数据需要继续发送
}while((status_reg & 0x01)==1); //校验最低位进行校验
_FLASH_CSS_HIGH();
}
/**
* @brief svoid SPI_Erase_Sector(uint32_t addr)
* @param 擦除FLASH指定扇区
* @retval
*/
void SPI_Erase_Sector(uint32_t addr){
SPI_Write_Enable();
/* 擦除扇区 */
/* 选择FLASH: CS低电平 */
_FLASH_CSS_LOW();
/* 发送扇区擦除指令*/
SPI_FLASH_Send_Byte(FLASH_SPI_REASE_SECTOR);
/*发送擦除扇区地址的高位*/
SPI_FLASH_Send_Byte((addr & 0xFF0000) >> 16);
/* 发送擦除扇区地址的中位 */
SPI_FLASH_Send_Byte((addr & 0xFF00) >> 8);
/* 发送擦除扇区地址的低位 */
SPI_FLASH_Send_Byte(addr & 0xFF);
/* 停止信号 FLASH: CS 高电平 */
_FLASH_CSS_HIGH();
/* 等待擦除完毕*/
SPI_WaitForWriteEnd();
}
/**
* @brief 擦除FLASH扇区,整片擦除
* @param 无
* @retval 无
*/
void SPI_FLASH_BulkErase(void){
//发送FLASH 写使能命令
SPI_Write_Enable();
//整块Erase
//选择FLASH :CS 低电平
_FLASH_CSS_LOW();
SPI_FLASH_Send_Byte(FLASH_SPI_ChipErase);
/* 停止信号 FLASH: CS 高电平 */
_FLASH_CSS_HIGH();
/* 等待擦除完毕*/
SPI_WaitForWriteEnd();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 读写操作
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* @brief void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint32_t numByteToRead ));
* @param 读取FLASH的内容
* @retval
*/
void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint16_t numByteToRead ){
//片选使能
_FLASH_CSS_LOW();
//发送地址
/* 发送 读 指令 */
SPI_FLASH_Send_Byte(FLASH_SPI_READ_DATA);
/* 发送 读 地址高位 */
SPI_FLASH_Send_Byte((addr>>16)&0xff);
/* 发送 读 地址中位 */
SPI_FLASH_Send_Byte((addr>>8)&0xff);
/* 发送 读 地址低位 */
SPI_FLASH_Send_Byte(addr&0xff);
// if(numByteToRead > SPI_PAGE_SIZE){
// numByteToRead = SPI_PAGE_SIZE;
// printf("SPI_FLASH_PageWrite too large!\n");
// }
/* 读取数据 */
while(numByteToRead--){
/* 读取一个字节*/
*readBuffer = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);
/* 指向下一个字节缓冲区 */
readBuffer++;
}
/* 停止信号 FLASH: CS 高电平 */
_FLASH_CSS_HIGH();
}
/**
* @brief void SPI_Write_Data(uint8_t *readBuffer , uint32_t addr ,uint32_t numByteToRead ));
* @param 读取FLASH的内容
* @retval
*/
void SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint16_t numByteToRead ){
SPI_Write_Enable();
//片选使能
_FLASH_CSS_LOW();
/* 写页写指令*/
SPI_FLASH_Send_Byte(FLASH_SPI_WRITE_DATA);
/*发送写地址的高位*/
SPI_FLASH_Send_Byte((addr&0xff0000)>>16);
/*发送写地址的中位*/
SPI_FLASH_Send_Byte((addr&0xff00)>>8);
/*发送写地址的低位*/
SPI_FLASH_Send_Byte(addr&0xff);
if(numByteToRead > SPI_PAGE_SIZE){
numByteToRead = SPI_PAGE_SIZE;
printf("SPI_FLASH_PageWrite too large!\n");
}
//写入数据
while(numByteToRead--){
//发送当前要写入的字节数据
SPI_FLASH_Send_Byte(*writeBuffer);
//指向先亿字节数据
writeBuffer++;
}
/* 停止信号 FLASH: CS 高电平 */
_FLASH_CSS_HIGH();
/* 等待写入完毕*/
SPI_WaitForWriteEnd();
}
/**
* @brief SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint32_t numByteToRead ){
* @param 读取FLASH的内容
* @retval
*/
void SPI_BufferWrite_Data(uint8_t *writeBuffer , uint32_t WriteAddr ,uint16_t numByteToWrite ){
uint32_t NumOfPage , NumOfSingle , BufferAddr ,count , temp;
if(numByteToWrite == 0){printf("SPI_FLASH_PageWrite too small!\n"); return;}
BufferAddr = WriteAddr % SPI_PAGE_Write_SIZE;
/*地址对应页的前方对齐数量*/
count = SPI_PAGE_Write_SIZE - BufferAddr;
/*当前页剩下的全部地址数量*/
if(count >= numByteToWrite){
//剩下的内容可以一行写完
SPI_Write_Data(writeBuffer ,WriteAddr ,numByteToWrite );
return;
}
SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)count );//分割写入单独页面
temp = numByteToWrite - count ; //排除多余部分
WriteAddr += count;
writeBuffer+=count;
NumOfPage = temp / SPI_PAGE_Write_SIZE ; //对大面积分割输入
NumOfSingle = temp % SPI_PAGE_Write_SIZE ;
if(NumOfPage == 0){
SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)NumOfSingle );
return;
}else{
while(NumOfPage--){
SPI_Write_Data(writeBuffer ,WriteAddr ,SPI_PAGE_Write_SIZE );
WriteAddr += SPI_PAGE_Write_SIZE;
writeBuffer += SPI_PAGE_Write_SIZE;
}
SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)NumOfSingle );
return;
}
}
/**
* @brief void SPI_Show_Data(uint8_t *readBuffer);
* @param 读取FLASH的内容
* @retval
*/
void SPI_Show_Data(uint8_t *readBuffer , uint16_t numByteToRead){
uint32_t i;
for(i=0 ;i<numByteToRead ;i++ ){
if(i%SPI_PAGE_Write_SIZE == 0){ //每隔256字节换行
printf("\r\n ");
}
printf("0x%x ",readBuffer[i]);
}
}
与所有使用到 GPIO 的外设一样,都要先把使用到的 GPIO 引脚模式初始化,配置好复 用功能。GPIO 初始化流程如下:(1) 使用 GPIO_InitTypeDef定义 GPIO初始化结构体变量,以便下面用于存储 GPIO 配置;
(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能 SPI 引脚使用的 GPIO 端口时钟。
(3) 向 GPIO 初始化结构体赋值,把 SCK/MOSI/MISO 引脚初始化成复用推挽模式。而 CS(NSS)引脚由于使用软件控制,我们把它配置为普通的推挽输出模式。
(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的 初始化
SPI_FLASH_SendByte 函数实现了前面讲解的“SPI 通讯过程”: (1) 本函数中不包含 SPI 起始和停止信号,只是收发的主要过程,所以在调用本函数 前后要做好起始和停止信号的操作;
(2) 对 SPITimeout 变量赋值为宏 SPIT_FLAG_TIMEOUT。这个 SPITimeout 变量在下 面的 while 循环中每次循环减 1,该循环通过调用库函数 SPI_I2S_GetFlagStatus 检 测事件,若检测到事件,则进入通讯的下一阶段,若未检测到事件则停留在此处 一直检测,当检测 SPIT_FLAG_TIMEOUT 次都还没等待到事件则认为通讯失败, 调用的 SPI_TIMEOUT_UserCallback 输出调试信息,并退出通讯;
(3) 通过检测 TXE 标志,获取发送缓冲区的状态,若发送缓冲区为空,则表示可能存 在的上一个数据已经发送完毕;
(4) 等待至发送缓冲区为空后,调用库函数 SPI_I2S_SendData 把要发送的数据“byte” 写入到 SPI 的数据寄存器 DR,写入 SPI 数据寄存器的数据会存储到发送缓冲区, 由 SPI 外设发送出去;
(5) 写入完毕后等待 RXNE 事件,即接收缓冲区非空事件。由于 SPI 双线全双工模式 下 MOSI 与 MISO 数据传输是同步的(请对比“SPI 通讯过程”阅读),当接收缓冲 区非空时,表示上面的数据发送完毕,且接收缓冲区也收到新的数据;
(6) 等待至接收缓冲区非空时,通过调用库函数 SPI_I2S_ReceiveData 读取 SPI 的数据 寄存器 DR,就可以获取接收缓冲区中的新数据了。代码中使用关键字“return” 把接收到的这个数据作为 SPI_FLASH_SendByte 函数的返回值,所以我们可以看 到在下面定义的 SPI 接收数据函数 SPI_FLASH_ReadByte,它只是简单地调用了 SPI_FLASH_SendByte 函数发送数据“Dummy_Byte”,然后获取其返回值(因为 不关注发送的数据,所以此时的输入参数“Dummy_Byte”可以为任意值)。可以 这样做的原因是 SPI 的接收过程和发送过程实质是一样的,收发同步进行,关键 在于我们的上层应用中,关注的是发送还是接收的数据。
5.建立I2C模拟传输的 头文件 I2C_soft_book.h
代码如下 :
#ifndef __I2C_SOFT_BOOK_H_
#define __I2C_SOFT_BOOK_H_
#include "stm32f10x.h"
//---------------- 这里封装了 I2C 通讯配置信息 -------------------
#define _Soft_I2C_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define _Soft_I2C_GPIO_CLK RCC_APB2Periph_GPIOB
#define _Soft_I2C_SCL_PORT GPIOB
#define _Soft_I2C_SCL_PIN GPIO_Pin_6
#define _Soft_I2C_SDA_PORT GPIOB
#define _Soft_I2C_SDA_PIN GPIO_Pin_7
#define _I2C_SCL_1() _Soft_I2C_SCL_PORT->BSRR = _Soft_I2C_SCL_PIN
#define _I2C_SCL_0() _Soft_I2C_SCL_PORT->BRR = _Soft_I2C_SCL_PIN
#define _I2C_SDA_1() _Soft_I2C_SCL_PORT->BSRR = _Soft_I2C_SDA_PIN
#define _I2C_SDA_0() _Soft_I2C_SCL_PORT->BRR = _Soft_I2C_SDA_PIN
#define _I2C_SDA_READ() ((_Soft_I2C_SCL_PORT->IDR & _Soft_I2C_SDA_PIN)!=0)
#define I2C_WR 0 /* 写控制bit */
#define I2C_RD 1 /* 读控制bit */
//----------------器件地址--------------------
/*
* 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 Soft_EEPROM_ADDRESS 0xA0 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA6 /* E2 = 0 */
/*读取数据的格式以及字符数量*/
#define _I2C_Soft_PageSize 8
#define _I2C_Soft_SIZE 256 /* 24xx02总容量 */
/*I2C 存储地址*/
#define EEP_Soft_Firstpage 0x90
void I2C_Soft_Init(void);
void EE_Soft_Trase(void);
uint8_t I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);
uint8_t I2C_Soft_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);
#endif
6.建立I2C模拟传输的 头文件 I2C_soft_book.c
代码如下 :
#include "I2C_soft_book.h"
#include "Systick_book.h"
static I2C_GPIO_Soft_Config(void){
GPIO_InitTypeDef GPIO_InitStructure;
_Soft_I2C_GPIO_APBxClock_FUN(_Soft_I2C_GPIO_CLK , ENABLE);
GPIO_InitStructure.GPIO_Pin = _Soft_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
GPIO_Init(_Soft_I2C_SCL_PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = _Soft_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
GPIO_Init(_Soft_I2C_SDA_PORT,&GPIO_InitStructure);
}
/**
* @brief I2C_EE_Init 程序初始化
* @param 无
* @retval 无
*/
static void I2C_Start(void){
// 当SCL高电平时候SDA 出现一个下降沿编号位I2C 总线启动信号
_I2C_SCL_1();
_I2C_SDA_1();
fn_Systick_Delay(50,_Systick_us);
_I2C_SDA_0();
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_0();
fn_Systick_Delay(50,_Systick_us);
}
/**
* @brief I2C_Stop 程序初始化
* @param 无
* @retval 无
*/
static void I2C_Stop(void){
// 当SCL高电平,SDA出现上升沿表示I2C总线停止信号
_I2C_SDA_0();
_I2C_SCL_1();
fn_Systick_Delay(50,_Systick_us);
_I2C_SDA_1();
}
/**
* @brief I2C_SendByte 程序初始化
* @param 无
* @retval 无
*/
static void I2C_SendByte(uint8_t _ucByte){
uint8_t i;
//发送字节的高位
for( i=0; i<8;i++ ){
if(_ucByte & 0x80){
_I2C_SDA_1();
}else{
_I2C_SDA_0();
}
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_1();
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_0();
_ucByte <<=1; //------注意这里不太一样
fn_Systick_Delay(50,_Systick_us);
}
_I2C_SDA_1();// 释放总线
}
/**
* @brief I2C_ReadByte 程序初始化
* @param 无
* @retval 无
*/
static uint8_t I2C_ReadByte(void){
uint8_t i;
uint8_t value;
//读到第1个bit 为数据的bit7
value = 0;
for(i=0 ;i<8 ;i++ ){
value <<=1;
_I2C_SCL_1();
fn_Systick_Delay(50,_Systick_us);
if(_I2C_SDA_READ()){
value++;
}
_I2C_SCL_0();
fn_Systick_Delay(50,_Systick_us);
}
return value;
}
/**
* @brief I2C_WaitAck
* @param 无
* @retval 无
*/
static uint8_t I2C_WaitAck(void){
uint8_t re;
_I2C_SDA_1();
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_1();
fn_Systick_Delay(50,_Systick_us);
if(_I2C_SDA_READ()){
re = 1;
}else{
re = 0;
}
_I2C_SCL_0();
fn_Systick_Delay(50,_Systick_us);
return re;
}
/**
* @brief I2C_ACK
* @param 无
* @retval 无
*/
static void I2C_ACK(void){
_I2C_SDA_0(); //CPU 驱动SDA = 0;
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_1(); //CPU 产生1个时钟
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_0();
fn_Systick_Delay(50,_Systick_us);
_I2C_SDA_1(); //CPU 释放SDA总线
}
/**
* @brief I2C_ACK
* @param 无
* @retval 无
*/
static void I2C_NACK(void){
_I2C_SDA_1(); //CPU 驱动SDA = 1;
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_1(); //CPU 产生1个时钟
fn_Systick_Delay(50,_Systick_us);
_I2C_SCL_0();
fn_Systick_Delay(50,_Systick_us);
}
static uint8_t I2C_CheckDevice(uint8_t _Address){
uint8_t ucAck;
I2C_GPIO_Soft_Config();
I2C_Start();
I2C_SendByte(_Address | I2C_WR);
ucAck = I2C_WaitAck(); /* 检测设备的ACK应答 */
I2C_Stop(); /* 发送停止信号 */
return ucAck;
}
//----------------------I2C 独写操作--------------------
//------------------------------------------------------
/**
* @brief EE_Soft_Check_State
* @param 判断串行EERPOM是否正常
* @retval 无
*/
static uint8_t EE_Soft_Check_State(void){
if(I2C_CheckDevice(Soft_EEPROM_ADDRESS)==0){return 1;}
else{I2C_Stop(); /* 发送停止信号 */ return 0;}
}
/**
* @brief uint8_t I2C_Soft_BufferRead(uint8_t* pBuffer,
* uint8_t ReadAddr, uint16_t NumByteToRead)
* @param 判断串行EERPOM是否正常
* @retval 无
*/
static uint8_t I2C_Soft_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
uint16_t i ;
//连续取得若干个字节
// 发起I2C总线启动信号
I2C_Start();
//发送控制字节地址和读取数据信号
I2C_SendByte(Soft_EEPROM_ADDRESS | I2C_WR);
//等待应答状态
if(I2C_WaitAck()!=0){printf("EEPROM 错误 1 !\r\n"); goto CMD_Fail;}
//发送数据读取位置信息信号
I2C_SendByte((uint8_t)ReadAddr);
//等待应答状态
if(I2C_WaitAck()!=0){printf("EEPROM 错误 2 !\r\n");goto CMD_Fail;}
//--------------
//重新启动I2C总线
I2C_Start();
//发送器件地址
I2C_SendByte(Soft_EEPROM_ADDRESS| I2C_RD);
//等待应答状态
if(I2C_WaitAck()!=0){printf("EEPROM 错误3 !\r\n"); goto CMD_Fail;}
for(i=0 ;i<NumByteToRead ;i++ ){
pBuffer[i] = I2C_ReadByte();
if(i!=NumByteToRead-1){
I2C_ACK();
}else{
I2C_NACK();
}
}
I2C_Stop();
return 1;
CMD_Fail:
I2C_Stop();
return 0;
}
/**
* @brief uint8_t EE_Soft_WriteBytes(uint8_t* pBuffer,
* uint8_t ReadAddr, uint16_t NumByteToRead)
* @param 判断串行EERPOM是否正常
* @retval 无
*/
static uint8_t EE_Soft_WriteBytes(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
uint16_t i , m;
uint16_t usAddr;
/*
写串行EEPROM 不像读操作可以连续读取很多字节每次写操作只能在同一个page
对于24C page size = 8
简单的处理方法为 按字节写操作模式,写一个字节都发送地址
为了提高连续写的效率 笨函数采用Page write操作
*/
usAddr = ReadAddr;
for(i=0 ;i<NumByteToRead;i++ ){
// 当发送第一个字节或者页面首地址时,需要重新发起启动信号和地址
if((i==0)||(usAddr)&(_I2C_Soft_PageSize-1)==0){
// 发送停止信号
I2C_Stop();
//通过检测器判断内存写入是否成功
m = 100;
for (m = 0; m < 100; m++){
//启动I2C总线
I2C_Start();
//发送器件地址
I2C_SendByte(Soft_EEPROM_ADDRESS| I2C_WR);
//等待应答状态
if(I2C_WaitAck()==0){break;}
}
if(m==100){printf("EEPROM 错误 4 !\r\n"); goto CMD_FAIL_bytes ; }
I2C_SendByte((uint8_t)usAddr);
if(I2C_WaitAck()!=0){printf("EEPROM 错误 5 !\r\n"); goto CMD_FAIL_bytes;}
}
// 开始写入数据
I2C_SendByte(pBuffer[i]);
//等待应答状态
if(I2C_WaitAck()!=0){printf("EEPROM 错误 7 !\r\n"); goto CMD_FAIL_bytes;}
usAddr++;
}
// 发送停止信号
I2C_Stop();
return 1;
CMD_FAIL_bytes:
// 发送停止信号
I2C_Stop();
return 0;
}
/**
* @brief void EE_Soft_Trase(void)
* @param 判断串行EERPOM是否正常
* @retval 无
*/
void EE_Soft_Trase(void){
uint16_t i ;
uint8_t buf[_I2C_Soft_SIZE]={0};
// 填充缓冲区
for(i=0 ;i<_I2C_Soft_SIZE ;i++ ){
buf[i] = 0xFF;
}
//写EEPROM 起始地址= 0 数据长度为256
if(EE_Soft_WriteBytes(buf,0,_I2C_Soft_SIZE)==0){
printf("擦除EEPROM出错!\r\n");
return;
}else{
printf("擦除EEPROM出错!\r\n");
}
}
/**
* @brief void I2C_Soft_Init(void)
* @param
* @retval 无
*/
void I2C_Soft_Init(void){
if(EE_Soft_Check_State()==0){
/* 没有检测到EEPROM */
printf("没有检测到串行EEPROM!\r\n");
}
}
/**
* @brief void I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
* @param 判断串行EERPOM是否正常
* @retval 无
*/
uint8_t I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
uint16_t i;
//-------------------------------
if(EE_Soft_Check_State()==0){
/* 没有检测到EEPROM */
printf("没有检测到串行EEPROM!\r\n");
return 1;
}
//------------写入I2C-------------------
if(EE_Soft_WriteBytes(pBuffer,WriteAddr ,NumByteToWrite)==0){
/* 没有检测到EEPROM */
printf("写EEPROM错误!\r\n");
return 1;
}else{
/* 没有检测到EEPROM */
printf("写EEPROM成功!\r\n");
}
fn_Systick_Delay(150,_Systick_us);
//--------------数据检查--------------
printf("EEPROM写入数据检查检查\r\n");
for(i=0 ;i<NumByteToWrite ;i++ ){
printf(" %d ",pBuffer[i]);
if((i & 15)==15){
printf("\r\n");
}
}
return 0;
}
uint8_t I2C_Soft_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
uint16_t i;
//-------------读取I2C------------------
if(I2C_Soft_BufferRead(pBuffer,WriteAddr,NumByteToWrite)==0){
/* 没有检测到EEPROM */
printf("读EEPROM错误!\r\n");
return 1;
}else{
/* 没有检测到EEPROM */
printf("\n读EEPROM成功!\r\n");
}
//--------------数据检查--------------
printf("EEPROM读取数据数据检查 \r\n");
for(i=0 ;i<NumByteToWrite ;i++ ){
printf(" %d ",pBuffer[i]);
if((i & 15)==15){
printf("\r\n");
}
}
return 1;
}
/*********************END OF FILE**********************/
7.建立I2C硬件传输的 头文件 I2C_book.h
代码如下 :
#ifndef __I2C_BOOK_H_
#define __I2C_BOOK_H_
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "USART_book.h"
//---------------- 这里封装了 I2C 通讯配置信息 -------------------
#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
/* I2C 器件地址 */
#define _I2Cx_OWN_ADDRESS7 0x5f
/*读取数据的格式以及字符数量*/
#define _I2C_PageSize 8
/*I2C 存储地址*/
#define EEP_Firstpage 0x90
#define EEP_SIZE 0xFF
//----------------器件地址--------------------
/*
* 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_ADDRESS 0xA0 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA6 /* E2 = 0 */
//----------------函数声明--------------------
//I2C 应用函数
void _I2C_EE_Init(void);
void I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);
void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);
#endif
8.建立I2C硬件传输的 头文件 I2C_book.c
代码如下 :
#include "I2C_book.h"
#include "Systick_book.h"
/**
* @brief I2C_EE_Init 程序初始化
* @param 无
* @retval 无
*/
static void I2C_GPIO_Config(void){
GPIO_InitTypeDef GPIO_InitStructure;
// 初始化 I2C 相关时钟
_EEPROM_I2C_APBxClock_FUN(_EEPROM_I2C_CLK,ENABLE);
_EEPROM_I2C_GPIO_APBxClock_FUN(_EEPROM_I2C_GPIO_CLK,ENABLE);
// 初始化I2C_SCL SDA
GPIO_InitStructure.GPIO_Pin = _EEPROM_I2C_SCL_PIN;
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;
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_EE_Init 程序初始化
* @param 无
* @retval 无
*/
static void I2C_Mode_Config(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);
}
/**************************************/
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode){
fn_Usart_SendString(_DEBUG_USARTx,"I2C 等待超时!errorCode =");
printf("%d\n",errorCode);
return 0;
}
/**************************************/
/*通讯等待超时时间*/
#define I2CT_FLAG_TIMEOUT ((uint32_t)0x6000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(10*I2CT_FLAG_TIMEOUT))
static uint16_t I2CTimeout;
/**************************************/
/**
* @brief 写一个字节到 I2C EEPROM 中
* @param pBuffer:缓冲区指针
* @param WriteAddr:写地址
* @retval 正常返回 1,异常返回 0
*/
static uint32_t I2C_EE_ByteWrite(u8* pBuffer, uint8_t WriteAddr ){
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_GetFlagStatus(_EEPROM_I2Cx , ENABLE)){
if((I2CTimeout--) == 0){return I2C_TIMEOUT_UserCallback(4);}
}
//产生I2C起始信号
I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
//检测EV5 事件并清除标识位
while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(5);}
}
//发送EEPROM 设备地址
I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
//检测EV6 事件并清除标识位
while(I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(6);}
}
//发送要写入的EEPROM 内部地址(即EEPROM内部存储其地址);
I2C_SendData(_EEPROM_I2Cx,WriteAddr);
I2CTimeout = I2CT_LONG_TIMEOUT;
//检测EV8 事件清除标志位
while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(2);}
}
//发送要写入的EEPROM 内部的数据;
I2C_SendData(_EEPROM_I2Cx,*pBuffer);
I2CTimeout = I2CT_LONG_TIMEOUT;
//检测EV8 事件清除标志位
while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(3);}
}
//发送要写入的EEPROM 内部的数据;
I2C_SendData(_EEPROM_I2Cx,ENABLE);
return 1;
}
/**
* @brief 将缓冲区中的数据写到 I2C EEPROM 中,采用单字节写入的方式,速度比页写入慢
* @param pBuffer:缓冲区指针
* @param WriteAddr:写地址
* @param NumByteToWrite:写的字节数
*/
static void I2C_EE_WaitEepromStandbyState(void){
vu16 SR1_Tmp = 0;
do{
//产生I2C起始信号
I2C_GenerateSTART(_EEPROM_I2Cx,ENABLE);
//读取I2C1 SR1 寄存器
SR1_Tmp = I2C_ReadRegister(_EEPROM_I2Cx, I2C_Register_SR1);
//发送EEPROM 地址+ 方向
I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(_EEPROM_I2Cx, I2C_Register_SR1) & 0x0002));
/* 清除 AF 位 */
I2C_ClearFlag(_EEPROM_I2Cx, I2C_FLAG_AF);
//发送停止位信号
I2C_GenerateSTOP(_EEPROM_I2Cx , ENABLE);
}
//zuozuo04-30
/**
* @brief 在 EEPROM 的一个写循环中可以写多个字节,但一次写入的字节数
* 不能超过 EEPROM 页的大小,AT24C02 每页有 8 个字节
* @param
* @param pBuffer:缓冲区指针
* @param WriteAddr:写地址
* @param NumByteToWrite:要写的字节数要求 NumByToWrite 小于页大小
* @retval 正常返回 1,异常返回 0
*/
static uint8_t I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_GetFlagStatus(_EEPROM_I2Cx , ENABLE)){
if((I2CTimeout--) == 0){return I2C_TIMEOUT_UserCallback(4);}
}
//产生I2C起始信号
I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
//检测EV5 事件并清除标识位
while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(5);}
}
//发送EEPROM 设备地址
I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
//检测EV6 事件并清除标识位
while(I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(6);}
}
//发送要写入的EEPROM内部地址(EEPROM内部存储器地址)
I2C_SendData(_EEPROM_I2Cx,WriteAddr);
I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
//检测EV7 事件并清除标识位
while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(7);}
}
//循环发送 NumByteToWrite个数据
while(NumByteToWrite--){
//发送缓冲区的数据
I2C_SendData(_EEPROM_I2Cx,*pBuffer++);
I2CTimeout = I2CT_FLAG_TIMEOUT; //这个变量是延时异常时间
//检测EV8 事件并清除标识位
while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(8);}
}
}
//发送停止信号
I2C_GenerateSTOP(_EEPROM_I2Cx,ENABLE);
return 1;
}
/** 快速写入一页
* @brief 将缓冲区中的数据写到 I2C EEPROM 中
* @param
* @arg pBuffer:缓冲区指针
* @arg WriteAddr:写地址
* @arg NumByteToWrite:写的字节数
* @retval 无
*/
#define I2C_PageSize 8 //AT24C01 02 每页有8个字节
static void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
u8 NumOfPage = 0, NumOfSingle = 0 , Addr = 0 , count = 0,temp = 0;
//Mod 求余运算,如果writeAddr 是 I2C_PageSize 整书倍,运算结果位Addr为0
Addr = WriteAddr % I2C_PageSize;
//差count个数值,刚好可以对齐到页面地址
count = I2C_PageSize - Addr;
//计算出要写多少整书页
NumOfPage = NumByteToWrite / I2C_PageSize;
//mod运算求余计算出不满一页的字节数
NumOfSingle = NumByteToWrite % I2C_PageSize;
// Addr = 0,则WriteAddr 刚好按页对齐aligned
// 这样就很简单了,直接写就可以写完整页后
// 把剩下的不满一页的写完即可
if(Addr == 0){
//如果 NumByteToWrite < I2C_PageSize
if(NumOfPage==0){
I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
I2C_EE_WaitEepromStandbyState();
}//如果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();
}
}
}
//如果 WriteAddr 不是按 I2C_PageSize 对齐
//那就算出对齐到页地址还需要多少数据,然后先把这几个数据写完,剩下开始的地址就已经对齐
//到页地址了,代码重复上面的即可
else{
//如果NumByteToWrite < I2C_PageSize
if(NumOfPage == 0){
//若NumOfSingle > count,当前面写不完,要写下一页
if(NumOfSingle > count){
temp = NumOfSingle - count;
I2C_EE_PageWrite(pBuffer , WriteAddr, count);
I2C_EE_WaitEepromStandbyState();
WriteAddr += count ;
pBuffer += count ;
I2C_EE_PageWrite(pBuffer , WriteAddr, temp);
I2C_EE_WaitEepromStandbyState();
}else{//若count 比 NumOfSingle大
I2C_EE_PageWrite(pBuffer , WriteAddr, NumByteToWrite);
I2C_EE_WaitEepromStandbyState();
}
}
//如果 NumByteToWrite > I2C_PageSize
else{
//地址不对齐多出的Count 分开处理,不加入这个运算
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / I2C_PageSize ;
NumOfSingle = NumByteToWrite % I2C_PageSize;
//先把 WriteAddr 所在页的剩余字节写了
if(count!=0){
I2C_EE_PageWrite(pBuffer , WriteAddr, count);
I2C_EE_WaitEepromStandbyState();
//加上 count 后,地址就对齐到页了
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();
}
}
}
}
/* EEPROM 读取
* @brief 从 EEPROM 里面读取一块数据
* @param pBuffer:存放从 EEPROM 读取的数据的缓冲区指针
* @param ReadAddr:接收数据的 EEPROM 的地址
* @param NumByteToRead:要从 EEPROM 读取的字节数
* @retval 正常返回 1,异常返回 0
*/
static uint8_t I2C_EE_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
I2CTimeout = I2CT_LONG_TIMEOUT ;
while(I2C_GetFlagStatus(_EEPROM_I2Cx , I2C_FLAG_BUSY)){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(9);}
}
//产生I2C起始信号
I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
//检测EV10 事件并清除标注
while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_MODE_SELECT)){
if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(10);}
}
//发送EEPROM 设备地址
I2C_Send7bitAddress(_EEPROM_I2Cx , EEPROM_ADDRESS , I2C_Direction_Transmitter);
I2CTimeout = I2CT_FLAG_TIMEOUT;
//检测EV11 事件并清除
while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(11);}
}
//通过重新设置PE位清除EV12事件
I2C_Cmd(_EEPROM_I2Cx ,ENABLE );
//发送要读取的EEPROM内部地址(即EEPROM内部存储器地址)
I2C_SendData(_EEPROM_I2Cx, ReadAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT ;
//检测EV12 事件并清除
while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(12);}
}
//产生第二次I2C起始信号
I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
//检测EV13 事件并清除
while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_MODE_SELECT)){
if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(13);}
}
//发送EEPROM 设备地址
I2C_Send7bitAddress(_EEPROM_I2Cx , EEPROM_ADDRESS , I2C_Direction_Receiver);
I2CTimeout = I2CT_FLAG_TIMEOUT;
//检测EV14 事件并清除
while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){
if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(14);}
}
//读取NumByteToRead个数据
while(NumByteToRead){
//如果 NumByteToRead=1,表示已经收到最后一个数据了
//发送应答信号结束输出
if(NumByteToRead == 1){
//发送非应答信号
I2C_AcknowledgeConfig(_EEPROM_I2Cx , DISABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_BYTE_RECEIVED)==0){
if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(3);}
}
//通过I2C,从设备中读取一个字节的数据
*pBuffer = I2C_ReceiveData(_EEPROM_I2Cx);
//存储数据的指针指以下地址
pBuffer++;
//接受数据自减
NumByteToRead--;
}
//发送停止信号
I2C_GenerateSTOP(_EEPROM_I2Cx , ENABLE);
//使能大应,方便一下I2C输出
I2C_AcknowledgeConfig(_EEPROM_I2Cx , ENABLE);
return 1;
}
//--------------------------------------------------------
/**
* @brief void _I2C_EE_Init(void)
* @param 无
* @retval 正常返回 1 ,不正常返回 0
*/
void _I2C_EE_Init(void){
I2C_GPIO_Config();
I2C_Mode_Config();
}
/**
* @brief I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
* @param 无
* @retval 正常返回 1 ,不正常返回 0
*/
void I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
u16 i;
printf("I2C_写入数据 \n");
I2C_EE_WaitEepromStandbyState();
I2C_EE_PageWrite(pBuffer,WriteAddr, NumByteToWrite);
for(i=0 ;i<NumByteToWrite ;i++ ){
printf("%d ", *pBuffer++);
if(i%16 == 15)
printf("\n\r");
}
printf("\nI2C_写入数据完成 \n");
I2C_EE_WaitEepromStandbyState();
for(i=0 ;i<NumByteToWrite ;i++ ){
printf("%d ", pBuffer[i]);
if(i%16 == 15)
printf("\n\r");
}
}
/**
* @brief I2C(void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite))读写测试
* @param 无
* @retval EEP_SIZE
*/
void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
u16 i;
printf("I2C_数据检测 \n");
I2C_EE_BufferRead(pBuffer,WriteAddr,NumByteToWrite);
printf("\nI2C_数据读取完毕 \n");
for(i=0 ;i<NumByteToWrite ;i++ ){
printf("%d ", pBuffer[i]);
if(i%16 == 15)
printf("\n\r");
}
printf("\n--->I2C_数据检测完成\n");
}
9.建立USART传输的 头文件 USART_book.h
代码如下 :
#ifndef __USART_BOOK_H_
#define __USART_BOOK_H_
#include "stm32f10x.h"
#include <stdio.h>
#include "stm32f10x_usart.h"
#include "stm32f10x_rcc.h"
//串口的宏定义 不同的串口挂在的总线和IO不一样
//串口1
#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_TX_GPIO_MODE GPIO_Mode_AF_PP
#define _DEBUG_USART_RX_GPIO_PORT GPIOA
#define _DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define _DEBUG_USART_RX_GPIO_MODE GPIO_Mode_IN_FLOATING
#define _DEBUG_NVIC_USART_IRQ USART1_IRQn
#define _DRBUG_USART_IRQHandler USART1_IRQHandler
void fn_USART_IO_Config(void);
void fn_USART_Config(void);
void fn_USART_Init(void);
void fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch );
void fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str);
void Usart_SendHalf_32_Word( USART_TypeDef * pUSARTx, uint32_t ch);
int fputc (int ch , FILE *f);
int fgetc(FILE *f);
void _DRBUG_USART_IRQHandler(void);
#endif
10.建立USART传输的C文件 USART_book.c
代码如下 :
#include "USART_book.h"
/**************************************************************
* @brief
* void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x ,
* LED_Corporate_state_t _LED_Corporate_state_t );
* @param
* //串口1
* #define _DEBUG_NVIC_USART_IRQ USART1_IRQn
* #define _DRBUG_NVIC_USART_IRQHandler USART1_IRQHandler
* @retval
*************************************************************/
static void NVIC_Configuration(void){
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制寄存器组选择*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置 USART 为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = _DEBUG_NVIC_USART_IRQ;
/* 抢断优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置 NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************
* @brief
* void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x ,
* LED_Corporate_state_t _LED_Corporate_state_t );
* @param
* //串口1
* // 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_TX_GPIO_MODE GPIO_Mode_AF_PP
* #define _DEBUG_USART_RX_GPIO_PORT GPIOA
* #define _DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
* #define _DEBUG_USART_RX_GPIO_MODE GPIO_Mode_AF_FLOATING
* @retval
*************************************************************/
void fn_USART_IO_Config(void){
GPIO_InitTypeDef GPIO_InitStructure;
// 打开串口 GPIO 的时钟
_DEBUG_USART_GPIO_APBxCLKCmd(_DEBUG_USART_GPIO_CLK , ENABLE);
//将USART TX 的GPIO配置为推挽模式
GPIO_InitStructure.GPIO_Pin = _DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = _DEBUG_USART_TX_GPIO_MODE;
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 = _DEBUG_USART_RX_GPIO_MODE;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(_DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure);
}
/**************************************************************
* @brief
* void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x ,
* LED_Corporate_state_t _LED_Corporate_state_t );
* @param
* //串口1
* #define _DEBUG_USARTx USART1
* #define _DEBUG_USART_CLK RCC_APB2Periph_USART1
* #define _DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
* #define _DEBUG_USART_BAUDRATE 115200
* @retval
*************************************************************/
void fn_USART_Config(void){
USART_InitTypeDef USART_InitStructure;
// 打开串口外设的时钟
_DEBUG_USART_APBxClkCmd(_DEBUG_USART_CLK , ENABLE);
//配置串口的工作参数
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);// 完成串口的初始化配置
NVIC_Configuration();// 串口中断优先级配置
USART_ITConfig(_DEBUG_USARTx , USART_IT_RXNE , ENABLE);// 使能串口接收中断
USART_Cmd(_DEBUG_USARTx , ENABLE);// 使能串口
}
/**************************************************************
* @brief
* void fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch );
* @param
* //串口1
* #define _DEBUG_USARTx USART1
* #define _DEBUG_USART_CLK RCC_APB2Periph_USART1
* #define _DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
* #define _DEBUG_USART_BAUDRATE 115200
* @retval
*************************************************************/
void fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch ){
/*发送一个字节数据到USART*/
USART_SendData(pUSARTx , ch);
/*等待发送数据寄存器为空*/
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);
}
/**************************************************************
* @brief
* void fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str);
* @param
* //串口1
* #define _DEBUG_USARTx USART1
* #define _DEBUG_USART_CLK RCC_APB2Periph_USART1
* #define _DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
* #define _DEBUG_USART_BAUDRATE 115200
* @retval
*************************************************************/
void fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str){
unsigned int k = 0;
do{
fn_Usart_Send_Byte(pUSARTx,*(str + k++));
}while(*(str + k)!='\0');
/*等待发送完成*/
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC));
}
/**************************************************************
* @brief
* void Usart_SendHalf_32_Word( USART_TypeDef * pUSARTx, uint32_t ch);
* @param
* @retval
*************************************************************/
void Usart_SendHalf_32_Word( USART_TypeDef * pUSARTx, uint32_t ch){
uint32_t temp_Half32;
uint8_t temp_Half=0,i_Half=4;
temp_Half32 =ch;
while(i_Half-->0){
temp_Half=(temp_Half32 & 0xFF000000)>>24;
temp_Half32<<=8;
fn_Usart_Send_Byte(pUSARTx,temp_Half);
}
/*等待发送完成*/
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC));
}
/**************************************************************
* @brief
* void fn_USART_Init(void);
* @param
* @retval
*************************************************************/
void fn_USART_Init(void){
fn_USART_IO_Config();
fn_USART_Config();
}
//须在 MDK 的工程选项把“Use MicroLIB”勾选上,MicoroLIB 是缺省 C 库的备选库,它对
//标准 C 库进行了高度优化使代码更少,占用更少资源。
/**************************************************************
* @brief
* int fputc (int ch , FILE *f)
* @param 重新定向C库函数Printf 到USART1
* @retval
*************************************************************/
int fputc (int ch , FILE *f){
/*发送一个字节数据到USART*/
USART_SendData(_DEBUG_USARTx , (uint8_t)ch);
/*等待发送数据寄存器为空*/
while(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)==RESET);
return (ch);
}
/**************************************************************
* @brief
* int fgetc(FILE *f);
* @param 重新定向C库函数Printf 到USART1
* @retval
*************************************************************/
int fgetc(FILE *f){
//等待串口1输入数据
while(USART_GetITStatus(_DEBUG_USARTx,USART_IT_RXNE)==RESET);
return (int)USART_ReceiveData(_DEBUG_USARTx);
}
/**************************************************************
* @brief
* void USART1_IRQHandler(void); 中断服务
* @param
* @retval
*************************************************************/
void _DRBUG_USART_IRQHandler(void){
uint8_t ucTemp = 0;
if(USART_GetITStatus(_DEBUG_USARTx,USART_IT_RXNE)!=RESET){
ucTemp = USART_ReceiveData(_DEBUG_USARTx);
USART_SendData(_DEBUG_USARTx ,ucTemp );
}
}
11.建立DMA传输的 头文件 DMA_book.h
代码如下 :
#ifndef __DMA_BOOK_H_
#define __DMA_BOOK_H_
#include "stm32f10x.h"
#define DMA_CLOCK RCC_AHBPeriph_DMA1 //DMA 时钟
/****** A ****************** ROM 到 RAM 的DMA输出 *******************************/
#define Map_DMA_CHANNEL DMA1_Channel6 // 当使用存储器到存储器模式时候,通道可以随便选,没有硬性的规定
#define Map_BUFFER_SIZE 20 // 要发送的数据大小
#define DMA_FLAG_TC DMA1_FLAG_TC6 // 传输完成标志
/* 定义 aSRC_Const_Buffer 数组作为 DMA 传输数据源
* const 关键字将 aSRC_Const_Buffer 数组变量定义为常量类型
* 表示数据存储在内部的 FLASH 中*/
extern const uint32_t aSRC_Cont_Buffer[Map_BUFFER_SIZE] ;
/* 定义 DMA 传输目标存储器存储在内部的 SRAM 中*/
extern uint32_t aDST_Buffer[Map_BUFFER_SIZE];
/*************************************************************************************/
/******** B **************** USART 到 RAM 的DMA输出 *******************************/
#define USART_DMA_CHANNEL DMA1_Channel4 //串口对应的 DMA 请求通道
#define USART_Source_ADDR (USART1_BASE+0x04) //串口数据的地址
extern uint32_t USART_BUFFER_SIZE ; // 要发送的数据大小
extern uint32_t* USART_DMA_Buffer ;
/************************************************************************************/
void _DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t _DMA_OutSource_ADDR, uint32_t _DMA_InSource_ADDR , uint32_t _DMA_DIR);
void _USART_DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t _DMA_OutSource_ADDR, uint32_t _DMA_InSource_ADDR , uint32_t _DMA_DIR);
uint8_t _Buffercmp(const uint32_t *pBuffer, uint32_t * pBuffer1 , uint16_t BufferLength);
void _Buffer_Show(uint32_t * pBuffer , uint16_t BufferLength);
//DMA对内存ROM数据的取出
void _DMA_ROM_TO_RAM(uint32_t _BUFFER_SIZE , uint32_t _DMA_Source_ADDR, uint32_t _DMA_AIM_ADDR );
//DMA对RAM到USART数据的取出
void _DMA_RAM_TO_USART(uint32_t _BUFFER_SIZE , uint32_t _DMA_Source_ADDR, uint32_t _DMA_AIM_ADDR );
#define _Map_DMA_Config_ _DMA_Config(Map_DMA_CHANNEL ,Map_BUFFER_SIZE ,aSRC_Cont_Buffer , aDST_Buffer , DMA_DIR_PeripheralSRC)
// ROM 到 RAM 的DMA输出 的程序初始化 DMA_DIR_PeripheralSRC:为方向外设到内存
#define _USART_DMA_Config_ _USART_DMA_Config(USART_DMA_CHANNEL ,USART_BUFFER_SIZE ,USART_Source_ADDR , USART_DMA_Buffer , DMA_DIR_PeripheralDST)
// ROM 到 RAM 的DMA输出 的程序初始化 DMA_DIR_PeripheralDST:为方向外设到内存
#define _DMA_InnerChange_ _Buffercmp(aSRC_Cont_Buffer , aDST_Buffer, Map_BUFFER_SIZE)
// ROM 到 RAM 的DMA输出 的数据验证
#define _RMA_InnerShow_ _Buffer_Show(aDST_Buffer, Map_BUFFER_SIZE)
#endif
12.建立DMA传输的C文件 DMA_book.c
代码如下 :
#include "DMA_book.h"
#include "USART_book.h"
#include "Systick_book.h"
const uint32_t aSRC_Cont_Buffer [Map_BUFFER_SIZE]={
'W','E','L','L',
'C','O','M','E',
' ','S','T','M',
'3','2',' ','S',
'T','U','D','Y',
};
uint32_t aDST_Buffer[Map_BUFFER_SIZE] ;
uint32_t* USART_DMA_Buffer ;
uint32_t USART_BUFFER_SIZE ;
void _DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t _DMA_Source_ADDR, uint32_t _DMA_AIM_ADDR , uint32_t _DMA_DIR){
DMA_InitTypeDef DMA_InitStructure ;
//开启DMA时钟
RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);
//源数据缓存地址(外设地址)
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)_DMA_Source_ADDR ;
//转换缓存地址地址(内存地址)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)_DMA_AIM_ADDR;
//方向:外设到存储器(这里的外设是内部的FLASH)DMA_DIR_PeripheralSRC:为方向外设到内存 DMA_DIR_PeripheralDST:为方向外设到内存
DMA_InitStructure.DMA_DIR = _DMA_DIR ;
//传输大小
DMA_InitStructure.DMA_BufferSize = _BUFFER_SIZE;
//外设(内部的FLASH)地址递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
//内存地址递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
//内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
//DMA模式,一次或者循环模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
//优先级:高
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//使能内存到内存的传输
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
//配置DMA通道
DMA_Init(_DMAy_Channelx , &DMA_InitStructure);
//使能DMA
DMA_Cmd(_DMAy_Channelx , ENABLE);
}
void _USART_DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t _DMA_Source_ADDR, uint32_t _DMA_AIM_ADDR , uint32_t _DMA_DIR){
DMA_InitTypeDef DMA_InitStructure ;
//开启DMA时钟
RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);
//源数据缓存地址(外设地址)
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)_DMA_Source_ADDR ;
//转换缓存地址地址(内存地址)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)_DMA_AIM_ADDR;
//方向:外设到存储器(这里的外设是内部的FLASH)DMA_DIR_PeripheralSRC:为方向外设到内存 DMA_DIR_PeripheralDST:为方向外设到内存
DMA_InitStructure.DMA_DIR = _DMA_DIR ;
//传输大小
DMA_InitStructure.DMA_BufferSize = _BUFFER_SIZE;
//外设(内部的FLASH)地址递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//内存地址递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//DMA_PeripheralDataSize_Byte; //注意这里需要根据数据类型经行修改
//内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//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(_DMAy_Channelx , &DMA_InitStructure);
//使能DMA
DMA_Cmd(_DMAy_Channelx , ENABLE);
}
///源数据与目标地址数据对比
uint8_t _Buffercmp(const uint32_t *pBuffer, uint32_t * pBuffer1 , uint16_t BufferLength){
/*数据长度递减*/
while(BufferLength--){
// Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer);
// Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer1);
/*判断两个数据源是否相等*/
if(*pBuffer != *pBuffer1){
/* 对应数据源不相等马上退出函数,并返回 0 */
return 0;
}
/* 递增两个数据源的地址指针 */
pBuffer++;
pBuffer1++;
}
/* 完成判断并且对应数据相对 */
return 1;
}
//对RAM数据进行展示
void _Buffer_Show(uint32_t * pBuffer , uint16_t BufferLength){
/*数据长度递减*/
while(BufferLength--){
Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer++);
}
}
//DMA对内存ROM数据的取出
void _DMA_ROM_TO_RAM(uint32_t _BUFFER_SIZE , uint32_t _DMA_Source_ADDR, uint32_t _DMA_AIM_ADDR ){
//----------------------------------------------------------------
printf("开始 ROM内存到RAM内存的DMA操作 \n");
//内存到内存DMA初始化
_DMA_Config(Map_DMA_CHANNEL ,_BUFFER_SIZE ,_DMA_Source_ADDR , _DMA_AIM_ADDR , DMA_DIR_PeripheralSRC);
while(DMA_GetFlagStatus(DMA_FLAG_TC) == RESET); //判断DMA传输结果是否正确
if(_DMA_InnerChange_== 0 ){
printf("ROM内存到DMA操作异常 \n");
}else{
printf("ROM内存到DMA操作正常 \n");
}
_RMA_InnerShow_;
}
//DMA对RAM到USART数据的取出
void _DMA_RAM_TO_USART(uint32_t _BUFFER_SIZE , uint32_t _DMA_Source_ADDR, uint32_t _DMA_AIM_ADDR ){
// 开始 USART内存到RAM内存的DMA操作
printf("\n开始 ROM到USART的传送初始化\n");
USART_BUFFER_SIZE = _BUFFER_SIZE;
USART_DMA_Buffer = _DMA_AIM_ADDR;
//内存到USART DMA初始化
_USART_DMA_Config(USART_DMA_CHANNEL ,USART_BUFFER_SIZE ,_DMA_Source_ADDR , USART_DMA_Buffer , DMA_DIR_PeripheralDST);
USART_DMACmd(_DEBUG_USARTx , USART_DMAReq_Tx , ENABLE); //串口DMA使能
/*USART_DMACmd 函数用于控制 USART 的 DMA 请求的启动和关闭。它接收三个参
数,第一个参数用于设置串口外设,可以是 USART1/2/3 和 UART4/5 这 5 个参数可选,第
二个参数设置串口的具体 DMA 请求,有串口发送请求 USART_DMAReq_Tx 和接收请求
USART_DMAReq_Rx 可选,第三个参数用于设置启动请求 ENABLE 或者关闭请求*/
fn_Systick_Delay(250,_Systick_ms); //DMA 传输进程中进行LED输出闪烁
while(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)==RESET);
printf("\rROM内存到USART外设的DMA操作完毕\n");//这个函数需要Delay 一段时间才可以用
}
//----------------------------------------------------------------
13.建立EXIT的 头文件 Exit_book.h
代码如下 :
#ifndef __EXIT_BOOK_H_
#define __EXIT_BOOK_H_
#include "stm32f10x.h"
#define _KEY_EXTI_IN_GPIO_Port GPIOA
#define _KEY_EXTI_IN_GPIO_Pin GPIO_Pin_0
#define _EXTI_IN_GPIO_PortSource GPIO_PortSourceGPIOA
#define _EXTI_IN_GPIO_PinSource GPIO_PinSource0
#define _EXTI_IN_EXTI_Line EXTI_Line0
#define _EXTI_IN_EXTI_Trigger EXTI_Trigger_Rising
#define _EXTI_IN_GPIO_Clock RCC_APB2Periph_AFIO
#define _EXTI_IN_EXTI_Mode EXTI_Mode_Interrupt
#define _EXTI_IN_EXTI_LineCmd ENABLE
#define _NVIC_IN_EXTI_IRQChannel EXTI0_IRQn
#define _NVIC_IN_EXTI_IRQChannelCmd ENABLE
#define _KEY2_EXTI_IN_GPIO_Port GPIOC
#define _KEY2_EXTI_IN_GPIO_Pin GPIO_Pin_13
#define _EXTI_IN2_GPIO_PortSource GPIO_PortSourceGPIOC
#define _EXTI_IN2_GPIO_PinSource GPIO_PinSource13
#define _EXTI_IN2_EXTI_Line EXTI_Line13
#define _EXTI_IN2_EXTI_Trigger EXTI_Trigger_Falling
#define _EXTI_IN2_GPIO_Clock RCC_APB2Periph_AFIO
#define _EXTI_IN2_EXTI_Mode EXTI_Mode_Interrupt
#define _EXTI_IN2_EXTI_LineCmd ENABLE
#define _NVIC_IN2_EXTI_IRQChannel EXTI15_10_IRQn
#define _NVIC_IN2_EXTI_IRQChannelCmd ENABLE
void fn_EXTI_GPIO_Config(void);
void fn_NVIC_Config(void);
void EXTI0_IRQHandler(void);
#endif
14.建立EXIT的C文件 Exit_book.c
代码如下 :
#include "Exit_book.h"
#include "Led_book.h"
/**************************************************************
* @brief
* void fn_EXTI_GPIO_Config(void)
* @param
*
* #define _KEY_EXTI_IN_GPIO_Port GPIOA
* #define _KEY_EXTI_IN_GPIO_Pin GPIO_Pin_0
* #define _EXTI_IN_GPIO_PortSource GPIO_PortSourceGPIOA
* #define _EXTI_IN_GPIO_PinSource GPIO_PinSource0
* #define _EXTI_IN_EXTI_Line EXTI_Line0
* #define _EXTI_IN_EXTI_Trigger EXTI_Trigger_Rising
* #define _EXTI_IN_GPIO_Clock RCC_APB2Periph_AFIO
* #define _EXTI_IN_EXTI_Mode EXTI_Mode_Interrupt
* #define _EXTI_IN_EXTI_LineCmd ENABLE
*
* #define _KEY2_EXTI_IN_GPIO_Port GPIOC
* #define _KEY2_EXTI_IN_GPIO_Pin GPIO_Pin_13
* #define _EXTI_IN2_GPIO_PortSource GPIO_PortSourceGPIOC
* #define _EXTI_IN2_GPIO_PinSource GPIO_PinSource13
* #define _EXTI_IN2_EXTI_Line EXTI_Line13
* #define _EXTI_IN2_EXTI_Trigger EXTI_Trigger_Falling
* #define _EXTI_IN2_GPIO_Clock RCC_APB2Periph_AFIO
* #define _EXTI_IN2_EXTI_Mode EXTI_Mode_Interrupt
* #define _EXTI_IN2_EXTI_LineCmd ENABLE
* @retval
*************************************************************/
void fn_EXTI_GPIO_Config(void){
EXTI_InitTypeDef EXIT_InitStruck;
RCC_APB2PeriphClockCmd(_EXTI_IN_GPIO_Clock , ENABLE);
//注意:我们除了开 GPIO 的端口时钟外,我们还打开了 AFIO 的时钟
GPIO_EXTILineConfig(_EXTI_IN_GPIO_PortSource | _EXTI_IN2_GPIO_PortSource , _EXTI_IN_GPIO_PinSource | _EXTI_IN2_GPIO_PinSource);
/* 选择 EXTI 的信号源 */
// GPIO_EXTILineConfig 函数用来指定中断/事件线的输入源,它实际是设定外部中断配
// 置寄存器的 AFIO_EXTICRx 值,该函数接收两个参数,第一个参数指定 GPIO 端口源,第
// 二个参数为选择对应 GPIO 引脚源编号。
EXIT_InitStruck.EXTI_Line = _EXTI_IN_EXTI_Line ; /* 选择 EXTI 的信号源 */
EXIT_InitStruck.EXTI_Mode = _EXTI_IN_EXTI_Mode; /* EXTI 为中断模式 */
EXIT_InitStruck.EXTI_Trigger = _EXTI_IN_EXTI_Trigger ; /* 上升沿中断 */
EXIT_InitStruck.EXTI_LineCmd = _EXTI_IN_EXTI_LineCmd; /* 使能中断 */
EXTI_Init(&EXIT_InitStruck);
// EXTI初始化配置的变量
// fn_NVIC_Config();
// 调用 NVIC_Configuration函数完成对按键 1、按键 2 优先级配置并使能中断通道
EXIT_InitStruck.EXTI_Line = _EXTI_IN2_EXTI_Line; /* 选择 EXTI 的信号源 */
EXIT_InitStruck.EXTI_Mode = _EXTI_IN2_EXTI_Mode; /* EXTI 为中断模式 */
EXIT_InitStruck.EXTI_Trigger = _EXTI_IN2_EXTI_Trigger; /* 下降沿中断 */
EXIT_InitStruck.EXTI_LineCmd = _EXTI_IN_EXTI_LineCmd;/* 使能中断 */
EXTI_Init(&EXIT_InitStruck);
fn_NVIC_Config();
}
/**************************************************************
* @brief
* void fn_NVIC_Config(void)
* @param
* #define _NVIC_IN_EXTI_IRQChannel EXTI0_IRQn
* #define _NVIC_IN_EXTI_IRQChannelCmd ENABLE
* #define _NVIC_IN2_EXTI_IRQChannel EXTI15_10_IRQn
* #define _NVIC_IN2_EXTI_IRQChannelCmd ENABLE
* @retval
*************************************************************/
void fn_NVIC_Config(void){
NVIC_InitTypeDef NVIC_InitStruct;
/* 配置 NVIC 为优先级组 1 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置中断源: */
NVIC_InitStruct.NVIC_IRQChannel = _NVIC_IN_EXTI_IRQChannel; //EXTI0_IRQn;
/* 配置抢占优先级:1 */
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级:1 */
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStruct.NVIC_IRQChannelCmd = _NVIC_IN_EXTI_IRQChannelCmd; //ENABLE
NVIC_Init(&NVIC_InitStruct);
/* 配置中断源: */
NVIC_InitStruct.NVIC_IRQChannel = _NVIC_IN2_EXTI_IRQChannel; //EXTI0_IRQn;
NVIC_Init(&NVIC_InitStruct);
}
/**************************************************************
* @brief
* void fn_NVIC_Config(void)
* @param
* #define _KEY_EXTI_IN_GPIO_Port GPIOA
* #define _KEY_EXTI_IN_GPIO_Pin GPIO_Pin_0
* @retval
*************************************************************/
void EXTI0_IRQHandler(void){
// EXTI_GetITStatus 函数用来获取 EXTI 的中断标志位状态,如果 EXTI 线有中断发生函
//数返回“SET”否则返回“RESET”。实际上,EXTI_GetITStatus 函数是通过读取
//EXTI_PR寄存器值来判断 EXTI线状态的。
if(EXTI_GetITStatus(_EXTI_IN_EXTI_Line)!= RESET){
if(GPIO_ReadInputDataBit(_KEY_EXTI_IN_GPIO_Port, _KEY_EXTI_IN_GPIO_Pin)==1){
__LED_Change__;
}
}
EXTI_ClearITPendingBit(_EXTI_IN_EXTI_Line); // 重要的清除中断标志位
}
void EXTI15_10_IRQHandler(void){
if(EXTI_GetITStatus(_EXTI_IN2_EXTI_Line)!= RESET){
if(GPIO_ReadInputDataBit(_KEY2_EXTI_IN_GPIO_Port, _KEY2_EXTI_IN_GPIO_Pin)==0){
__LED_Change__;
}
}
EXTI_ClearITPendingBit(_EXTI_IN2_EXTI_Line); // 重要的清除中断标志位
}
15.建立Key传输的 头文件 Key_book.h
代码如下 :
#ifndef __KEY_BOOK_H_
#define __KEY_BOOK_H_
#include "stm32f10x.h"
#include "Led_book.h"
#define KEY_IN_GPIO_Port GPIOA
#define KEY_IN_GPIO_Clock RCC_APB2Periph_GPIOA
#define KEY_IN_GPIO_Pin GPIO_Pin_0
#define KEY_IN_GPIO_Pin_Bit 0
#define Key_IN_GPIO_Modle GPIO_Mode_IN_FLOATING //浮空输入
#define KEY2_IN_GPIO_Port GPIOC
#define KEY2_IN_GPIO_Clock RCC_APB2Periph_GPIOC
#define KEY2_IN_GPIO_Pin GPIO_Pin_13
#define KEY2_IN_GPIO_Pin_Bit 13
#define Key2_IN_GPIO_Modle GPIO_Mode_IN_FLOATING //浮空输入
typedef union {
struct{
unsigned char BIT0:1;unsigned char BIT1:1;unsigned char BIT2:1;unsigned char BIT3:1;
unsigned char BIT4:1;unsigned char BIT5:1;unsigned char BIT6:1;unsigned char BIT7:1;
//unsigned char BIT8:1;unsigned char BIT9:1;unsigned char BIT10:1;unsigned char BIT11:1;
//unsigned char BIT12:1;unsigned char BIT13:1;unsigned char BIT14:1;unsigned char BIT15:1;
}DATA_BIT;
uint8_t DATA_BYTE;
}Per_key_type;
extern volatile Per_key_type key_flag;
#define bkey_10ms key_flag.DATA_BIT.BIT0
#define bkey_judge key_flag.DATA_BIT.BIT1
#define bkey_judge_long key_flag.DATA_BIT.BIT2
#define bkey_Effect key_flag.DATA_BIT.BIT3
#define bkey_LongEffect key_flag.DATA_BIT.BIT4
#define bkey_Effect_Lose key_flag.DATA_BIT.BIT5
#define bkey_Effect_LLose key_flag.DATA_BIT.BIT6
void fn_Key_GPIO_Config( GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock , uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef );
void fn_Key_Init(void);
void fn_key_judge(void);
void fn_key_Effect(void);
void fn_key_Check(void);
#endif
16.建立Key的C文件 Key_book.c
代码如下 :
#include "Key_book.h"
volatile Per_key_type key_flag;
/**************************************************************
* @brief
* void fn_Key_GPIO_Config( GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,
* uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef );
* @param
* #define KEY_IN_GPIO_Port GPIOA
* #define KEY_IN_GPIO_Clock RCC_APB2Periph_GPIOA
* #define KEY_IN_GPIO_Pin GPIO_Pin_0
* #define KEY_IN_GPIO_Pin_Bit 0
* #define Key_IN_GPIO_Modle GPIO_Mode_IN_FLOATING //浮空输入
*
* #define KEY2_IN_GPIO_Port GPIOC
* #define KEY2_IN_GPIO_Clock RCC_APB2Periph_GPIOC
* #define KEY2_IN_GPIO_Pin GPIO_Pin_13
* #define KEY2_IN_GPIO_Pin_Bit 13
* #define Key2_IN_GPIO_Modle GPIO_Mode_IN_FLOATING //浮空输入
* @retval
*************************************************************/
void fn_Key_GPIO_Config( GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock , uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef ){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = _GPIOMode_TypeDef;
GPIO_InitStruct.GPIO_Pin = _GPIO_Pin_x;
RCC_APB2PeriphClockCmd(_GPIO_Clock,ENABLE);
GPIO_Init(_GPIO_x , &GPIO_InitStruct);
}
/**************************************************************
* @brief
* void fn_Key_Init(void);
* @param
* @retval
*************************************************************/
void fn_Key_Init(void){
fn_Key_GPIO_Config(KEY_IN_GPIO_Port,KEY_IN_GPIO_Clock,KEY_IN_GPIO_Pin,Key_IN_GPIO_Modle);
fn_Key_GPIO_Config(KEY2_IN_GPIO_Port,KEY2_IN_GPIO_Clock,KEY2_IN_GPIO_Pin,Key2_IN_GPIO_Modle);
}
/************************************************************
* @brief
* void fn_key_judge(void);
* @param
* @retval
**************************************************************/
#define _LONG_key 30
static uint16_t count_key ;
void fn_key_judge(void){
if(!bkey_10ms){return;}
bkey_10ms = 0;
if(GPIO_ReadInputDataBit(KEY_IN_GPIO_Port,KEY_IN_GPIO_Pin)){
if(count_key++<3){return;}
if(!bkey_judge){
bkey_judge = 1;
bkey_Effect = 1;
}else{
if(count_key>_LONG_key){
bkey_judge_long = 1;
bkey_LongEffect = 1;
}
}
}
else{
count_key = 0;
if(bkey_judge){
bkey_judge = 0;
if(bkey_judge_long){
bkey_judge_long = 0;
bkey_Effect_LLose = 1;
}else{
bkey_judge_long = 0;
bkey_Effect_Lose = 1;
}
}else{
bkey_judge = 0;
}
}
}
/************************************************************
* @brief
* void fn_key_Effect(void);
* @param
* @retval
*************************************************************/
void fn_key_Effect(void){
if(bkey_Effect){
bkey_Effect = 0;
fn_LED_Corporate(LED_OUT_GPIO_Port,LED_OUT_GPIO_Pin,LED_Corporate_Toggle);
}
}
/**************************************************************
* @brief
* void fn_key_Check(void);
* @param
* @retval
*************************************************************/
void fn_key_Check(void){
fn_key_judge();
fn_key_Effect();
}
17.建立LED 的头文件 Led_book.h
代码如下 :
#ifndef __LED_BOOK_H_
#define __LED_BOOK_H_
#include "stm32f10x.h"
#define LED_OUT_GPIO_Port GPIOB //GPIO Point
#define LED_OUT_GPIO_Clock RCC_APB2Periph_GPIOB //GPIO clock
#define LED_OUT_GPIO_Pin GPIO_Pin_5
#define LED_OUT_GPIO_Pin_Bit 5
#define LED_OUT_GPIO_Modle GPIO_Mode_Out_PP
#define LED_R_OUT_GPIO_Pin GPIO_Pin_5
#define LED_G_OUT_GPIO_Pin GPIO_Pin_0
#define LED_B_OUT_GPIO_Pin GPIO_Pin_1
typedef enum {
LED_Corporate_On = 1,
LED_Corporate_OFF = 2,
LED_Corporate_Toggle = 3,
} LED_Corporate_state_t;
void fn_LED_GPIO_Config(GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,\
uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef);
void fn_Led_Init(void);
void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x , \
LED_Corporate_state_t _LED_Corporate_state_t );
void fn_LED_ALL_OFF(void);
#define __LED_Change__ fn_LED_Corporate(LED_OUT_GPIO_Port,LED_OUT_GPIO_Pin,LED_Corporate_Toggle)
#define __R_OUT__ GPIO_ResetBits(LED_OUT_GPIO_Port,LED_R_OUT_GPIO_Pin)
#define __G_OUT__ GPIO_ResetBits(LED_OUT_GPIO_Port,LED_G_OUT_GPIO_Pin)
#define __B_OUT__ GPIO_ResetBits(LED_OUT_GPIO_Port,LED_B_OUT_GPIO_Pin)
#endif
18.建立LED 的 文件 Led_book.c
代码如下 :
#include "Led_book.h"
/**************************************************************
* @brief
* void fn_LED_GPIO_Config(GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,
* uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef);
* @param
* @retval
*************************************************************/
#define LED_GPIO_Speed GPIO_Speed_10MHz
void fn_LED_GPIO_Config(GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = _GPIOMode_TypeDef;
GPIO_InitStruct.GPIO_Pin = _GPIO_Pin_x;
GPIO_InitStruct.GPIO_Speed = LED_GPIO_Speed;
RCC_APB2PeriphClockCmd(_GPIO_Clock ,ENABLE);
GPIO_Init(_GPIO_x , &GPIO_InitStruct) ;
GPIO_SetBits(_GPIO_x,_GPIO_Pin_x);
}
/**************************************************************
* @brief
* void fn_Led_Init(void);
* @param
* @retval
*************************************************************/
void fn_Led_Init(void){
fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_R_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_G_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_B_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
fn_LED_ALL_OFF();
}
/**************************************************************
* @brief
* void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x ,
* LED_Corporate_state_t _LED_Corporate_state_t );
* @param
* @retval
*************************************************************/
void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x , LED_Corporate_state_t _LED_Corporate_state_t ){
switch(_LED_Corporate_state_t){
case LED_Corporate_On :
GPIO_SetBits(_GPIO_x,_GPIO_Pin_x);
break;
case LED_Corporate_OFF:
GPIO_ResetBits(_GPIO_x,_GPIO_Pin_x);
break;
case LED_Corporate_Toggle:
GPIO_ReadOutputDataBit(_GPIO_x,_GPIO_Pin_x)?GPIO_ResetBits(_GPIO_x,_GPIO_Pin_x):GPIO_SetBits(_GPIO_x,_GPIO_Pin_x);
break;
}
}
void fn_LED_ALL_OFF(void){
GPIO_SetBits(LED_OUT_GPIO_Port,LED_R_OUT_GPIO_Pin);
GPIO_SetBits(LED_OUT_GPIO_Port,LED_G_OUT_GPIO_Pin);
GPIO_SetBits(LED_OUT_GPIO_Port,LED_B_OUT_GPIO_Pin);
}
//practice
//fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
// while(1){
// delay(10000);
// fn_LED_Corporate(LED_OUT_GPIO_Port,LED_OUT_GPIO_Pin,LED_Corporate_Toggle);
// }
19.建立 Systick传输的 头文件 Systick_book.h
代码如下 :
#ifndef __SYSTIC_BOOK_H_
#define __SYSTIC_BOOK_H_
#include "stm32f10x.h"
#include "Key_book.h"
typedef enum {
_Systick_us = 1,
_Systick_ms = 2,
_Systick_s = 3,
} Systick_time_state_t;
void fn_Systick_Delay(uint32_t _Delay_time , Systick_time_state_t _Systick_time_state_t);
void fn_Systick_Delay_Handler_set(uint32_t _Delay_ms , Systick_time_state_t _Systick_time_state_t);
void fn_SysTick_delay_decrement(void);
void SysTick_Handler(void);
#define __Systick_Delay_Handler_set__ fn_Systick_Delay_Handler_set(10,_Systick_ms)
#endif
20.建立 Systick的C文件 Systick_book.c
代码如下 :
#include "Systick_book.h"
/************************************************************
* @brief
* void fn_Systick_Delay(uint32_t _Delay_time , \
Systick_time_state_t _Systick_time_state_t){
* @param
* @retval
*************************************************************/
void fn_Systick_Delay(uint32_t _Delay_time , Systick_time_state_t _Systick_time_state_t){
uint32_t i;
if(_Systick_time_state_t == _Systick_us){SysTick_Config(SystemCoreClock/1000000);}
if(_Systick_time_state_t == _Systick_ms){
SysTick_Config(SystemCoreClock/1000);
}
else{SysTick_Config(SystemCoreClock);}
for( i=0;i<_Delay_time ; i++){
while(!((SysTick->CTRL)&(1<<16)));
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
/************************************************************
* @brief
* void fn_Systick_Delay_Handler_set(uint32_t _Delay_ms , \
* Systick_time_state_t _Systick_time_state_t){
* @param
* @retval
*************************************************************/
static uint32_t _SysTick_delay = 0 ;
void fn_Systick_Delay_Handler_set(uint32_t _Delay_ms , Systick_time_state_t _Systick_time_state_t){
if(_Systick_time_state_t == _Systick_us){SysTick_Config(SystemCoreClock/1000000);}
if(_Systick_time_state_t == _Systick_ms){
SysTick_Config(SystemCoreClock/1000);
}
else{SysTick_Config(SystemCoreClock);}
_SysTick_delay = _Delay_ms ;
}
/************************************************************
* @brief
* void fn_SysTick_delay_decrement(void)
* @param
* @retval
*************************************************************/
static uint32_t SysTick_delay = 0 ;
void fn_SysTick_delay_decrement(void){
if(SysTick_delay++ > _SysTick_delay){
SysTick_delay = 0;
bkey_10ms = 1;
}
}
/************************************************************
* @brief
* void SysTick_Handler(void)
* @param
* @retval
*************************************************************/
void SysTick_Handler(void){
fn_SysTick_delay_decrement();
}
21.建立 头文件函数 头文件 PROJ_book.h
代码如下 :
#ifndef __PROJ_BOOK_H__
#define __PROJ_BOOK_H__
#include "stm32f10x.h"
#include "Led_book.h"
#include "Key_book.h"
#include "RCC_book.h"
#include "Systick_book.h"
#include "Exit_book.h"
#include "USART_book.h"
#include "DMA_book.h"
#include "I2C_book.h"
#include "I2C_soft_book.h"
#endif