DMA
步骤一:配置系统时钟
在配置DMA之前,首先需要配置系统时钟,确保所有外设都能正常工作。以下是一个简单的时钟配置示例,假设使用的是STM32F103芯片。
#include "stm32f10x.h"
void RCC_Config(void) {
// 配置系统时钟为72MHz,使用外部晶振HSE为8MHz
RCC_DeInit(); // 复位RCC寄存器
RCC_HSEConfig(RCC_HSE_ON); // 启动外部晶振
RCC_WaitForHSEStartUp(); // 等待外部晶振启动
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // 配置PLL时钟为8MHz * 9 = 72MHz
RCC_PLLCmd(ENABLE); // 启动PLL
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // 等待PLL稳定
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // 将PLL时钟设置为系统时钟
while (RCC_GetSYSCLKSource() != 0x08); // 等待PLL成为系统时钟
RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB时钟 = 系统时钟
RCC_PCLK2Config(RCC_HCLK_Div1); // APB2时钟 = 系统时钟
RCC_PCLK1Config(RCC_HCLK_Div2); // APB1时钟 = 系统时钟 / 2
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
}
步骤二:配置DMA控制器6void DMA_Config(void) {
DMA_InitTypeDef DMA_InitStructure;
// 使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// DMA初始化
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); // 外设地址,比如这里使用USART1的数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer; // 内存地址,比如buffer数组的地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 数据传输方向:外设到内存
DMA_InitStructure.DMA_BufferSize = sizeof(buffer); // 数据大小,比如buffer数组的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 禁止外设地址自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 允许内存地址自增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 内存数据大小为字节 半字 字 1 2 4
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据大小为字节 半字 字 1 2 4
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA传输模式:单次模式:传输计数器计到0(字节)时结束,循环模式计数到0自动重装
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁止内存到内存模式DMA_M2M_Disable:禁用内存到内存模式。这意味着DMA传输只能在外设和内存之间进行,而不能直接在两个内存区域之间传输数据。
//DMA_M2M_Enable:启用内存到内存模式。这时DMA可以直接在两个内存区域之间传输数据,而不需要通过外设。这种模式适用于需要在内存之间进行数据复制或传输的场景。
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 使能DMA通道
DMA_Cmd(DMA1_Channel1, ENABLE);
//DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);
}
步骤三:初始化外设
void USART_Config(void) {
USART_InitTypeDef USART_InitStructure;
// 使能USART时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// USART初始化
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
// 使能USART
USART_Cmd(USART1, ENABLE);
//使能外设DMA
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
}
步骤四:启动DMA传输
void Start_DMA_Transfer(void) {
// 清除标志位
DMA_ClearFlag(DMA1_FLAG_TC1);
// 启动DMA传输
DMA_Cmd(DMA1_Channel1, ENABLE);
}
步骤五:处理DMA传输完成中断(如果需要)
如果配置了DMA传输完成中断,在中断处理函数中进行相应的处理。
void DMA1_Channel1_IRQHandler(void) {
if (DMA_GetITStatus(DMA1_IT_TC1)) {
// DMA传输完成中断处理
// 清除中断标志
DMA_ClearITPendingBit(DMA1_IT_TC1);
}
}
//若不用中断
void DMAx Enable(DMA Channel TypeDef* DMAy channelx,u16 ndtr)
{
DMA Cmd(DMAy Channelx,DISABLE);
DMA SetCurrDataCounter(DMAy Channelx,ndtr);
DMA Cmd(DMAy Channelx,DISABLE):
while(DMA_GetFlagStatus(DMA1_FlAG_TC1)==RESET);
DMA_ClearFlag(DMA1_FlAG_TC1);
}
是否完成传输
传输数据剩余量
设置传输数据剩余量
按键双击和长按
#define KEYA_SPEED1 100 //长按的时间长度(单位10mS)
#define KEYA_SPEED2 10 //双击的时间长度(单位20mS)
int main (void){
//主程序
u8 a=0,b,c=0;
RCC_Configuration(); //系统时钟初始化
LED_Init();//LED初始化
TOUCH_KEY_Init();//按键初始化
while(1){
if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
//检测按键是否按下
delay_ms(20); //延时去抖动
if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
//判断长短键
while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&c<KEYA_SPEED1){
//循环判断长按,到时跳转
c++;delay_ms(10); //长按判断的计时
}
if(c>=KEYA_SPEED1){
//长键处理
//长按后执行的程序放到此处
GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
}else{
//单击处理
for(b=0;b<KEYA_SPEED2;b++){
//检测双击
delay_ms(20);
if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
a=1;
//双击后执行的程序放到此处
GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
}
}
if(a==0){
//判断单击
//单击后执行的程序放到此处
GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
}
}
a=0;c=0; //参数清0
}
} //按键判断在此结束
}
}
触摸按键滑动程序
#define KEYA_SPEED1 100 //长按的时间长度(单位10mS)
#define KEYA_SPEED2 10 //双击的时间长度(单位20mS)
int main (void){
//主程序
u16 k=1000; //用于滑动加减计数
u8 a=0,b,c=0;
u8 s=0; //刚刚结束滑动标志
RCC_Configuration(); //系统时钟初始化
USART1_Init(115200); //串口初始化,参数中写波特率
LED_Init();//LED初始化
TOUCH_KEY_Init();//按键初始化
while(1){
//A
if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
//检测按键是否按下
delay_ms(20); //延时去抖动
if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
//判断长短键
while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&c<KEYA_SPEED1){
//循环判断长按,到时跳转
c++;delay_ms(10); //长按判断的计时
}
if</