本文展示了STM32 DMA程序串口的
内容涉及 :
DMA 内存传输 DMA外设内存传输
USART串口的识别
IO口输入输出
按键的外部中断处理
32位数据通讯,字符串通讯,单字符通讯
完整代码
文章目录
- 前言
- 一、 编程要点
- 二、使用步骤
-
- 1.理解原理图
- 2.建立DMA传输的 头文件 DMA_book.h
- 3.建立DMA传输的 头文件 DMA_book.c
- 4.建立USART串口的 头文件 USART_book.h
- 5.建立USART串口的 头文件 USART_book.c
- 6.利用之前的Systick 定时输出的 头文件 Systick_book.h
- 7.利用之前的Systick 输出的 头文件 Systick_book.c
- 8.利用之前的RCC_book输出的 头文件 RCC_book.h
- 9.利用之前的RCC_book输出的 头文件 RCC_book.c
- 10.利用之前的LED输出的 头文件 Led_book.h
- 11.利用之前的LED输出的 头文件 Led_book.c
- 12.利用Key输出的程序 Key_book.h
- 13.利用Key输出的程序 Key_book.c
- 14.利用复习Exit中断的程序 Exit_book.h
- 15.利用复习Exit中断的程序 Exit_book.c
- 16.建立USART 输出的 主程序 main.c
- 总结
前言
STM32 的 DMA简介 DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是 DMA2 只存 在于大容量的单片机中。DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储 器到存储器。
一、 编程要点
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 – 主程序
总结
- 要传多少,单位是什么
当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多
少,数据的单位是什么。
以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由
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,而串口数据寄存器只有一个,那么外设的地址
指针就固定不变。具体的数据指针的增量模式由实际情况决定。
- 什么时候传输完成
数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个
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 即可启动存储器到存储器模式。