記錄一下學習過程
DMA
DMA,全稱為: Direct Memory Access,即直接存儲器訪問, DMA 傳輸將數(shù)據(jù)從一個
地址空間復制到另外一個地址空間。 這一過程無需cpu的參與,從而提高cpu使用的效率
DMA相關的參數(shù):1 數(shù)據(jù)的源地址、2 數(shù)據(jù)傳輸?shù)哪繕说刂?、3 傳輸寬度,4 傳輸多少字節(jié),5 傳輸模式。
傳輸寬度是指一次傳輸數(shù)據(jù)的的大小,可以為字節(jié)(8b)、半字(16b)、字(32b)
傳輸模式分為正常模式(一次結(jié)束)和循環(huán)模式
DMA通道
STM32 最多有 2 個 DMA 控制器(DMA2 僅存在大容量產(chǎn)品中), DMA1 有 7 個通道。 DMA2 有 5
個通道。每個通道專門用來管理來自于一個或多個外設對存儲器訪問的請求。還有一個仲裁起來協(xié)調(diào)各個 DMA 請求的優(yōu)先權(quán)。每個通道都直接連接專用的硬件 DMA 請求,每個通道都同樣支持軟件觸發(fā)。這些功能通過軟件來配置。
如圖

DMA傳輸方式
外設到內(nèi)存
內(nèi)存到內(nèi)存(配置為內(nèi)存到內(nèi)存時,DMA的模式只能選用正常模式)
內(nèi)存到外設
DMA配置的代碼以串口為例
#include "MyDMA.h"
u16 DMA1_MEM_LEN;//保存DMA每次數(shù)據(jù)傳送的長度
//DMA1的各通道配置
//這里的傳輸形式是固定的,這點要根據(jù)不同的情況來修改
//從存儲器->外設模式/8位數(shù)據(jù)寬度/存儲器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外設地址
//cmar:存儲器地址
//cndtr:數(shù)據(jù)傳輸量
void MyDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
DMA_InitTypeDef DMA_InitStructure;
DMA1_MEM_LEN=cndtr;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA傳輸
DMA_DeInit(DMA_CHx); //將DMA的通道1寄存器重設為缺省值
DMA_InitStructure.DMA_BufferSize=cndtr;//DMA通道的DMA緩存的大?。ㄞD(zhuǎn)運的數(shù)據(jù)量)
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//數(shù)據(jù)傳輸方向,從外設讀取發(fā)送到內(nèi)存
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//DMA通道x沒有設置為內(nèi)存到內(nèi)存?zhèn)鬏? DMA_InitStructure.DMA_MemoryBaseAddr=cmar;//DMA內(nèi)存基地址
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//數(shù)據(jù)寬度為8位
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//內(nèi)存地址寄存器遞增
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//工作在正常模式
DMA_InitStructure.DMA_PeripheralBaseAddr=cpar;//DMA外設基地址
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//數(shù)據(jù)寬度為8位
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外設地址寄存器不變
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //DMA通道 x擁有中優(yōu)先級
DMA_Init(DMA_CHx,&DMA_InitStructure);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);//使能外設的DMA通道,這句可以放在對應的外設里
}
//開啟一次DMA傳輸
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE ); //關閉所指示的通道
DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//重新設定DMA通道的DMA緩存的大小
DMA_Cmd(DMA_CHx, ENABLE); //使能所指示的通道
}
#ifndef __MYDMA_H
#define __MYDMA_H
#include "sys.h"
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);
void MyDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);
#endif
DMA+串口空閑中斷實現(xiàn)不定長收發(fā)
現(xiàn)在上面的DMA頭文件部分定義接受緩存區(qū)最長長度
#ifndef __MYDMA_H
#define __MYDMA_H
#include "sys.h"
#define rx_buff_maxlen 200//定義接受緩存區(qū)最長長度
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);
void MyDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);
#endif
串口部分的配置
#include "MyUSART.h"
u8 len,Flag=0;
void MyUSART_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure; //定義GPIO初始化結(jié)構(gòu)體
USART_InitTypeDef USART_InitStructure; //定義USART初始化結(jié)構(gòu)體
NVIC_InitTypeDef NVIC_InitStructure; //定義NVIC初始化結(jié)構(gòu)體
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //開啟GPIOA的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //開啟USART1的時鐘
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //配置為復用推挽輸出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //PA9為TX
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIO初始化
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //PA10為RX
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitStructure.USART_BaudRate=bound; //設置串口的波特率
USART_InitStructure.USART_WordLength=USART_WordLength_8b; //設置字長為8位數(shù)據(jù)格式
USART_InitStructure.USART_Mode=USART_Mode_Tx | USART_Mode_Rx; //設置串口模式為發(fā)送和接受
USART_InitStructure.USART_StopBits=USART_StopBits_1; //設置停止位為1位
USART_InitStructure.USART_Parity=USART_Parity_No; //無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; // 無硬件流控制
USART_Init(USART1,&USART_InitStructure); //初始化USART1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷分組這個最好放在main函數(shù)里,確保所有中斷都是采用同一分組
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//設置中斷來源
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //IRQ中斷使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //設置搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3; //設置響應優(yōu)先級
NVIC_Init(&NVIC_InitStructure); //初始化NVIC
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //開啟USART1的空閑中斷
USART_Cmd(USART1,ENABLE); //使能USART1
}
void MyUSART_SendByte(u8 Byte)
{
USART_SendData(USART1,Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//防止發(fā)送過快,前一個數(shù)據(jù)還沒發(fā)送出去就別覆蓋的情況
}
void MyUSART_SendArray(u8* Array,u16 Length)//發(fā)送數(shù)組,最長長度為2^16
{
u16 i;
for(i=0;i<Length;i++)
{
MyUSART_SendByte(Array[i]);
}
}
void MyUSART_SendString(char* str)//發(fā)送字符串
{
u16 i;
for(i=0;str[i]!='\0';i++)
{
MyUSART_SendByte(str[i]);
}
}
void USART1_IRQHandler(void) //串口1中斷服務程序,當接受完畢后便會觸發(fā)空閑中斷
{
u8 clear;
if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET) //接收中斷
{
clear=USART1->DR;//清楚中斷標志位
Flag=1;//標志一次接受完畢,在main函數(shù)中讀取flag來判斷是否接收完畢,并在主函數(shù)中清零
len=DMA_GetCurrDataCounter(DMA1_Channel5)-sizeof(rx_buff);//讀取剩余未轉(zhuǎn)運的長度
}
}
頭文件部分文章來源:http://www.zghlxwxcb.cn/news/detail-634003.html
#ifndef __MYUSART_H
#define __MYUSART_H
#include "sys.h"
#include "OLED.h"
#include "Delay.h"
#include "MyDMA.h"
extern u8 len;
extern u8 Flag;
extern char rx_buff[200];
void MyUSART_Init(u32 bound);
extern u8 ReceiveData ;
void MyUSART_SendByte(u8 Byte);
void MyUSART_SendArray(u8* Array,u16 Length);
void MyUSART_SendString(char* str);
#endif
main函數(shù)部分文章來源地址http://www.zghlxwxcb.cn/news/detail-634003.html
#include "MyUSART.h" // Device header
int main()
{
char rx_buff[200]={'\0'};
MyUSART_Init(115200);
OLED_Init();
MyDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)rx_buff,rx_buff_maxlen);
MYDMA_Enable(DMA1_Channel5);
while(1)
{
if(Flag)
{
Flag=0;
MyUSART_SendString(rx_buff);
MyUSART_SendString("\r\n");
OLED_ShowNum(1,1,len,3);
MYDMA_Enable(DMA1_Channel5);
}
}
}
到了這里,關于STM32學習筆記(五)串口空閑中斷+DMA實現(xiàn)不定長收發(fā)(stm32c8t6)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!