这一篇可和AD那篇一起stm32外部AD——ADC08060 驱动_居安士的博客-CSDN博客
搜索stm32代码大部分的DMA配置都和内部AD相连,没有单独实现串口DMA的,对于外部AD或者其他应用需要单独启用串口DMA的应用,本篇或许可以给大家以参考
当我们计算的串口波特率 无法满足数据传输速度的时候,我们需要将串口设置为DMA发送(不占用CPU资源),但是这样也不够,这个时候我们需要采用抽帧的方法进行发送,接下来会依次进行详解。
目录
串口DMA配置方法
DMA串口发送
在配置串口DMA之前,我们要先看一下自己的串口是usart1还是usart2
不同的usart对应着不同的DMA,以及不同的数据流
usart2对应着DMA1通道4的数据流5和数据流6
usart1对应着DMA2通道4的数据流5和数据流7
为了方便大家,我把需要修改的地方备注出来(备注乱码忽略),大家直接对应修改即可!!赞赞
下面是usart2的
//-----------------DMA1配置-------------------------------------
void uart2_init(int bps)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //修改
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//修改
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2¸修改
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3¸修改
//USART2¶Ë¿ÚÅäÖÃ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //修改
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //¸´Óù¦ÄÜ
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //ËÙ¶È50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //ÍÆÍ츴ÓÃÊä³ö
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //ÉÏÀ
GPIO_Init(GPIOA,&GPIO_InitStructure); //修改
//USART2³õʼ»¯ÉèÖÃ
USART_InitStructure.USART_BaudRate =bps; //²¨ÌØÂÊÉèÖÃ
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //×Ö³¤Îª8λÊý¾Ý¸ñʽ
USART_InitStructure.USART_StopBits = USART_StopBits_1; //Ò»¸öֹͣλ
USART_InitStructure.USART_Parity = USART_Parity_No;//ÎÞÆæżУÑéλ
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_CTS; //ÎÞÓ²¼þÊý¾ÝÁ÷¿ØÖÆ
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //ÊÕ·¢Ä£Ê½
USART_Init(USART2, &USART_InitStructure); //³õʼ»¯´®¿Ú2
USART_Cmd(USART2, ENABLE); //修改
USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
USART_ClearFlag(USART2, USART_FLAG_TC);
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE); //修改
//Usart2 NVIC ÅäÖÃ
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÉèÖÃϵͳÖжÏÓÅÏȼ¶·Ö×é0
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//´®¿Ú1ÖжÏͨµÀ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//ÇÀÕ¼ÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //×ÓÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQͨµÀʹÄÜ
NVIC_Init(&NVIC_InitStructure); //¸ù¾ÝÖ¸¶¨µÄ²ÎÊý³õʼ»¯VIC¼Ä´æÆ÷¡¢
}
void usart2_dma_Config(uint32_t * usart2_dma_buffer, int byteLen)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //修改
DMA_DeInit(DMA1_Stream6);
while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){} //µÈ´ýDMA¿ÉÅäÖÃ
/* ÅäÖÃ DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //ͨµÀÑ¡Ôñ
DMA_InitStructure.DMA_PeripheralBaseAddr =((u32)&USART2->DR); //DMAÍâÉèµØÖ·
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)usart2_dma_buffer; //DMA ´æ´¢Æ÷0µØÖ·
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //´æ´¢Æ÷µ½ÍâÉèģʽ
DMA_InitStructure.DMA_BufferSize =byteLen; //Êý¾Ý´«ÊäÁ¿
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //ÍâÉè·ÇÔöÁ¿Ä£Ê½
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //´æ´¢Æ÷ÔöÁ¿Ä£Ê½
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //ÍâÉèÊý¾Ý³¤¶È:8λ
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //´æ´¢Æ÷Êý¾Ý³¤¶È:8λ
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //ʹÓÃÆÕͨģʽ
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //ÖеÈÓÅÏȼ¶
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //´æ´¢Æ÷Í»·¢µ¥´Î´«Êä
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //ÍâÉèÍ»·¢µ¥´Î´«Êä
DMA_Init(DMA1_Stream6, &DMA_InitStructure); //修改
DMA_Cmd(DMA1_Stream6, ENABLE);
}
void usart2_dma_ConfigHalfWord(uint16_t * usart2_dma_buffer, int byteLen)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //修改
DMA_DeInit(DMA1_Stream6);
while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){} //修改
/* ÅäÖÃ DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //ͨµÀÑ¡Ôñ
DMA_InitStructure.DMA_PeripheralBaseAddr =((u32)&USART2->DR); //DMAÍâÉèµØÖ·
DMA_InitStructure.DMA_Memory0BaseAddr = (u32 )usart2_dma_buffer; //DMA ´æ´¢Æ÷0µØÖ·
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //´æ´¢Æ÷µ½ÍâÉèģʽ
DMA_InitStructure.DMA_BufferSize =byteLen; //Êý¾Ý´«ÊäÁ¿
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //ÍâÉè·ÇÔöÁ¿Ä£Ê½
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //´æ´¢Æ÷ÔöÁ¿Ä£Ê½
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //ÍâÉèÊý¾Ý³¤¶È:8λ
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //´æ´¢Æ÷Êý¾Ý³¤¶È:8λ
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //ʹÓÃÆÕͨģʽ
DMA_InitStructure.DMA_Priority =DMA_Priority_Medium; //ÖеÈÓÅÏȼ¶
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //´æ´¢Æ÷Í»·¢µ¥´Î´«Êä
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //ÍâÉèÍ»·¢µ¥´Î´«Êä
DMA_Init(DMA1_Stream6, &DMA_InitStructure); //修改
DMA_Cmd(DMA1_Stream6, ENABLE);
}
void usart2_dma_ConfigByte(uint8_t * usart2_dma_buffer, int byteLen)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //修改
DMA_DeInit(DMA1_Stream6);
while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){} //修改
/* ÅäÖÃ DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //ͨµÀÑ¡Ôñ
DMA_InitStructure.DMA_PeripheralBaseAddr =((u32)&USART2->DR); //修改
DMA_InitStructure.DMA_Memory0BaseAddr = (u32 )usart2_dma_buffer; //修改
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //´æ´¢Æ÷µ½ÍâÉèģʽ
DMA_InitStructure.DMA_BufferSize =byteLen; //Êý¾Ý´«ÊäÁ¿
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //ÍâÉè·ÇÔöÁ¿Ä£Ê½
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //´æ´¢Æ÷ÔöÁ¿Ä£Ê½
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //ÍâÉèÊý¾Ý³¤¶È:8λ
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //´æ´¢Æ÷Êý¾Ý³¤¶È:8λ
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //ʹÓÃÆÕͨģʽ
DMA_InitStructure.DMA_Priority =DMA_Priority_Medium; //ÖеÈÓÅÏȼ¶
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //´æ´¢Æ÷Í»·¢µ¥´Î´«Êä
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //ÍâÉèÍ»·¢µ¥´Î´«Êä
DMA_Init(DMA1_Stream6, &DMA_InitStructure); //修改
//DMA_SetCurrDataCounter(DMA1_Stream6, byteLen);
DMA_Cmd(DMA1_Stream6, ENABLE);
我们除了需要修改串口号,IO口号,还要注意修改一个是数据流(RX和TX要注意区分,我这里是TX) 另一个是DMA内存号
.h文件别忘了加:
#include "sys.h"
#include
void uart2_init(int bps);
void usart2_dma_Config(uint32_t * usart2_dma_buffer, int byteLen);
void usart2_dma_ConfigHalfWord(uint16_t * usart2_dma_buffer, int byteLen);
void usart2_dma_ConfigByte(uint8_t * usart2_dma_buffer, int byteLen);
我们在配置里面,已经将串口DMA发送的代码写过了,就是函数:usart2_dma_ConfigByte(uint8_t * usart2_dma_buffer, int byteLen)
我们是不可以把AD输出的input直接用串口输出的,这样会掉帧,所以所有的数据我们都先放进一个缓冲区,再从缓冲区里面读取。
之后我们只需要调用函数 usart2_dma_ConfigByte(数据缓冲区名称,一次传输的数据个数);
当我们这样写之后,会发现依然漏掉数据,这是因为仅仅串口DMA发送,只是不占用CPU资源,但是速度上不会有很大提高,我们需要使用“抽帧”方法 :
即计数存多幅图的图像,之后只输出一幅图图像,保留足够的时间给串口进行传输
在这里我们需要设计两个计数器:一个是像素计数器(计数输出一幅图的所有像素个数);另一个是帧计数器(计数输出图的个数)
假如我一幅图是552个像素,那么像素计数器=552的时候,帧计数器应该+1
再利用AD的上升沿作为像素计数器加一的标志
总结如下:每来一个AD上升沿,我们进行一次中断,将AD输出数据input存一次缓冲区,像素计数器+1;当像素计数器=552的时候,帧计数器+1,当帧计数器=n时(n为多少帧发一次,n越大越慢,可以进行几次尝试,选择最小的n值),启动串口DMA发送
void TIM4_IRQHandler(void)
{
int i=0;
uint8_t frameBufCopy[1024];//缓冲区
if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) //中断
{
frameBuf[pixleInd]=input;
pixleInd++; //像素计数器
if(pixleInd>=552)
{
pixleInd=0;
frameCount++;//帧计数器
}
if(frameCount>=100)
{
frameCount=0;
memcpy(frameBufCopy,frameBuf,552);
usart2_dma_ConfigByte(frameBufCopy,552);//串口发送
}
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //
//}
}
我们要注意以下几点
(1)DMA发送的缓冲区和计数器需要定义成全局变量
extern uint8_t frameBuf[1024];
extern u16 pixleInd;
extern u16 frameCount;
(2)串口DMA发送前拷贝数据
在串口发送的时候,AD还在不断覆盖缓冲区,需要把将要发送的数据拷贝出来,放进一个单独的缓冲区,在这里,使用了memcpy函数
void *memcpy(void*dest, const void *src, size_t n);
它用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;
它由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。
我这里的数据是用眼睛去看,由于视觉残留,少图没关系,如果大家对应数据完整性要求特别高, 而数据本身又是M级别的速率,就不要使用串口,应该使用网口