学习笔记(4):STM32H743的SPI通信,DMA方式_h743 spi_wql_njust的博客-程序员秘密

技术标签: 研究生阶段学习笔记  stm32  c语言  单片机  

在已经完成MCU和传感器的SPI通信的基础上,增加DMA读取方式。

学习笔记(1)stm32h743和ADXL355的SPI通信,基于cubeMX和keil5MDK平台,采用HAL库编程,代码已模块化处理_wql_njust的博客-程序员秘密

在cubeMX中,如下图左下角,点击add,增加两个DMA Requset,优先级可以配置为Medium,

使用DMA需要配置中断,所以 DMA1 stream0、DMA1stream1、 SPI global interrupt需设置合适的优先级。

生成代码:

不要复制我的! 我这是示意性的,中断没有设置,也没包含句柄的声明。

下面的代码 在stm32h7xx_hal_msp.c中,可移植到自己的SPI.C文件中,

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */

  /* USER CODE END SPI2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI2 GPIO Configuration
    PC1     ------> SPI2_MOSI
    PC2_C     ------> SPI2_MISO
    PB10     ------> SPI2_SCK
    */
    GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* SPI2 DMA Init */
    /* SPI2_RX Init */
    hdma_spi2_rx.Instance = DMA1_Stream0;
    hdma_spi2_rx.Init.Request = DMA_REQUEST_SPI2_RX;
    hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi2_rx.Init.Mode = DMA_NORMAL;
    hdma_spi2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hspi,hdmarx,hdma_spi2_rx);

    /* SPI2_TX Init */
    hdma_spi2_tx.Instance = DMA1_Stream1;
    hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;
    hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi2_tx.Init.Mode = DMA_NORMAL;
    hdma_spi2_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hspi,hdmatx,hdma_spi2_tx);

    /* SPI2 interrupt Init */
    HAL_NVIC_SetPriority(SPI2_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(SPI2_IRQn);
  /* USER CODE BEGIN SPI2_MspInit 1 */

  /* USER CODE END SPI2_MspInit 1 */
  }

}

下面的代码 在main.c中,可移植到自己的SPI.C文件中, 

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  /* DMA1_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
  /* DMA1_Stream7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);

}

下面的代码 在stm32h7xx_it.c中,需要移植到自己的SPI.C文件中, 是DMA的完成中断,自己重写也可以,如果不及时清除中断标志,程序会卡死。

void DMA1_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */

  /* USER CODE END DMA1_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi2_rx);
  /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */

  /* USER CODE END DMA1_Stream0_IRQn 1 */
}

/**
  * @brief This function handles DMA1 stream1 global interrupt.
  */
void DMA1_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */

  /* USER CODE END DMA1_Stream1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi2_tx);
  /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */

  /* USER CODE END DMA1_Stream1_IRQn 1 */
}

下面的函数是我用来读传感器数据的,调用函数时,为三个形参传递的值:读取数据的地址,读到的数据存在哪里,读多少个字节

注意! DMA方式下,Delay是必不可少的,不然将会读到全是0xff,因为DMA命令发出后,不会阻塞CPU,因此,如果下面的程序没有延时,发送未完成时,接受命令便发出了,同理,接收未完成时,片选已经拉高了,但是常规的HAL_Delay最小可以延时1ms,实在是没有必要,因此我写了一个微秒级的延时函数。

void SPI_DMA_ReadWriteByte(uint8_t TData,uint8_t * DRxData,uint8_t Len )
{

    uint8_t adress=TData;
	adress = adress << 1;                                          
    adress |= 0x01;  

	HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_RESET); 
                                          
    if(HAL_SPI_Transmit_DMA(&hspi2, &adress, 1) != HAL_OK)
		printf("\r\nDMA发送不正常!\r\n");
	Delay_us(2);

	if(HAL_SPI_Receive_DMA(&hspi2, DRxData, Len) != HAL_OK)
        printf("\r\nDMA接收不正常!\r\n");

	Delay_us(2);	
	
	HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_SET); 
}

Delay_us():

void Delay_us(uint16_t time)//Delay_us(1) = 1us
{
HAL_SYSTICK_Config(400);
HAL_Delay(time);
HAL_SYSTICK_Config(400000);
}

 在我的实验中,DMA并未达到预期的加速效果,因为我每次只读取9个字节,然后要对9个字节进行拼接和数据转换,还要做均值滤波,依然要占用CPU资源,而且在我需要的数据传输完成之前,我的下一个任务还是被阻塞的,所以,后面只能在ADXL355接个MCU,把数据处理完成后,源源不断地发送到主MCU,然后采用DMA双缓冲的办法。这个是后话了,不过一定会做的。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_55928737/article/details/124009823

智能推荐

监控服务器磁盘阵列哪个稳定,监控选择存储服务器和磁盘阵列_猫咪逗你玩儿的博客-程序员秘密

监控选择存储服务器和磁盘阵列 内容精选换一换当服务器中的磁盘发生故障、或者由于人为误操作导致服务器数据丢失时,可以使用已经创建成功的备份恢复原服务器。如果希望将数据恢复至其他服务器上,请参考如何将原服务器数据恢复至新创建的服务器?备份的数据盘的数据,不能恢复到系统盘中。不支持恢复到状态处于“故障”的服务器。不支持使用复制操作产生的备份用于恢复。需要恢复的服务器中的磁盘运行状态正常。配置目的端或启动...

cocos2d-x /cocos2d-iphone在mac下环境搭建_Colin丶的博客-程序员秘密

换了macbook ,迫不及待的装了xcode。下面谈谈在mac下cocos2d-x环境的搭建。(cocos2d-iphone)同样道理。首先,下载必须的文件。cocos2d 下载地址  http://www.cocos2d-iphone.org/downloadcoco2d -x 下载地址 http://code.google.com/p/cocos2d-x/down

UVA - 10480 Sabotage 最小割,输出割法_dengpangbu3703的博客-程序员秘密

UVA - 10480 Sabotage题意:现在有n个城市,m条路,现在要把整个图分成2部分,编号1,2的城市分成在一部分中,拆开每条路都需要花费,现在问达成目标的花费最少要隔开那几条路。题解:建图直接按给你的图建一下,然后呢跑一下最大流,我们就知道了最小割是多少,答案就是最小割了 。现在要求输出割法。我们从s开始往前跑,如果某条正向边有流量,我们就按着这条边继续往外走,...

【API设计风格—RESTful】:番外篇:跨域问题(四)_ionic serve ip_xu_chenyang的博客-程序员秘密

问题描述: Ionic serve 在浏览器中调试时IP是:http://localhost:8100 而我的本机的服务端的IP是:http://localhost:8080 所以存在跨域访问的问题。解决方法【1】设置response(此处只是了解,最终解决方案在下面) response.setHeader(“Access-Con

Xtool X100 PAD2 Error Code 5 Data Not Exist Solution_abc1294211669的博客-程序员秘密

If your XTOOL X100 Pad2 gives your error ” GetUpdateListEx: Error code= 5. Data doesn’t exist.”, which means your 2-year free update online service is due.Need to pay for new updates subscription expires after 2 years.https://xtooltools.blogspot.com/202

请输入一个字符串s和两个正整数m和n,将字符串s中从第m个字符开始的n个字符复制到字符串s中,再输出字符串。要求用字符指针定义并调用函数strcopy(s,m,n),它的功能是将字符串s中从第m个字_jueduiwudi6的博客-程序员秘密

#include&amp;lt;stdio.h&amp;gt;#include&amp;lt;string.h&amp;gt;void strcopy(char *s,int m,int n){ char *p,*q; for(q=s,p=s+n-1;p&amp;lt;s+m+n-1;p++,q++) *q=*p; *q='\0';}int main(){ char str[20]; int m=3,n...

随便推点

铅酸蓄电池三阶simulink仿真模型,可获得蓄电池充放电特性,并提供数学模型相关文献和多组电池模型参数。_铅酸电池三阶模型_「已注销」的博客-程序员秘密

铅酸蓄电池三阶simulink仿真模型,可获得蓄电池充放电特性,并提供数学模型相关文献和多组电池模型参数。ID:6950652335028570t***5

无人机室内编队系统集成(一)_无人机编队室内飞行系统_北里小生的博客-程序员秘密

无人机室内编队系统集成简介系统集成关键组件室内定位系统支持外部位置数据导入的开源飞控组网通信地面站基础功能系统集成关键组件1 支持引入外部定位数据(室内定位系统提供)的飞行控制器2 提供精确室内定位数据的室内定位系统3 具备定位数据转发和飞行器实时监控及实时轨迹控制的地面站4 点到多点的组网通信系统室内定位系统目前精度可以满足无人机室内飞行的较为成熟的室内定位方案有:1 基于视觉的...

HDFS——HDFS的安全模式(4)_上善若水的博客-程序员秘密

什么时候安全模式?安全模式是hadoop的一种保护机制,用于保证集群中的数据块的安全性。如果HDFS处于安全模式,则表示HDFS是只读状态。 当集群启动的时候,会首先进入安全模式。当系统处于安全模式时会检查数据块的完整性。假设我们设置的副本数(即参数dfs.replication)是5,那么在datanode上就应该有5个副本存在,假设只存在3个副本,那么比例就是3/5=0.6。在配置文件hdf...

长草问题_长草 c++_haobingqian_的博客-程序员秘密

问题描述  小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1。  小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。  这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,这四小块空地都将变为有草的小块。  请告诉小明,k 个月后空地上哪些地方有草。1 2 3 4输入格式  输入的第一行包含两个整数 n, m。  接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字

webuploader 使用实例_abel0130的博客-程序员秘密

log:http://16aspx.com/CodeView/2014/5112/ueditor/third-party/webuploader/webuploader.custom.js.html

手动安装em(redhat 5/oracle 11g)_lixiaohuiok111的博客-程序员秘密

手动安装em(redhat 5/oracle 11g)(2010-07-21 09:35:11) 静默安装完11g后,发现em无法启用,检查后发现还未安装……囧。没办法,只能手动安装了。首先用emca命令清除em的相关配置:1、使用emca -deconfig dbcontrol db命令删除配置2、使用emca -repos drop删除repository清除步骤一

推荐文章

热门文章

相关标签