tag 标签: dma

相关帖子
相关博文
  • 热度 6
    2020-9-24 12:06
    3859 次阅读|
    1 个评论
    STM32 串口 DMA方式接收数据及数据处理方法
    通常我们都使用中断方式接收串口数据。用DMA方式接收,效率会更高。 先在CUBE中设置一下。 使用 UART DMA 接收功能,并使用 Circular 模式。收到的数据将循环放在缓冲区里。 #define BUFFERSIZE 128 uint8_t Uart1RxBuf ; // 软件中定义一个接收缓冲区 uint16_t curchr,lastchr; // 指向当前未处理过的字符和最后一个字符。 HAL_UART_Receive_DMA(&huart1,Uart1RxBuf,BUFFERSIZE); // 程序初始化时,需要启动一次 UART DMA 接收。 /* 需要定时查询是否收到数据。 */ NDTR; // NDTR 指示存储空间的剩余长度 if(curchr != lastchr) { LEDRUNON(); // 指示收到一个数据包 SerialParse(Uart1RxBuf,&curchr,lastchr); } 找 SerialParse 寻找自定协议中的数据包的头和尾。 因为数据是循环接收的,有可能一个数据包会分布在缓冲区尾部和头部。索引时,需要取模运算。 (data == 'E')&&(data == 'R')&&(data == 'R') 好处就是,串口一直接收着,不会丢掉任何数据,只要合理的缓冲区长度,并及时处理数据。能达到很高的速度且不丢包。
  • 热度 2
    2016-4-9 13:07
    1576 次阅读|
    0 个评论
    由于硬件上采用了mpu6050模块,这模块是自带姿态解算和卡尔曼滤波功能的,所以省了不少事儿。直接通过串口读模块发送的数据,然后按照模块固定的协议把数据解析出来,就能得到三轴角速度,三轴加速度,三轴角度。其实mpu6050内部只集成了陀螺仪测角速度,加速度计测加速度,没有直接测量角度的传感器,但是角度可以通过角速度积分得到,并且通过重力加速度在各轴上的分量进行校正,通过数据融合的算法(比如卡尔曼滤波),就可以比较准确的得到角度啦。当然这个过程都在模块内部完成,我们只是应用的话不需要太关注(以后做四轴的话应该得好好研究一下姿态解算和数据融合方面的东西)。 好了,废话不多说。先说代码思路,然后就上代码。Stm32F405的usart3连接模块,接收数据,通过DMA直接存到一个11字节的数组GYRO_buffer 里面,当数组存满了,也就是接受到了完整的一帧数据(一帧数据包括可能是角度数据、加速度数据或者是角速度数据,要根据帧头的第二个字节加以区分,具体的数据帧格式见附件里面的模块资料),然后触发DMA中断,在中断中对数据进行解析,得到最终数据,存储到一个GYRO结构体变量中,这个变量被设置为全局变量,可以在其他的函数中使用该变量的值,做姿态的控制。 代码: #include "main.h"   GYRO gyro = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; unsigned char GYRO_buffer ;   void usart3_configuration(void) {          //一定注意配置顺序,先除能再使能等等          USART_InitTypeDef usart3;          GPIO_InitTypeDef  gpio;          NVIC_InitTypeDef  nvic;          DMA_InitTypeDef   dma;                     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);          RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);                   /*配置GPIO*/          GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_USART3);          GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_USART3);          gpio.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;          gpio.GPIO_Mode = GPIO_Mode_AF;          gpio.GPIO_OType = GPIO_OType_PP;          gpio.GPIO_Speed = GPIO_Speed_100MHz;          gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;          GPIO_Init(GPIOC,gpio);                   /*配置DMA*/                  DMA_Cmd(DMA1_Stream1,DISABLE);          while(DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);//等待DMA可配置          DMA_DeInit(DMA1_Stream1);          dma.DMA_Channel= DMA_Channel_4;          dma.DMA_PeripheralBaseAddr = (uint32_t)(USART3-DR);          dma.DMA_Memory0BaseAddr = (uint32_t)GYRO_buffer;          dma.DMA_DIR = DMA_DIR_PeripheralToMemory;          dma.DMA_BufferSize = 11;          dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;          dma.DMA_MemoryInc = DMA_MemoryInc_Enable;          dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;          dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;          dma.DMA_Mode = DMA_Mode_Circular;          dma.DMA_Priority = DMA_Priority_VeryHigh;          dma.DMA_FIFOMode = DMA_FIFOMode_Disable;          dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;          dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;          dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;          DMA_Init(DMA1_Stream1,dma);          DMA_Cmd(DMA1_Stream1,ENABLE);                   /*配置NVIC*/          nvic.NVIC_IRQChannel = DMA1_Stream1_IRQn;          nvic.NVIC_IRQChannelPreemptionPriority = 1;          nvic.NVIC_IRQChannelSubPriority = 1;          nvic.NVIC_IRQChannelCmd = ENABLE;          NVIC_Init(nvic);                   DMA_ITConfig(DMA1_Stream1,DMA_IT_TC,ENABLE);                   /*配置usart3*/          usart3.USART_BaudRate = 115200;          usart3.USART_WordLength = USART_WordLength_8b;          usart3.USART_StopBits = USART_StopBits_1;          usart3.USART_Parity = USART_Parity_No;          usart3.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;          usart3.USART_HardwareFlowControl = USART_HardwareFlowControl_None;          USART_Init(USART3,usart3);/*usart3初始化*/          //USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);/*配置usart3中断:读数据寄存器非空则中断*/          USART_Cmd(USART3,ENABLE);/*使能usart3*/          USART_DMACmd(USART3,USART_DMAReq_Rx,ENABLE);          }   void DMA1_Stream1_IRQHandler(void) {          if(DMA_GetITStatus(DMA1_Stream1, DMA_IT_TCIF1))          {                    DMA_ClearFlag(DMA1_Stream1, DMA_FLAG_TCIF1);                    DMA_ClearITPendingBit(DMA1_Stream1, DMA_IT_TCIF1);                    if(GYRO_buffer == 0x55)                    {                             switch(GYRO_buffer )                             {                                      case 0x51:                                                gyro.AX = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*16;//g                                                gyro.AY = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*16;                                                gyro.AZ = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*16;                                                gyro.Temperature = ((short)(GYRO_buffer 8 | GYRO_buffer )) /340.0f+36.53f;                                                led_red_on();                                                led_green_off();                                                break;                                      case 0x52:                                                gyro.GX = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*2000;//°/s                                                gyro.GY = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*2000;                                               gyro.GZ = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*2000;                                                gyro.Temperature = ((short)(GYRO_buffer 8 | GYRO_buffer )) /340.0f+36.53f;                                                led_red_off();                                                led_green_on();                                                break;                                      case 0x53:                                                gyro.PITCH = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*180;//度                                                gyro.ROLL = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*180;                                                gyro.YAW = ((short)(GYRO_buffer 8 | GYRO_buffer ))/32768.0*180;                                                gyro.Temperature = ((short)(GYRO_buffer 8 | GYRO_buffer )) /340.0f+36.53f;                                                led_red_on();                                                led_green_on();                                                break;                             }                    }          } } 其中GYRO结构体的结构是这样的: typedef struct {          float AX;          float AY;          float AZ;          float GX;          float GY;          float GZ;          float PITCH;          float ROLL;          float YAW;          float Temperature; }GYRO;   我开始在调试的过程中一直进不了DMA中断,后来发现是DMA配置的问题。下面把DMA配置部分的代码单独调出来说一下          /*配置DMA*/                  DMA_Cmd(DMA1_Stream1,DISABLE);          while(DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);//等待DMA可配置          DMA_DeInit(DMA1_Stream1);          dma.DMA_Channel= DMA_Channel_4;          dma.DMA_PeripheralBaseAddr = (uint32_t)(USART3-DR);          dma.DMA_Memory0BaseAddr = (uint32_t)GYRO_buffer;          dma.DMA_DIR = DMA_DIR_PeripheralToMemory;          dma.DMA_BufferSize = 11;          dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;          dma.DMA_MemoryInc = DMA_MemoryInc_Enable;          dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;          dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;          dma.DMA_Mode = DMA_Mode_Circular;          dma.DMA_Priority = DMA_Priority_VeryHigh;          dma.DMA_FIFOMode = DMA_FIFOMode_Disable;          dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;          dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;          dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;          DMA_Init(DMA1_Stream1,dma);          DMA_Cmd(DMA1_Stream1,ENABLE); 配置的时候首先用DMA_Cmd(DMA1_Stream1,DISABLE);除能DMA,因为DAM在运行过程中是不能配置的。然后用while(DMA_GetCmdStatus(DMA1_Stream1) != DISABLE);语句等待DMA被除能(可能是因为除能需要一定的时间吧,现在也不是很清楚为啥),确定DMA除能后,开始配置DMA,配置完成后,用DMA_Cmd(DMA1_Stream1,ENABLE);重新使能DMA。然后在使能usart3之后,还要加上USART_DMACmd(USART3,USART_DMAReq_Rx,ENABLE);语句,使能usart3的DAM功能。          关于stm32的DMA功能具体的配置,网上有很多资料,就不详细说啦。这里只把我看了网上的资料后仍然没有解决,最终基本靠自己摸索解决的问题写出来,供大家参考。
  • 热度 14
    2015-1-15 09:56
    1119 次阅读|
    0 个评论
    PCI9054    C 模式下的数据传输方式 C 模式下的数据传输分为直接数据传输方式( Direct Data Transfer Modes )与 DMA 方式。直接数据传输方式又分为 Target 与 Initiator 方式,所以 C 模式下这三种数据传输方式,对应了 PCI 的三种数据通信。     1  、 Target 方式 CPU 与 PCI 设备( 9054 )通信,对应了 Target 方式。这种方式又叫程序 IO 方式, CPU 通过执行程序(调用 API )实现对 PCI 设备的访问。 假设访问 9054 , CPU 在 HOST 总线( FSB ,前端总线)上发起一个指向 9054 地址存储器或 IO 端口读写的总线周期, HOST 桥对地址进行译码,由于发起的地址映射于 9054 的地址空间, HOST 桥请求仲裁 PCI 总线的使用权,获得总线后,将 CPU 的该 HOST 总线周期转换并生成一个 PCI 存储器或 IO 读写周期。 PCI 总线上的所有设备都对该地址进行译码,只有 9054 认领该事务,成为该事务的目标。 PCI 总线上的 MASTER 设备( HOST 桥)与 SLAVE 设备( 9054 )之间传送数据。 2 、 Initiator 方式 PCI 设备与 PCI 设备直接通信,对应了 9054 的 Initiator 方式,准确的说是 9054 作为 MASTER 访问 PCI 总线上的其它设备。 比如 9054 访问 PCI 总线上的以太网设备, PCI 总线上的 MASTER 设备( 9054 )申请总线,经总线仲裁器( HOST 桥)仲裁后获得 PCI 总线。 9054 启动一个 PCI 事务(比如存储器读事务),指向以太网映射于 PCI 设备地址空间中的地址,以太网设备识别该地址,认领此总线周期,成为该事务的目标。然后 9054 与以太网设备通过 PCI 总线协议通信。 3 、 DMA 方式 PCI 设备与主存之间的数据通信,可以采用程序 IO 方法,也可以用 DMA 方式实现。 如果使用程序 IO 传送,当 PCI 设备需要传送数据时,它产生中断通知 CPU , CPU 执行设备中断服务程序( ISR ),申请 PCI 总线,从该 PCI 设备中将数据读入 CPU 寄存器中,然后再申请 PCI 总线,将其寄存器中的的数据写入主存。显然,每一个数据传输都要产生两个总线周期,再加上中断时间,所以效率很低。 PCI 设备与主存之间的数据通信,效率较高的方式是 DMA 方式。假设 9054 与主存进行 DMA 通信, 9054 是一个总线主设备, HOST 桥为从设备。首先 9054 申请总线并获得总线使用权,然后在 PCI 总线上启动一个 PCI 事务,指向主存在 PCI 地址空间中的映射地址(由于主存没有直接挂在 PCI 总线上,所以它是通过 HOST 桥把地址映射到 PCI 总线上的)。 HOST 桥译码该地址,认领此总线周期,成为该事务的目标,然后 9054 传输数据到 HOST 桥。同时, HOST 桥又生成一个主存总线周期,与主存传递数据。最后, 9054 设备产生一个中断知 CPU 数据传送已经完成。 转自:http://www.cnblogs.com/spider33/articles/2393079.html
  • 热度 17
    2014-9-18 17:23
    1665 次阅读|
    0 个评论
    WIZnet W5500 支持高达 80MHz SPI 时钟,所以用户可用 MCU来提供一个最大传输速率的高速以太网SPI通讯。本文中,我将展示如何用STM32 MCU来让W5500达到最大传输速率。 当使用来自STMicro的Cortex M3/M4产线的32位处理器,以太网传输速率可以在使用SPI通讯模式事产生最大变化。我将比较使用SPI标准模式和SPI DMA模式的不同传输速率。 组成     MCU : Nucleo STM32F401RE 以太网控制器 : WIZnet WIZ550io(内嵌 W5500) 引脚连接 MCU与WIZnet WIZ550io之间的引脚连接,请参见下表。 首先,连接电源线。 其次,连接SPI信号。连接SCS 引脚到GPIOA_Pin12,因为我将用软件方法处理它。 第三,连接 RSTn 引脚到 GPIOA_Pin11 来复位WIZ550io. 最后, 用GPIOA_pin1这个引脚连接到W550io的RDY引脚上完成初始化. RSTn 引脚和 RDY 引脚的连接并不至关重要,但是连上更稳定. 怎样实现SPI协议  SPI 协议控制W5500和在SPI标准模式及SPI DMA模式是相同的。然而,这两种模式之间的不同是,在SPI总线的数据之间的空闲时间。用于W5500的SPI协议在WIZnet ioLibrary中W5500.c中提供,具有如下功能。 WIZCHIP_READ(uint32_t AddrSel) WIZCHIP_WRITE(uint32_t AddrSel, uint8_t wb) WIZCHIP_READ_BUF(uint32_t AddrSel, uint8_t* pBuf, uint16_t len) WIZCHIP_WRITE_BUF(uint32_t AddrSel, uint8_t* pBuf, uint16_t len) 当SPI DMA 模式未使用时, 内部函数调用标准SPI 读/写函数,比如下面的WIZCHIP_READ_BUF() 功能。 #if !defined (SPI_DMA) WIZCHIP.IF.SPI._write_byte((AddrSel 0x00FF0000) 16); WIZCHIP.IF.SPI._write_byte((AddrSel 0x0000FF00) 8); WIZCHIP.IF.SPI._write_byte((AddrSel 0x000000FF) 0); for(i = 0; i len; i++) pBuf = WIZCHIP.IF.SPI._read_byte(); 当使用 SPI DMA 模式时,准备命令数据,由地址和操作码组成,称作 SPI_DMA_READ() 函数。 #else spi_data = (AddrSel 0x00FF0000) 16; spi_data = (AddrSel 0x0000FF00) 8; spi_data = (AddrSel 0x000000FF) 0; SPI_DMA_READ(spi_data, pBuf, len); #endif 正如在“如何在STM32F2xx or STM32F4xx中使用SPI DMA来完成全双攻通信”, SPI_DMA_READ() 和 SPI_DMA_WRITE() 是由使用SPI DMA模式的代码组成的。 标准 SPI 模式  SPI DMA 模式的性能比较  下面相关数据的源代码是用于loopback测试的例程。可以看到在标准SPI模式和SPI DMA模式中存在多大的性能差异。 标准SPI模式 在 spi_handler.h 中 #ifndef SPI_DMA //#define SPI_DMA #endif 如果你把“#define SPI_DMA” 行打上注释,你将在标准SPI模式下操作。 如果你在Nucleo板上编译之后下载了二进制文件,并用WIZnet提供的AX1.exe上做了loopback的测试,随后传输和接收就如下图所示分别能达到 1.6Mbps,我们可知SPI全速传输速率高达3.2Mbps。 这里, SPI 时钟是 24MHz,你可以清晰的看到在SPI数据间的空闲时间。   SPI DMA 模式 #ifndef SPI_DMA #define SPI_DMA #endif 如上, 移去 “#define SPI_DMA”行注释. 接下来, 如果你下载二进制文件到 Nucleo, 然后你就能看到 SPI DMA 模式的传输速率。传输和接收的loopback测试性能可分别达到  4.3Mbps,并且 SPI全速传输速率超过 8Mbps。如果你使用自己的板子,并且有一个高速的外部时钟来代替Nucleo 板, 随之你就得到了更快的传输速率。 这里, 你可以看到无任何空闲时间的连续SPI通讯,如下图。 by James YS Kim
  • 热度 14
    2013-1-22 16:36
    1045 次阅读|
    0 个评论
      以往的大部分以太网扩展板使用的是WIZnet W5100芯片,我有个一WIZnet WIZ820io模块(使用W5200芯片),W5200有32KB的Buffer缓存(W5100的为16KB),在SPI模式下能运行速率能最大达到33.3Mbps(在W5100上为0.3Mbps)。我在hardware/arduino/sam/libraries/Ethernet/utility/中更新了W5100.cpp和W5100.h以兼容W5200(对于Ethernet.h也有略微改动)。这些更新文件和测试结果(wizperf.txt)能在以下网页获取 https://github.com/manitou48/DUEZoo 简单测试的结果是:从DUE发送出去的1000字节的UDP数据包会包含8字节的响应时延和一定的接收损失率。WIZnet的芯片在每次发送和接收时,会将2048字节的数据送入缓存/每个SOCKET。UDP协议不能保证可靠传输。与UDP协议相比,TCP协议下的数据传输通常较慢,但却能保证可靠传输,并能根据带宽自动调整传输速率。 结果包括在UNO和maple开发板上的测试。在maple和DUE上可以进行SPI+DMA模式下的测试。WIZnet为W5200在SPI模式下给出了33.3Mbps的参考速率。我在SPI时钟频率为28MHz时得到了可靠的结果,但是在42MHz下出错。 在使用DMA模式,且SPI时钟频率为28MHz的情况下,能达到将近16Mbps的速率,大约是只使用SPI传输模式的三倍。文件“w5100.cpp.dma1”是“w5100.cpp”的修正版本,能支持W5200以及DMA+SPI传输模式。详见以下网页中的“wizperf.txt”:https://github.com/manitou48/DUEZoo 更多信息,欢迎了解WIZnet开源硬件合作伙伴:http://blog.iwiznet.cn/?page_id=1494
相关资源