2021-06-18 STM32F103 DMA 与 DMA串口代码 使用固件库

本文展示了STM32 DMA程序串口的
内容涉及 :
DMA 内存传输 DMA外设内存传输
USART串口的识别
IO口输入输出
按键的外部中断处理
32位数据通讯,字符串通讯,单字符通讯

完整代码


前言

STM32 的 DMA简介 DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是 DMA2 只存 在于大容量的单片机中。

DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储 器到存储器。

GITthub 代码源码下载

一、 编程要点

USART:
1) 使能 DMA 时钟;
2) 配置 DMA 数据参数;
3) 使能 DMA,进行传输;
4) 等待传输完成,并对源数据和目标地址数据进行比较。
5) 配置 USART 通信功能;
6) 设置串口 DMA 工作参数;
7) 使能 DMA;
8) DMA 传输同时 CPU 可以运行其他任务。

二、使用步骤

1.理解原理图

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

代码如下:
: STM32F103ZET6 串口引脚位PA9 ,10
: STM32F103ZET6 输出口为PB5低电平点有效
: STM32F103ZET6 Key检测脚为PA8

2.建立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     32             // 要发送的数据大小
#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)    //串口数据的地址
#define   USART_BUFFER_SIZE     9000               // 要发送的数据大小
extern    uint32_t              USART_DMA_Buffer[USART_BUFFER_SIZE];
/************************************************************************************/

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);
uint8_t   Buffercmp(const uint32_t *pBuffer, uint32_t * pBuffer1 , uint16_t BufferLength);
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);

#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   _DMA_InnerChange_  Buffercmp(aSRC_Cont_Buffer , aDST_Buffer, Map_BUFFER_SIZE)
//  ROM 到 RAM 的DMA输出  的数据验证

#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:为方向外设到内存


#endif

3.建立DMA传输的 头文件 DMA_book.c

代码如下(示例):

#include "DMA_book.h"
#include "USART_book.h"

const uint32_t  aSRC_Cont_Buffer  [Map_BUFFER_SIZE]={
    
    
   'A','B','C','D',
   'E','F','B','C',
   'A','X','B','C',
   'A','D','B','C',
   'A','C','B','C',
   'A','V','B','C',
   'A','B','W','C',
  ' A','R','B','C'
};
uint32_t    aDST_Buffer[Map_BUFFER_SIZE] ;
uint32_t    USART_DMA_Buffer[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);
}

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;
}


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_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(_DMAy_Channelx , &DMA_InitStructure);
  //使能DMA
  DMA_Cmd(_DMAy_Channelx , ENABLE);
}
 

4.建立USART串口的 头文件 USART_book.h

代码如下(示例):

#ifndef  __USART_BOOK_H_
#define  __USART_BOOK_H_

#include "stm32f10x.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);
void  _DRBUG_USART_IRQHandler(void);
#endif

5.建立USART串口的 头文件 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 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 );
  }
}
/**************************************************************
* @brief  
* void fn_USART_Init(void);
* @param  
* @retval 
*************************************************************/ 
void fn_USART_Init(void){
    
    
  fn_USART_IO_Config();
  fn_USART_Config();
}

6.利用之前的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



7.利用之前的Systick 输出的 头文件 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();
}

8.利用之前的RCC_book输出的 头文件 RCC_book.h

代码如下(示例):

#ifndef  __RCC_BOOK_H_
#define  __RCC_BOOK_H_

#include "stm32f10x.h"
 
#define   RCC_OUT_GPIO_Port     GPIOA                 //GPIO Point
#define   RCC_OUT_GPIO_Clock    RCC_APB2Periph_GPIOA  //GPIO clock
#define   RCC_OUT_GPIO_Pin      GPIO_Pin_8             
#define   RCC_OUT_GPIO_Pin_Bit  8
#define   RCC_OUT_GPIO_Modle    GPIO_Mode_AF_PP
#define   RCC_OUT_GPIO_Speed    GPIO_Speed_10MHz

void  fn_HRCC_SetSystic( uint32_t  _RCC_PLLMul_x );
void  fn_IRCC_SetSystic( uint32_t  _RCC_PLLMul_x );
void  fn_MCO_GPIO_Config(void);
void  fn_RCC_Init(void);
#endif

9.利用之前的RCC_book输出的 头文件 RCC_book.c

代码如下(示例):

#include  "RCC_book.h"

/************************************************************
* @brief  
* void  fn_HRCC_SetSystic( uint32_t  _RCC_PLLMul_x );
* @param  
* @retval 
*************************************************************/ 
void  fn_HRCC_SetSystic( uint32_t  _RCC_PLLMul_x ){
    
    
  __IO  uint32_t  HSEStatus = 0;
  /* SYSCLK , HCLK , PCLK2 and PCLK1 cnfiguration */
  RCC_DeInit();
  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);
  /* wait till HSE is ready and if Time Out is reached exit*/
  HSEStatus = RCC_WaitForHSEStartUp();
  if(HSEStatus == SUCCESS){
    
    
    // Enable Prefetch Buffer  缓冲区可开启
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    // Flash 2 wait state 
    FLASH_SetLatency(FLASH_Latency_2);
    // 这些位表示SYSCLK(系统时钟)周期与闪存访问的比例
    // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2
    // 设置成 2 的时候,SYSCLK 低于 48M 也可以工作,如果设置成 0 或者 1 的时候,
    // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
    // 0:0 < SYSCLK <= 24M
    // 1:24< SYSCLK <= 48M
    // 2:48< SYSCLK <= 72M */
    
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
    /* HCLK = SYSCLK */     //AHB预分频 (AHB Prescaler) 
    RCC_PCLK1Config(RCC_HCLK_Div2);
    /* PCLK1 = HCLK */     //低速APB预分频(APB1) (APB low-speed prescaler (APB1))   
    RCC_PCLK2Config(RCC_HCLK_Div1);
    /* PCLK1 = HCLK */     //低速APB预分频(APB1) (APB low-speed prescaler (APB1))   
  
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1 ,_RCC_PLLMul_x);
    RCC_PLLCmd(ENABLE);
    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz 
    //  PLLSRC: PLL输入时钟源 (PLL entry clock source)
    //  RCC_PLLSource_HSE_Div1 RCC_PLLSource_HSE_Div2 RCC_PLLSource_HSI_Div1
    //  _RCC_PLLMul_x  为倍频因子*/
    while((RCC_GetFlagStatus(RCC_FLAG_PLLRDY))==RESET);
     /* Wait till PLL is ready */
    
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
     /* Select PLL as system clock source */
    //		SW[1:0]:系统时钟切换 (System clock switch)  位1:0
    //		由软件置’1’或清’0’来选择系统时钟源。
    //		在从停止或待机模式中返回时或直接或间接作为系统时钟的HSE出现故障时,由硬件强制选择
    //		HSI作为系统时钟(如果时钟安全系统已经启动)
    //		00:HSI作为系统时钟;
    //		01:HSE作为系统时钟;
    //		10:PLL输出作为系统时钟;
    //		11:不可用  */
    
    while (RCC_GetSYSCLKSource()!=0x08);  
  }else{
    
    
     
  }
}

/************************************************************
* @brief  
* void  fn_IRCC_SetSystic( uint32_t  _RCC_PLLMul_x );
* @param  
* @retval 
*************************************************************/ 
void  fn_IRCC_SetSystic( uint32_t  _RCC_PLLMul_x ){
    
    
  __IO uint32_t HSIStatus = 0;
  __IO uint32_t StartUpCounter = 0;
  /*SYSCLK  HCLK PCLK2 PCLK1 con figuration---*/
  RCC_DeInit();
  
  /*Enable HSE*/
  RCC_HSICmd(ENABLE);
  
  /* Wait till HSE is ready and if Time out is reached exit */
  do{
    
    
     HSIStatus = RCC->CR & RCC_CR_HSIRDY;
     StartUpCounter++;
  }while((HSIStatus == 0)&&(StartUpCounter != HSE_STARTUP_TIMEOUT));
  
  if(HSIStatus == SUCCESS){
    
    
     /*Enable Prefetch Buffer 预计缓冲区开启 */
     FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
     /*FLASH 2 Wait state */ //这些位表示SYSCLK(系统时钟)周期与闪存访问时间的比例  
     FLASH_SetLatency(FLASH_Latency_2);
    // 这些位表示SYSCLK(系统时钟)周期与闪存访问的比例
    // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 2
    // 设置成 2 的时候,SYSCLK 低于 48M 也可以工作,如果设置成 0 或者 1 的时候,
    // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
    // 0:0 < SYSCLK <= 24M
    // 1:24< SYSCLK <= 48M
    // 2:48< SYSCLK <= 72M */
    
      RCC_HCLKConfig(RCC_SYSCLK_Div1);
      /* HCLK = SYSCLK */     //AHB预分频 (AHB Prescaler) 
      RCC_PCLK1Config(RCC_HCLK_Div2);
      /* PCLK1 = HCLK */     //低速APB预分频(APB1) (APB low-speed prescaler (APB1))   
      RCC_PCLK2Config(RCC_HCLK_Div1);
      /* PCLK1 = HCLK */     //低速APB预分频(APB1) (APB low-speed prescaler (APB1))   
  
      RCC_PLLConfig(RCC_PLLSource_HSI_Div2,_RCC_PLLMul_x);
      RCC_PLLCmd(ENABLE);
      /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz 
      //  PLLSRC: PLL输入时钟源 (PLL entry clock source)
      //  RCC_PLLSource_HSE_Div1 RCC_PLLSource_HSE_Div2 RCC_PLLSource_HSI_Div1
      //  _RCC_PLLMul_x  为倍频因子*/
      
      while((RCC_GetFlagStatus(RCC_FLAG_PLLRDY))== RESET);
      /* Wait till PLL is ready */
      
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
      /* Select PLL as system clock source */
      
      //		SW[1:0]:系统时钟切换 (System clock switch)  位1:0
      //		由软件置’1’或清’0’来选择系统时钟源。
      //		在从停止或待机模式中返回时或直接或间接作为系统时钟的HSE出现故障时,由硬件强制选择
      //		HSI作为系统时钟(如果时钟安全系统已经启动)
      //		00:HSI作为系统时钟;
      //		01:HSE作为系统时钟;
      //		10:PLL输出作为系统时钟;
      //		11:不可用
          
      /** Wait till PLL is used as system clock source
            - 0x00: HSI used as system clock
            - 0x04: HSE used as system clock
            - 0x08: PLL used as system clock
      */
      while(RCC_GetSYSCLKSource()!=0x08);
  }else{
    
    
    
  }

}

/************************************************************
* @brief  
* void  fn_MCO_GPIO_Config(void);
* @param  
*    #define   RCC_OUT_GPIO_Port     GPIOA                 //GPIO Point
*    #define   RCC_OUT_GPIO_Clock    RCC_APB2Periph_GPIOA  //GPIO clock
*    #define   RCC_OUT_GPIO_Pin      GPIO_Pin_8             
*    #define   RCC_OUT_GPIO_Pin_Bit  8
*    #define   RCC_OUT_GPIO_Modle    GPIO_Mode_AF_PP
*    #define   RCC_OUT_GPIO_Speed    GPIO_Speed_10MHz
* @retval 
*************************************************************/ 
void  fn_MCO_GPIO_Config(void){
    
    
   GPIO_InitTypeDef  GPIO_InitStruct;
   GPIO_InitStruct.GPIO_Mode = RCC_OUT_GPIO_Modle;
   GPIO_InitStruct.GPIO_Pin = RCC_OUT_GPIO_Pin;
   GPIO_InitStruct.GPIO_Speed = RCC_OUT_GPIO_Speed;
   
   RCC_APB2PeriphClockCmd(RCC_OUT_GPIO_Clock,ENABLE);
   GPIO_Init(RCC_OUT_GPIO_Port,&GPIO_InitStruct);
   RCC_MCOConfig(RCC_MCO_SYSCLK);
   //RCC_MCOConfig(RCC_MCO_HSI);
   //RCC_MCOConfig(RCC_MCO_HSE);
}

/************************************************************
* @brief  
* void fn_RCC_Init(void);
* @param  
* @retval 
*************************************************************/ 
void fn_RCC_Init(void){
    
    
    fn_HRCC_SetSystic(RCC_PLLMul_9 );
    //fn_IRCC_SetSystic(RCC_PLLMul_9 );
    //fn_MCO_GPIO_Config();
}


10.利用之前的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

11.利用之前的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);		 
// }	 

12.利用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

13.利用Key输出的程序 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();
}


14.利用复习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

15.利用复习Exit中断的程序 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);  // 重要的清除中断标志位 
}
 

16.建立USART 输出的 主程序 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 delay(int x);
void fn_LED_Flash_Init(void);
void fn_usart_show_Init(void);
int main(void)
{
    
     
      uint32_t i;
      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_Usart_SendString(_DEBUG_USARTx,": 开始 ROM内存到RAM内存的DMA操作 \n");  
      _Map_DMA_Config_;       //内存到内存DMA初始化
      while(DMA_GetFlagStatus(DMA_FLAG_TC) == RESET);  //判断DMA传输结果是否正确          
      if(_DMA_InnerChange_== 0 ){
    
    
          fn_LED_ALL_OFF();
          __B_OUT__;
      }else{
    
    
          fn_LED_ALL_OFF();
          __G_OUT__;
      }
      fn_Usart_SendString(_DEBUG_USARTx,":  ROM内存到RAM内存的DMA操作完成 \n");  
      //----------------------------------------------------------------
      //----------------------------------------------------------------
      fn_Usart_SendString(_DEBUG_USARTx,": ROM内存到USART外设的DMA操作开始 \n");
      for (i=0; i<USART_BUFFER_SIZE; i++){
    
      //对内存初始化
        USART_DMA_Buffer[i] = 'F'; 
      }
      // 开始 USART内存到RAM内存的DMA操作 
      fn_Usart_SendString(_DEBUG_USARTx,": ROM内存初始化开始 \n");
      _USART_DMA_Config_;
      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 或者关闭请求*/
    
      while(1){
    
    
      fn_Systick_Delay(50,_Systick_ms);   //DMA 传输进程中进行LED输出闪烁
      __LED_Change__;
        if(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)!=RESET){
    
    
          fn_Usart_SendString(_DEBUG_USARTx,": ROM内存到USART外设的DMA操作完毕 \n");  //这个函数需要Delay 一段时间才可以用
          break;
        }
      }
      while(1);
}


void fn_LED_Flash_Init(void){
    
    
  uint16_t  count_Init = 2;
  
  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(100,_Systick_ms);
    fn_LED_ALL_OFF();
    __B_OUT__;
    fn_Systick_Delay(100,_Systick_ms);
    fn_LED_ALL_OFF();
    __R_OUT__;
  } 
}

void fn_usart_show_Init(void){
    
     
  fn_Usart_Send_Byte(_DEBUG_USARTx,'T');
  fn_Usart_Send_Byte(_DEBUG_USARTx,'O');
  Usart_SendHalf_32_Word(_DEBUG_USARTx,0xA1B2C3D4);
  fn_Usart_SendString(_DEBUG_USARTx," : wangqi \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****/

www.firebbs.cn。


参考笔记。
_STM32f103 中断 以及 EXT

STM32 EXIT – LED 程序

STM32 EXIT – EXIT 程序

STM32 EXIT – Key 程序

STM32 RCC – 主程序

总结

  1. 要传多少,单位是什么
当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多
少,数据的单位是什么。
以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由
DMA_CNDTR 配置,这是一个 32 位的寄存器,一次最多只能传输 65535 个数据。
要想数据传输正确,源和目标地址存储的数据宽度还必须一致,串口数据寄存器是 8
位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由 DMA_CCR 的
PSIZE[1:0]配置,可以是 8/16/32 位,存储器的数据宽度由 DMA_CCR 的 MSIZE[1:0]配置,
可以是 8/16/32 位。
在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必
须正确设置两边数据指针的增量模式。外设的地址指针由 DMA_CCRx 的 PINC 配置,存储
器的地址指针由 MINC 配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完
一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址
指针就固定不变。具体的数据指针的增量模式由实际情况决定。
  1. 什么时候传输完成
数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个
DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该
类型的中断后,则会产生中断。有关各个标志位的详细描述请参考 DMA 中断状态寄存器
DMA_ISR 的详细描述。
传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一
次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循
环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的
由 DMA_CCR 寄存器的 CIRC 循环模式位控制。

DMA_ InitTypeDef 初始化结构体

typedef struct{
    
    
	  uint32_t DMA_PeripheralBaseAddr; // 外设地址
	  uint32_t DMA_MemoryBaseAddr; // 存储器地址
	  uint32_t DMA_DIR; // 传输方向
	  uint32_t DMA_BufferSize; // 传输数目
	  uint32_t DMA_PeripheralInc; // 外设地址增量模式
	  uint32_t DMA_MemoryInc; // 存储器地址增量模式
	  uint32_t DMA_PeripheralDataSize; // 外设数据宽度
	  uint32_t DMA_MemoryDataSize; // 存储器数据宽度
	  uint32_t DMA_Mode; // 模式选择
	  uint32_t DMA_Priority; // 通道优先级
	  uint32_t DMA_M2M; // 存储器到存储器模式
} DMA_InitTypeDef;

1) DMA_PeripheralBaseAddr:外设地址,设定 DMA_CPAR 寄存器的值;一般设置
为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器
地址。
2) DMA_Memory0BaseAddr:存储器地址,设定 DMA_CMAR 寄存器值;一般设置
为我们自定义存储区的首地址。
3) DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设。它设定
DMA_CCR 寄存器的 DIR[1:0]位的值。这里并没有存储器到存储器的方向选择,
当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。
4) DMA_BufferSize:设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。
5) DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动
递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数
据寄存器,所以一般不会使能该位。
6) DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动
递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自定义的存储区一般
都是存放多个数据的,所以要使能存储器地址自动递增功能。
7) DMA_PeripheralDataSize:外设数据宽度,可选字节(8)、半字(16)和字(32),它设定 DMA_CCR 寄存器的 PSIZE[1:0]位的值。
8) DMA_MemoryDataSize:存储器数据宽度,可选字节(8)、半字(16)和字(32),它设定 DMA_CCR 寄存器的 MSIZE[1:0]位的值。当外设和存储器之间传数
据时,两边的数据宽度应该设置为一致大小。
9) DMA_Mode:DMA 传输模式选择,可选一次传输或者循环传输,它设定
DMA_CCR 寄存器的 CIRC 位的值。例程我们的 ADC 采集是持续循环进行的,所
以使用循环传输模式。
10) DMA_Priority:软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、
中和低,它设定 DMA_CCR 寄存器的 PL[1:0]位的值。DMA 通道优先级只有在多
个 DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。
11) DMA_M2M :存 储器 到存 储器 模式 ,使 用存储 器到 存储 器时 用到, 设定
DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。

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

猜你喜欢

转载自blog.csdn.net/u012651389/article/details/118038429