目录
前言
基于stm32f103zet6,将串口收发不定长数据和adc采集通过使用dma搬运数据,提高单片机cpu的运行效率,加深对dma和adc功能的学习。
一、DMA及各外设介绍
可以参考之前文章,这里只贴出相关代码。
【32单片机学习】(6)STM32串口+DMA收发不定长数据_dma 不定长数据的收发_Godox_user的博客-CSDN博客
二、实验现象
串口发送
LCD显示
三、代码部分
1.adc部分
adc.c
这里使用的是ADC1的通道16,stm32内部自带的温度传感器;
ADC3的通道6,在GPIOF_PIN_8上接的光敏传感器。
由于是ADC1和ADC3两个不同组ADC,所以要分开单独进行测量,当ADC1中有多个ADC设备时,可以打开连续扫描模式。
#include "adc.h"
#include "delay.h"
#include "serial.h"
uint16_t AD_Value1[1],AD_Value2[1];
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_InitTypeDef ADC_InitStructure1;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF | RCC_APB2Periph_ADC1| RCC_APB2Periph_ADC3, ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOF, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_DeInit(ADC3); //复位ADC1
ADC_TempSensorVrefintCmd(ENABLE); //开启内部温度传感器
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_InitStructure1.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure1.ADC_ScanConvMode = ENABLE; //模数转换工作在单通道模式
ADC_InitStructure1.ADC_ContinuousConvMode = ENABLE; //模数转换工作在单次转换模式
ADC_InitStructure1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure1.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure1.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC3, &ADC_InitStructure1); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_RegularChannelConfig(ADC1,ADC_Channel_16,1,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC3,ADC_Channel_6,1,ADC_SampleTime_55Cycles5);
DMA_Cmd(DMA1_Channel1,ENABLE);
DMA_Cmd(DMA2_Channel5,ENABLE);
ADC_Cmd(ADC1,ENABLE); //使能指定的ADC1
ADC_Cmd(ADC3,ENABLE); //使能指定的ADC1
ADC_DMACmd(ADC1,ENABLE);
ADC_DMACmd(ADC3,ENABLE);
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC3); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC3)); //等待复位校准结束
ADC_StartCalibration(ADC3); //开启AD校准
while(ADC_GetCalibrationStatus(ADC3)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC3, ENABLE);
}
short Get_Temprate(void) //获取内部温度传感器温度值
{
u32 adcx=0;
short result=0;
double temperate=0;
adcx=AD_Value1[0]; //读取通道16,20次取平均
//USART1_printf(" temp:%d\r\n",adcx);
temperate=(float)adcx*(3.3/4096); //电压值
temperate=(1.43-temperate)/0.0043+25; //转换为温度值
result=temperate*=100; //扩大100倍.
return result;
}
//读取Light Sens的值
//0~100:0,最暗;100,最亮
u8 Lsens_Get_Val(void)
{
u32 temp_val=0;
temp_val=AD_Value2[0];//得到平均值
//USART1_printf(" light:%d\r\n",temp_val);
if(temp_val>4000)temp_val=4000;
return (u8)(100-(temp_val/40));
}
adc.h
#ifndef __ADC_H__
#define __ADC_H__
#include "sys.h"
extern uint16_t AD_Value1[1],AD_Value2[1];
void Adc_Init(void);
uint16_t Get_ADValue1(u8 ch);
uint16_t Get_ADValue2(u8 ch);
short Get_Temprate(void);
u8 Lsens_Get_Val(void);
#endif
2.串口部分
串口部分使用了串口1,使用串口接收的空闲中断,利用双缓存区的方法对DMA接收到的数据进行转运,防止数据产生覆盖和缓存空间的溢出。通过可变参串口打印函数,使其可以收发不定长数据,将printf只能用于单串口的局限性消除。
serial.c
#include "serial.h"
uint8_t USART1_TX_BUF[USART1_MAX_TX_LEN]; //发送缓冲,最大USART1_MAX_TX_LEN字节
uint8_t u1rxbuf[USART1_MAX_RX_LEN]; //发送数据缓冲区1
uint8_t u2rxbuf[USART1_MAX_RX_LEN]; //发送数据缓冲区2
uint8_t witchbuf=0; //标记当前使用的是哪个缓冲区,0:使用u1rxbuf;1:使用u2rxbuf
uint8_t USART1_TX_FLAG; //USART1发送标志,启动发送时置1
uint8_t USART1_RX_FLAG; //USART1接收标志,启动接收时置1
void Serial_Init(u32 bound)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//开启串口1和外设GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//初始化串口的GPIO口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//发送TXD
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//接收RXD
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化串口结构体
USART_InitStructure.USART_BaudRate=bound;//串口波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode=USART_Mode_Tx| USART_Mode_Rx;//收发模式
USART_InitStructure.USART_Parity=USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_StopBits=USART_StopBits_1;//一个停止位
USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长为8位数据格式
USART_Init(USART1,&USART_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//DMA1通道5 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; //NVIC通道设置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
//DMA1通道4 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; //NVIC通道设置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//开启串口接收中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断
USART_ClearFlag(USART1,USART_FLAG_TC);//清除USART1标志位
USART_Cmd(USART1,ENABLE);//使能串口1
}
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Array[i]);
}
}
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch);
return ch;
}
//可变参串口打印函数
void USART1_printf(char *format, ...)
{
va_list arg_ptr; //实例化可变长参数列表
while(USART1_TX_FLAG); //等待上一次发送完成(USART1_TX_FLAG为1即还在发送数据)
va_start(arg_ptr, format); //初始化可变参数列表,设置format为可变长列表的起始点(第一个元素)
// USART1_MAX_TX_LEN+1可接受的最大字符数(非字节数,UNICODE一个字符两个字节), 防止产生数组越界
vsnprintf((char*)USART1_TX_BUF, USART1_MAX_TX_LEN+1, format, arg_ptr); //从USART1_TX_BUF的首地址开始拼合,拼合format内容;USART1_MAX_TX_LEN+1限制长度,防止产生数组越界
va_end(arg_ptr); //注意必须关闭
DMA_USART1_Tx_Data(USART1_TX_BUF,strlen((const char*)USART1_TX_BUF)); //发送USART1_TX_BUF内容
}
//串口通过DMA 发送数据
//buffer-->发送数据地址
//size-->发送的字节大小
void DMA_USART1_Tx_Data(u8 *buffer, u32 size)
{
while(USART1_TX_FLAG); //等待上一次发送完成(USART1_TX_FLAG为1即还在发送数据)
USART1_TX_FLAG=1; //USART1发送标志(启动发送)
DMA1_Channel4->CMAR = (uint32_t)buffer; //设置要发送的数据地址
DMA1_Channel4->CNDTR = size; //设置要发送的字节数目
DMA_Cmd(DMA1_Channel4, ENABLE); //开始DMA发送
}
//DMA1通道4中断,判断上次数据串口是否已经发送完成
void DMA1_Channel4_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC4)!= RESET) //DMA接收完成标志
{
DMA_ClearITPendingBit(DMA1_IT_TC4); //清除中断标志
USART_ClearFlag(USART1,USART_FLAG_TC); //清除串口1的标志位
DMA_Cmd(DMA1_Channel4, DISABLE ); //关闭USART1 TX DMA1 所指示的通道
USART1_TX_FLAG=0; //USART1发送标志(关闭)
}
}
//串口1中断函数
void USART1_IRQHandler(void)
{
uint8_t *p;
uint8_t USART1_RX_LEN = 0; //接收数据长度
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //串口1空闲中断
{
USART_ReceiveData(USART1); //清除串口1空闲中断IDLE标志位
USART_ClearFlag(USART1,USART_FLAG_TC); //清除USART1标志位
DMA_Cmd(DMA1_Channel5, DISABLE ); //关闭USART1 TX DMA1 所指示的通道
USART1_RX_LEN = USART1_MAX_RX_LEN - DMA1_Channel5->CNDTR; //获得接收到的字节数
if(witchbuf) //之前用的u1rxbuf,切换为u2rxbuf
{
p=u2rxbuf; //先保存前一次数据地址再切换缓冲区
DMA1_Channel5->CMAR=(u32)u1rxbuf; //切换为u2rxbuf缓冲区地址
witchbuf=0; //下一次切换为u1rxbuf
}
else //之前用的u1rxbuf,切换为u1rxbuf
{
p=u1rxbuf; //先保存前一次数据地址再切换缓冲区
DMA1_Channel5->CMAR=(u32)u2rxbuf; //切换为u1rxbuf缓冲区地址
witchbuf=1; //下一次切换为u1rxbuf
}
DMA1_Channel5->CNDTR = USART1_MAX_RX_LEN; //DMA通道的DMA缓存的大小
DMA_Cmd(DMA1_Channel5, ENABLE); //使能USART1 TX DMA1 所指示的通道
//******************数据处理******************//
DMA_USART1_Tx_Data(p,USART1_RX_LEN);
}
}
serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "sys.h"
#include <stdio.h>
#include <stdarg.h>
#define USART1_MAX_TX_LEN 256
#define USART1_MAX_RX_LEN 256
extern uint8_t USART1_TX_FLAG; //USART1发送标志,启动发送时置1
extern uint8_t USART1_RX_FLAG; //USART1接收标志,启动接收时置1
extern size_t strlen(const char *);
extern uint8_t USART1_TX_BUF[USART1_MAX_TX_LEN]; //发送缓冲,最大USART1_MAX_TX_LEN字节
extern uint8_t u1rxbuf[USART1_MAX_RX_LEN]; //发送数据缓冲区1
extern uint8_t u2rxbuf[USART1_MAX_RX_LEN]; //发送数据缓冲区2
void Serial_Init(u32 bound);
void USART1_printf(char *format, ...);
void DMA_USART1_Tx_Data(u8 *buffer, u32 size);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
uint32_t Serial_Pow(uint32_t X, uint32_t Y);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
int fputc(int ch, FILE *f);
#endif
3.DMA配置
dma部分用到DMA1通道1、4、5分别作内部温度、串口发送、串口接收的数据转运;DMA2通道5用于光敏传感器数据转运,转运ADC是将数据从ADC的数据寄存器(ADCX->DR)转运到SRAM的数组AD_Value的数组中,转运串口是将接收的数据从串口的数据寄存器(USARTX->DR)转运到USARTX_TX_BUF的数据接收缓冲区中。
dma.c
#include "dma.h"
#include "adc.h"
void MyDMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_InitTypeDef DMA_InitStructure1;
DMA_InitTypeDef DMA_InitStructure2;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
DMA_DeInit(DMA1_Channel1);//内部温度传感器
DMA_InitStructure1.DMA_MemoryBaseAddr=(uint32_t)AD_Value1;
DMA_InitStructure1.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
DMA_InitStructure1.DMA_MemoryInc=DMA_MemoryInc_Disable;
DMA_InitStructure1.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR;
DMA_InitStructure1.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure1.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure1.DMA_BufferSize=1;
DMA_InitStructure1.DMA_DIR=DMA_DIR_PeripheralSRC;
DMA_InitStructure1.DMA_M2M=DMA_M2M_Disable;
DMA_InitStructure1.DMA_Mode=DMA_Mode_Circular;
DMA_InitStructure1.DMA_Priority=DMA_Priority_High;
DMA_Init(DMA1_Channel1,&DMA_InitStructure1);
DMA_DeInit(DMA2_Channel5);//PF8
DMA_InitStructure2.DMA_MemoryBaseAddr=(uint32_t)AD_Value2;
DMA_InitStructure2.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
DMA_InitStructure2.DMA_MemoryInc=DMA_MemoryInc_Disable;
DMA_InitStructure2.DMA_PeripheralBaseAddr=(uint32_t)&ADC3->DR;
DMA_InitStructure2.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure2.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure2.DMA_BufferSize=1;
DMA_InitStructure2.DMA_DIR=DMA_DIR_PeripheralSRC;
DMA_InitStructure2.DMA_M2M=DMA_M2M_Disable;
DMA_InitStructure2.DMA_Mode=DMA_Mode_Circular;
DMA_InitStructure2.DMA_Priority=DMA_Priority_High;
DMA_Init(DMA2_Channel5,&DMA_InitStructure2);
DMA_DeInit(DMA1_Channel4); //tx 发送数据
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_TX_BUF;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = USART1_MAX_TX_LEN;
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_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)u1rxbuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = USART1_MAX_RX_LEN;
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_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
DMA_Init(DMA1_Channel5,&DMA_InitStructure);
DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE); //开USART2 Rx DMA中断
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); //开USART2 Tx DMA中断
DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE); //开USART2 Rx DMA中断
DMA_ITConfig(DMA2_Channel5,DMA_IT_TC,ENABLE); //开USART2 Tx DMA中断
DMA_Cmd(DMA1_Channel5,ENABLE); //使DMA通道5停止工作
DMA_Cmd(DMA1_Channel4,DISABLE); //使DMA通道4停止工作
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //开启串口DMA发送
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //开启串口DMA接收
}
dma.h
#ifndef __DMA_H
#define __DMA_H
#include "sys.h"
#include "serial.h"
void MyDMA_Init(void);
#endif
4.main函数
main.c
#include "delay.h"
#include "lcd.h"
#include "dma.h"
#include "serial.h"
#include "adc.h"
int main(void)
{
short temp=0;
u8 adcx;
delay_init();
Serial_Init(115200);
MyDMA_Init();
LCD_Init();
Adc_Init();
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(30,140,200,16,16,"TEMPERATE: 00.00C");
LCD_ShowString(30,156,200,16,16,"LSENS_VAL:");
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
ADC_SoftwareStartConvCmd(ADC3, ENABLE);
while(1)
{
temp=Get_Temprate(); //得到温度值
if(temp<0)
{
temp=-temp;
LCD_ShowString(30+10*8,140,16,16,16,"-"); //显示负号
}else LCD_ShowString(30+10*8,140,16,16,16," "); //无符号
LCD_ShowxNum(30+11*8,140,temp/100,2,16,0); //显示整数部分
LCD_ShowxNum(30+14*8,140,temp%100,2,16, 0X80); //显示小数部分
USART1_printf(" TEMPERATE:%d.%d\r\n",temp/100,temp%100);
adcx=Lsens_Get_Val();
LCD_ShowxNum(30+10*8,156,adcx,3,16,0);//显示ADC的值
USART1_printf(" LSENS_VAL:%d\r\n",adcx);
delay_ms(250);
}
}
总结
以上就是今天要讲的内容,本文是笔者在stm32学习中将adc采集和串口外设接收的数据通过DMA进行转运,进一步理解DMA的配置和转运数据的机制,加深对个外设的理解