历史上的今天
返回首页

历史上的今天

今天是:2025年04月27日(星期日)

正在发生

2019年04月27日 | STM32F10XX中SPI的DMA发送数据

2019-04-27 来源:eefocus

参考资料:


http://blog.csdn.net/jdh99/article/details/7603029


http://www.openedv.com/posts/list/3159.htm


    上面提到的两篇博文比较详细深刻的说明了DMA的工作方式以及SPI的DMA传输方式的特点。结合对Stm32F103VET6中SPI的DMA传输方式的配置和学习谈谈感受,在看下面的内容之前请先看上面的两篇参考博文,这里就不在说明。


    要使用SPI的DMA功能,首先配置好SPI外设,这里以SPI1为例子。下面的代码初始化了SPI1对应的GPIO以及SPI1工作的模式。


void SpiCC3000Init(void)

{

     SPI_InitTypeDef   SPI_InitStructure;

    GPIO_InitTypeDef  GPIO_InitStructure;

  /*!< Disable SPI */

  SPI_Cmd(SPI_USED, DISABLE);

  

  /*!< DeInitializes the SPI */

  SPI_I2S_DeInit(SPI_USED);

  

  /*!< SPI Periph clock disable */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 , DISABLE); 

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 , DISABLE);   

/*Enable SPI2 Clock */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);

/*Enable SPI1 Clock */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 |RCC_APB2Periph_GPIOA,ENABLE);

/*Config  SCLK and MOSI */

GPIO_InitStructure.GPIO_Pin = SPI_CLK_PIN| SPI_MOSI_PIN;// 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//AF_PP

GPIO_Init(SPI_PORT, &GPIO_InitStructure);

  /*!< Configure SPI pins: MISO */

  GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

  GPIO_Init(SPI_PORT, &GPIO_InitStructure);

/*!< Configure SPI pins: CS output high */

GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(SPI_PORT, &GPIO_InitStructure);

GPIO_SetBits(SPI_PORT,SPI_CS_PIN);

  /*!< CC3000 SPI Init */

  SPI_StructInit(&SPI_InitStructure);

  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;  

  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;/* The buadrate is a fraction of the 72MHz clock*/

  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 7;

  SPI_Init(SPI_USED, &SPI_InitStructure);

  SPI_SSOutputCmd(SPI_USED,ENABLE);//NSS(CS) 

  SPI_Cmd(SPI_USED, ENABLE);

  

  /*Configure SPI for DMA Operation*/

#if defined(ENABLE_SPI_DMA)

  SpiCC3000DMAInit(); 

#else

  SpiRxInterruptClkInit();

#endif

}

    在配置好SPI1以后,配置DMA1功能(选择SPI1的Tx连接到DMA上),因为SPI1对应的DMA功能由DMA1来实现。  下面函数中的DMA时钟将在调用它的函数中开启。

void C3000_DMA_Config(SPI_DMADirection_TypeDef Direction, uint8_t* buffer, uint16_t NumData)

{

#if defined(ENABLE_SPI_DMA)

  DMA_InitTypeDef DMA_InitStructure;

  /* Initialize the DMA_PeripheralBaseAddr member */

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )&SPI1->DR; //SPI_DR_BASE; // 

//DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )&SPI2->DR; //SPI_DR_BASE; // 

  /* Initialize the DMA_MemoryBaseAddr member */

  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;

   /* Initialize the DMA_PeripheralInc member */

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  /* Initialize the DMA_MemoryInc member */

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  /* Initialize the DMA_PeripheralDataSize member */

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

  /* Initialize the DMA_MemoryDataSize member */

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

  /* Initialize the DMA_Mode member */

//yichuan

  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

  /* Initialize the DMA_Priority member */

  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

  /* Initialize the DMA_M2M member */

  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  

  /* If using DMA for Reception */

  if (Direction == SPI_DMA_RX)

  {

    /* Initialize the DMA_DIR member */

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

    

    /* Initialize the DMA_BufferSize member */

    DMA_InitStructure.DMA_BufferSize = NumData;

    

    DMA_DeInit(SPI_DMA_RX_CHANNEL);

    

    DMA_Init(SPI_DMA_RX_CHANNEL, &DMA_InitStructure);

  }

   /* If using DMA for Transmission */

  else if (Direction == SPI_DMA_TX)

  { 

    /* Initialize the DMA_DIR member */

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

    

    /* Initialize the DMA_BufferSize member */

    DMA_InitStructure.DMA_BufferSize = NumData;

    

    DMA_DeInit(SPI_DMA_TX_CHANNEL);

 

    DMA_Init(SPI_DMA_TX_CHANNEL, &DMA_InitStructure);

  }

#endif

}

   配置好DMA1和SPI1后,要做的事情就是把二者联合起来,且配置DMA1的发送完成中断。

void SpiCC3000DMAInit(void)

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

 

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 

  DMA_DeInit(SPI_DMA_RX_CHANNEL);

  DMA_DeInit(SPI_DMA_TX_CHANNEL);

 

  /* Configure and enable SPI DMA TX Channel interrupt */ 

 // NVIC_RxInt_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; //SPI2 TX

  NVIC_RxInt_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;//SPI1 TX // not config the Rx channel

  NVIC_RxInt_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

  NVIC_RxInt_InitStructure.NVIC_IRQChannelSubPriority = 0;//0

  NVIC_RxInt_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_RxInt_InitStructure);

 

 

  /* Configure DMA Peripheral but don't send data*/

  C3000_DMA_Config(SPI_DMA_RX, (uint8_t*)wlan_rx_buffer,0); 

 //C3000_DMA_Config(SPI_DMA_TX, (uint8_t*)wlan_tx_buffer,0);

  C3000_DMA_Config(SPI_DMA_TX, (uint8_t*)wlan_tx_buffer,1700);  //buffer is 1700

 

 

    /* Enable SPI DMA request */

  SPI_I2S_DMACmd(SPI_USED,SPI_I2S_DMAReq_Tx, ENABLE);

  SPI_I2S_DMACmd(SPI_USED,SPI_I2S_DMAReq_Rx, ENABLE);

  /* Enable the DMA Channels Interrupts */

  /*It should be put after the DMA config  */

  DMA_ITConfig(SPI_DMA_TX_CHANNEL, DMA_IT_TC, ENABLE); 

 

  /* Enable DMA RX Channel */

  DMA_Cmd(SPI_DMA_RX_CHANNEL, ENABLE);  

/*Note: Enable the SPI_DMA Channel,it begin SPI translate*/

  /* Enable DMA TX Channel ,begin the dma translate*/  

  DMA_Cmd(SPI_DMA_TX_CHANNEL, ENABLE); 

}

  这里要强调的是:使能DMA中断标志的语句DMA_ITConfig(SPI_DMA_TX_CHANNEL, DMA_IT_TC, ENABLE); 应当放到DMA1配置完以后,不然发送数据完成以后,不会进入中断处理函数,就是相当于发送完成后产生中断的这个功能并没有配置成功,如果放到DMA1配置前面。下面的图片是参考手册中关于DMA的配置过程,其中中断的使能在第六步,所以应当在DMA初始化完成后,再使能中断功能。


 

    接下来就是中断处理函数了,响应传输完成中断的产生


void DMA1_Channel3_IRQHandler(void)

{

    if(DMA_GetITStatus(DMA1_IT_TC3)==SET)

    {

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_ResetBits(GPIOC,GPIO_Pin_6);

 

    }

   DMA_ClearITPendingBit(DMA1_IT_TC3);

 

}

最后启动DMA传输是通过DMA_Cmd(SPI_DMA_TX_CHANNEL, ENABLE) 这句话实现,当DMA通道被使能以后,它就自动开始传输数据,而不影响CPU其干其它事情,直到DMA传输完数据产生中断时,CPU才会去处理中断函数。


推荐阅读

史海拾趣

Adaptive Interconnect Electronics, Inc. [AIE]公司的发展小趣事

为了适应全球电子市场的快速发展,AIE公司积极实施全球化战略。公司在多个国家和地区设立了分支机构或办事处,以便更好地服务当地客户。同时,AIE还积极参加国际电子展会和论坛,与全球各地的合作伙伴和客户建立了广泛的联系。这些举措不仅拓展了AIE的市场份额,也提升了公司的国际影响力。

Aeroflex Metelics / Hi-Rel Components公司的发展小趣事

为了适应全球电子市场的快速发展,AIE公司积极实施全球化战略。公司在多个国家和地区设立了分支机构或办事处,以便更好地服务当地客户。同时,AIE还积极参加国际电子展会和论坛,与全球各地的合作伙伴和客户建立了广泛的联系。这些举措不仅拓展了AIE的市场份额,也提升了公司的国际影响力。

ECLIPSE公司的发展小趣事

随着Eclipse项目的不断发展,越来越多的知名公司加入到这一开源社区中,如Oracle、Red Hat等。这些公司不仅为Eclipse贡献了代码,还提供了资金支持。Eclipse的功能日益完善,逐渐成为了Java开发领域的佼佼者。同时,Eclipse也支持其他编程语言,如C/C++、Python等,进一步扩大了其用户群体。

ApexTool Group公司的发展小趣事

随着全球电子市场的不断扩大,Apex Tool Group积极实施全球化战略,拓展市场版图。公司在全球范围内建立了多个生产基地和分销网络,以便更好地服务全球客户。同时,Apex Tool Group还积极与全球知名的电子企业开展合作,共同推动电动工具技术的创新和发展。这些举措使得Apex Tool Group在全球电子行业中的地位日益稳固。

Compound Photonics公司的发展小趣事

在公司发展初期,Compound Photonics曾专注于一款微投显示模组的研发。然而,这个项目并未如预期般顺利,最终宣告失败。面对挫折,公司并没有放弃,而是迅速调整战略,转向LCoS方向。这一决策不仅让公司避免了进一步的损失,还为其在LCoS领域的发展打开了新的大门。

长工微电子公司的发展小趣事

面对日益激烈的市场竞争,长工微电子始终保持对研发的重视。公司不断加大产品研发力度,积极拓展国内外市场,目标成为国际一流的半导体公司。同时,长工微电子也积极关注行业动态和技术趋势,不断调整和优化产品策略,以适应市场的变化。在未来,长工微电子将继续秉承创新、务实、进取的精神,为电子行业的发展做出更大的贡献。

问答坊 | AI 解惑

青越锋PCB软件——快捷键教程

在进行连接线的过程中, 按住W键可进行线宽的设置; 按住L键可进行层的切换 当选择一根strack时,按住F5即可选中整个管脚对;     按住F6可选择整个net网络;     按住Q键进行单位切换; Esc:终止 ...…

查看全部问答>

设计

现在汽车事故非常多, 我想用单片机设计,去解决这个问题,就是让前后两辆车始终保持一定的距离,当小于这个距离时让车自动减速。老师,您能给指导一下吗?…

查看全部问答>

关于单片机 sfr IP = 0xB8; //中断优先级控制 寄存器的几点疑惑?

最近写个51程序,用Keil生成.HEX文件,proteus仿真,用到定时器0 无意中发现                 TMOD=0x01; //定时器T0工作方式1;16位方式         TR0=1; //启动定时器T0 ...…

查看全部问答>

Windows驱动难题请教,如何让UI层响应设备插入的事件

我给一个USB设备写了个驱动程序 这个设备每次在插入的时候,都需要在UI层进行一系列的配置工作,然后才能开始工作 考虑如果使用Coinstaller,只能在第一次安装的时候起作用,以后再插入就不行了 也不能使用服务或者其他随系统启动运行的扫描进程 ...…

查看全部问答>

ATmega16的延时问题

void Delayms(unsigned char  MS)                  //延时程序 { unsigned char i,j; for( i=0;i…

查看全部问答>

关于6N137光耦输出的问题

今天想做一个步进电机的东东,用了6N137光耦作为驱动器的脉冲信号输入。但是不管程序怎么写电机就是不转。 后来用万用表测了一下,发现6N137光耦的输入断也就是接单片机I/O的脚是有脉冲电平的,但是光耦的输出脚Vo始终是高电平,始终不懂。难怪电 ...…

查看全部问答>

ARM嵌入式QQ群收集

收集一些群,给大家玩玩。我一般晚上回去群聊。 花江群 39063007——主要是wince的,单片机啥都有。 珠海电子工程师群 92580412 ——里面有一些骨灰级工程师,偏向硬件。 桂电科协群32986294——里面有部分是学生,但是也蛮多深圳,广州的工程师 ...…

查看全部问答>

关于闩锁问题

很多32位CPU在电源缓慢上升时会出现闩锁现象,即进入此状态后,即使给芯片复位信号,芯片也不能启动,除非掉电再重新上电。STR71X会不会也有这样的问题呢?如果有此问题,有没有推荐的简单可靠电路解决此问题?…

查看全部问答>

我的网络芯片换成DM9161了,请问NDK怎么才能移植使用

我的网络芯片换成DM9161了,请问NDK怎么才能移植使用,使用的是合众达6437…

查看全部问答>

求教,,,关于传感器测高度

请教一下各位高手,,我想用传感器测量一个物体的高度(比如说水杯,,水壶之类的)要求是不受物体的限制,当然高度也会有一个范围。也就是说,当物体往那里一放,就能测出它的高度,,用什么传感器好呢???? 还有,测量液面的高度用超声波传 ...…

查看全部问答>