搜索
bottom↓
回复: 56

九九的STM32笔记(四)DMA、USART的演示

[复制链接]

出0入0汤圆

发表于 2009-2-18 19:30:35 | 显示全部楼层 |阅读模式
这里有个小小的例子,来演示DMA模块与系统程序并行工作。
  用串口以低波特率发送一个10K的数据,花费近10s时间,此时按照以往方法,CPU要不断等待数据发送、送数据;或者送数据、进中断、送数据,处理起来比较消耗时间。
  使用了DMA功能以后,用户程序中只需配置好DMA,开启传输后,再也不需要操心,10K数据完成后会有标志位或中断产生,期间可以做任何想做的事,非常方便。
  这个是相应的代码例子,基于STM32F103VBT6


/******************************************************************************
* 本文件实现串口发送功能(通过重构putchar函数,调用printf;或者USART_SendData()
* 这里是一个用串口实现大量数据传输的例子,使用了DMA模块进行内存到USART的传输
* 每当USART的发送缓冲区空时,USART模块产生一个DMA事件,
* 此时DMA模块响应该事件,自动从预先定义好的发送缓冲区中拿出下一个字节送给USART
* 整个过程无需用户程序干预,用户只需启动DMA传输传输即可
* 在仿真器调试时,可以在数据传输过程中暂停运行,此时DMA模块并没有停止
* 串口依然发送,表明DMA传输是一个独立的过程。
* 同时开启接收中断,在串口中断中将数据存入缓冲区,在main主循环中处理
* 作者:jjldc(九九)
* 代码硬件基于万利199元的EK-STM32F开发板,CPU=STM32F103VBT6
*******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_lib.h"
#include "stdio.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define USART1_DR_Base  0x40013804

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#define SENDBUFF_SIZE   10240
vu8 SendBuff[SENDBUFF_SIZE];
vu8 RecvBuff[10];
vu8 recv_ptr;

/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void DMA_Configuration(void);
void USART1_Configuration(void);

int fputc(int ch, FILE *f);
void Delay(void);

/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
int main(void)
{
    u16 i;
#ifdef DEBUG
    debug();
#endif
    recv_ptr = 0;
   
    RCC_Configuration();
    GPIO_Configuration();
    NVIC_Configuration();
    DMA_Configuration();
    USART1_Configuration();
   
    printf("\r\nSystem Start...\r\n");
    printf("Initialling SendBuff... \r\n");
    for(i=0;i<SENDBUFF_SIZE;i++)
    {
        SendBuff = i&0xff;
    }
    printf("Initial success!\r\nWaiting for transmission...\r\n");
    //发送去数据已经准备好,按下按键即开始传输
    while(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3));
   
    printf("Start DMA transmission!\r\n");
   
    //这里是开始DMA传输前的一些准备工作,将USART1模块设置成DMA方式工作
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    //开始一次DMA传输!
    DMA_Cmd(DMA1_Channel4, ENABLE);
   
    //等待DMA传输完成,此时我们来做另外一些事,点灯
    //实际应用中,传输数据期间,可以执行另外的任务
    while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET)
    {
        LED_1_REV;      //LED翻转
        Delay();        //浪费时间
    }
    //DMA传输结束后,自动关闭了DMA通道,而无需手动关闭
    //下面的语句被注释
    //DMA_Cmd(DMA1_Channel4, DISABLE);
   
    printf("\r\nDMA transmission successful!\r\n");

   
    /* Infinite loop */
    while (1)
    {
    }
}

/*******************************************************************************
* Function Name  : 重定义系统putchar函数int fputc(int ch, FILE *f)
* Description    : 串口发一个字节
* Input          : int ch, FILE *f
* Output         :
* Return         : int ch
* 这个是使用printf的关键
*******************************************************************************/
int fputc(int ch, FILE *f)
{
    //USART_SendData(USART1, (u8) ch);
    USART1->DR = (u8) ch;
   
    /* Loop until the end of transmission */
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {
    }

    return ch;
}

/*******************************************************************************
* Function Name  : Delay
* Description    : 延时函数
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void Delay(void)
{
    u32 i;
    for(i=0;i<0xF0000;i++);
    return;
}

/*******************************************************************************
* Function Name  : RCC_Configuration
* Description    : 系统时钟设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void RCC_Configuration(void)
{
    ErrorStatus HSEStartUpStatus;

    //使能外部晶振
    RCC_HSEConfig(RCC_HSE_ON);
    //等待外部晶振稳定
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
    //如果外部晶振启动成功,则进行下一步操作
    if(HSEStartUpStatus==SUCCESS)
    {
        //设置HCLK(AHB时钟)=SYSCLK
        RCC_HCLKConfig(RCC_SYSCLK_Div1);

        //PCLK1(APB1) = HCLK/2
        RCC_PCLK1Config(RCC_HCLK_Div2);

        //PCLK2(APB2) = HCLK
        RCC_PCLK2Config(RCC_HCLK_Div1);

        //FLASH时序控制
        //推荐值:SYSCLK = 0~24MHz   Latency=0
        //        SYSCLK = 24~48MHz  Latency=1
        //        SYSCLK = 48~72MHz  Latency=2
        FLASH_SetLatency(FLASH_Latency_2);
        //开启FLASH预取指功能
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

        //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
        //启动PLL
        RCC_PLLCmd(ENABLE);
        //等待PLL稳定
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        //系统时钟SYSCLK来自PLL输出
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        //切换时钟后等待系统时钟稳定
        while(RCC_GetSYSCLKSource()!=0x08);


        /*
        //设置系统SYSCLK时钟为HSE输入
        RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
        //等待时钟切换成功
        while(RCC_GetSYSCLKSource() != 0x04);
        */
    }

    //下面是给各模块开启时钟
    //启动GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \
                           RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\
                           ENABLE);
    //启动AFIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    //启动USART1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    //启动DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
   
}



/*******************************************************************************
* Function Name  : GPIO_Configuration
* Description    : GPIO设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    //PC口4567脚设置GPIO输出,推挽 2M
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    //KEY2 KEY3 JOYKEY
    //位于PD口的3 4 11-15脚,使能设置为输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11 | GPIO_Pin_12 |\
        GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    //USART1_TX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //USART1_RX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

}



/*******************************************************************************
* Function Name  : NVIC_Configuration
* Description    : NVIC设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

#ifdef  VECT_TAB_RAM
    // Set the Vector Table base location at 0x20000000
    NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else  /* VECT_TAB_FLASH  */
    // Set the Vector Table base location at 0x08000000
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif

    //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    //串口接收中断打开   
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


/*******************************************************************************
* Function Name  : USART1_Configuration
* Description    : NUSART1设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USART1_Configuration(void)
{
    USART_InitTypeDef USART_InitStructure;
   
    USART_InitStructure.USART_BaudRate = 9600;
    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_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);
   
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
   
    USART_Cmd(USART1, ENABLE);
}


void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    //DMA设置:
    //设置DMA源:内存地址&串口数据寄存器地址
    //方向:内存-->外设
    //每次传输位:8bit
    //传输大小DMA_BufferSize=SENDBUFF_SIZE
    //地址自增模式:外设地址不增,内存地址自增1
    //DMA模式:一次传输,非循环
    //优先级:中
    DMA_DeInit(DMA1_Channel4);
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
    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_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);
}


打包的例子,是MDK下的工程,可供参考:)
点击此处下载 ourdev_419646.rar(文件大小:175K) (原文件名:stm32-4.rar)

我的博客中本文的地址,内有语法高亮的代码,方便阅读

出0入0汤圆

发表于 2009-9-25 22:46:22 | 显示全部楼层
标记

出0入0汤圆

发表于 2009-11-30 17:35:09 | 显示全部楼层
学习学习

出0入0汤圆

发表于 2010-2-6 10:47:03 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-15 23:43:08 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-3-21 20:08:44 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-14 17:00:24 | 显示全部楼层
我也标记

出0入0汤圆

发表于 2010-4-19 10:59:21 | 显示全部楼层
学习,谢谢!!

出0入0汤圆

发表于 2010-7-21 22:14:28 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-21 22:24:18 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-17 11:47:44 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-9-9 22:49:06 | 显示全部楼层
在IAR中,好像没有“FILE”这种类型

出0入0汤圆

发表于 2010-12-1 15:16:02 | 显示全部楼层
99厉害,学习了,看来这个过程是先进入串口中断,然后才能DMA传送

出0入0汤圆

发表于 2010-12-1 16:37:56 | 显示全部楼层
dddddddddddddddddddd

出0入0汤圆

发表于 2010-12-9 21:28:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-20 15:46:39 | 显示全部楼层
我想问个问题,如果同时用两个串口,那printf应该怎么使用???

出0入0汤圆

发表于 2011-3-8 16:30:42 | 显示全部楼层

出0入0汤圆

发表于 2011-4-1 04:09:47 | 显示全部楼层
Din! can you use USB and DMA?

出0入0汤圆

发表于 2011-4-1 09:11:32 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-9 12:03:09 | 显示全部楼层
UART接收也是DMA方式?

出0入0汤圆

发表于 2011-6-2 13:15:24 | 显示全部楼层
回复【19楼】gavin_li
-----------------------------------------------------------------------

STM32是可以的

出0入0汤圆

发表于 2011-6-7 01:37:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-6-16 09:16:21 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-6-16 09:36:19 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-12 14:06:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-12 14:10:25 | 显示全部楼层
正在学习STM32中,谢了。。。

出0入8汤圆

发表于 2011-7-22 15:21:58 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-7-24 16:44:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-24 17:26:31 | 显示全部楼层
要IAR的

出0入8汤圆

发表于 2011-7-26 09:33:59 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-7-26 09:59:32 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-7-26 12:11:34 | 显示全部楼层
很不错的资料,受益匪浅

出0入0汤圆

发表于 2011-10-7 00:21:40 | 显示全部楼层
直接到博客里看了

出0入0汤圆

发表于 2011-11-23 23:26:09 | 显示全部楼层
果断下载回去看看,谢谢

出0入0汤圆

发表于 2011-11-24 08:07:44 | 显示全部楼层
学习学习

出0入0汤圆

发表于 2011-11-26 15:26:12 | 显示全部楼层
请教楼主,我现在用STM32 的DMA通过UART发送数据,接收方有什么要求吗?用c8051f020查询方式,能接收吗?

出0入0汤圆

发表于 2011-11-27 10:08:46 | 显示全部楼层
都得学习啊

出0入0汤圆

发表于 2011-12-28 11:52:07 | 显示全部楼层
我按照楼主的程序改了一个,可以串口发送,只不过收到的是乱码,求解。

出0入0汤圆

发表于 2011-12-28 12:06:47 | 显示全部楼层
回复【39楼】akwkevin2011
-----------------------------------------------------------------------

解决了,我把DMA设置放在另一个C文件中,用函数调用缓存数组地址过去的,本以为可以对应同一个缓存数组,按我的想法应该是可以啊,没想通,后来放在一个C文件中,就好了。

出0入0汤圆

发表于 2012-3-2 09:37:49 | 显示全部楼层
顶了

出0入0汤圆

发表于 2012-3-5 14:49:28 | 显示全部楼层
学习

出0入0汤圆

发表于 2012-3-7 22:11:16 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-4-12 21:46:56 | 显示全部楼层
谢谢楼主奉献

出0入0汤圆

发表于 2012-4-12 23:55:02 来自手机 | 显示全部楼层
这小子要转多少莫元啊

出0入0汤圆

发表于 2012-9-20 15:49:03 | 显示全部楼层
mark了  谢啦

出0入0汤圆

发表于 2012-10-9 15:16:36 | 显示全部楼层
MARK收藏学习

出0入0汤圆

发表于 2012-10-11 08:14:27 | 显示全部楼层
学习中,谢谢!

出0入0汤圆

发表于 2012-12-5 23:00:39 | 显示全部楼层
Thanks for sharing!

出0入0汤圆

发表于 2012-12-8 18:00:52 | 显示全部楼层
haodongxi,xuexile

出0入0汤圆

发表于 2012-12-19 09:16:34 | 显示全部楼层
谢谢楼主 下下来了 哈

出0入0汤圆

发表于 2013-3-17 14:41:35 | 显示全部楼层
周末没事 mark优秀帖

出0入0汤圆

发表于 2013-5-10 10:00:47 | 显示全部楼层
mark   USART_DMA

出0入0汤圆

发表于 2013-5-10 10:32:03 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2013-7-19 10:39:44 | 显示全部楼层
学习了 谢谢
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-5 07:55

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表