ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

DSP F28335与STM32 HAL SPI通信详解

2020-12-16 00:02:02  阅读:670  来源: 互联网

标签:spi2 DMA HAL DSP STM32 SPI Init GPIO


目录

SPI通信简介

一、DSP F28335主机代码

二、STM32代码

 


SPI通信简介

PCB芯片间的数据通信方式有很多种,其中串行(IO引脚较少)低速总线最常用的是SPI和I2C。
I2C(即IIC)是一种半双工总线(即同一时刻只能收或只能发),每个芯片只需提供两个两个引脚(SDA和SCL)即可实现多块芯片间的通信。
SPI是一种全双工同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。如果有多个从机,主机就要为每个从机提供一个CS片选引脚。

SPI通信回路


在全双工的SPI通信模式下,主机每发送一个字节(或半字),从机就必须同步返回一个字节(或半字)。在这种通信机制下,SPI的通信效率非常高。同时,由于SCLK的时钟信号可以设置得比较高(MBit级),SPI的通信速度也非常快。
项目中DSP和STM32之间需要进行通信,由于SPI的通信协议更加简单,波特率更高,我选用了SPI作为芯片间的通信方式。其中DSP作为主机,STM32作为从机。


 

一、DSP F28335主机代码

DSP在通信中作为主机,所有通信都由DSP主动发起,DSP发起前一定要让stm32做一个spi的准备完毕的信号(在下面代码中是GPIO49),另外,如果发现数据有错位、乱序的话要做好复位。spi通信对时钟信号的依赖非常强,只要一开始错位,那么后面的数据全部是错的。

Uint16 sdata[17];
Uint16 rdata_SPI[17];
Uint16 rdata[17];
void Bsp_SpiInit(void)
{
    //初始化管脚
	EALLOW;
	GpioCtrlRegs.GPBPUD.bit.GPIO54 = 0;   // Enable pull-up on GPIO54 (SPISIMOA)
	GpioCtrlRegs.GPBPUD.bit.GPIO55 = 0;   // Enable pull-up on GPIO55 (SPISOMIA)
	GpioCtrlRegs.GPBPUD.bit.GPIO56 = 0;   // Enable pull-up on GPIO56 (SPICLKA)
	GpioCtrlRegs.GPBPUD.bit.GPIO57 = 0;   // Enable pull-up on GPIO57 (SPISTEA)
	GpioCtrlRegs.GPBQSEL2.bit.GPIO54 = 3; // Asynch input GPIO16 (SPISIMOA)
	GpioCtrlRegs.GPBQSEL2.bit.GPIO55 = 3; // Asynch input GPIO17 (SPISOMIA)
	GpioCtrlRegs.GPBQSEL2.bit.GPIO56 = 3; // Asynch input GPIO18 (SPICLKA)
	GpioCtrlRegs.GPBQSEL2.bit.GPIO57 = 3; // Asynch input GPIO19 (SPISTEA)
	GpioCtrlRegs.GPBMUX2.bit.GPIO54 = 1; // Configure GPIO54 as SPISIMOA
	GpioCtrlRegs.GPBMUX2.bit.GPIO55 = 1; // Configure GPIO55 as SPISOMIA
	GpioCtrlRegs.GPBMUX2.bit.GPIO56 = 1; // Configure GPIO56 as SPICLKA
	GpioCtrlRegs.GPBMUX2.bit.GPIO57 = 1; // Configure GPIO57 as SPISTEA
	EDIS;
    SpiaRegs.SPIFFTX.all=0xE040;//使能SPIFIFO功能;TXFIFO复位;清除 TXFF INT 中断位;TXFIFO 中 断不使能;
	SpiaRegs.SPIFFRX.all=0x204f;//重新使能接收 FIFO 操作;清RXFIFO中断标志位;中断不使能。
	SpiaRegs.SPIFFCT.all=0x0;//这个寄存器是设置FIFO延时的,不需要用到。

	SpiaRegs.SPICCR.all =0x004F; // 复位,下降沿发送,上升沿接收(即时钟极性是:空闲时为高电平),  字长16位。关闭SPI内部LOOP BACK  禁止回送
	SpiaRegs.SPICTL.all =0x0006; // 主机模式, 时钟相位为正常相位, 
    SpiaRegs.SPICTL.bit.CLK_PHASE=0;//TALK=1使能主机发送, SPI中断不使能.  时钟相位为:数据在第2个时钟边沿被选择
	SpiaRegs.SPIBRR =0x0003; //波特率由主机决定=9.375MHz 。波特率=LSPCLK/(SPIBRR+1)=150MHz/4/4=9.375MHz
	SpiaRegs.SPICCR.all =0x00CF;//下降沿发送,上升沿接收(即时钟极性是:空闲时为高电平), 字长16位。准备发送或接收  禁止回送模式SPILBK=0
	SpiaRegs.SPIPRI.bit.FREE = 1; // Set so breakpoints don't disturb xmission //仿真用的
}

void Bsp_SpiSendRecv(Uint16* Data,int Len)
{
     Uint16 i,j,tmp;
     sdata[0]=0xaa;                 //数据帖头
     static Uint16 ErrorCount=0;
     //采样值发去ARM显示
     if (Bsp_GpioRead(49)!=1)//stm32已经拉低
     {
		 for ( i=0;i<Len;i++ )
		 {
			 sdata[i+1]=Data[i];
		 }
		 SpiaRegs.SPIFFTX.all=0xE040;//使能SPIFIFO功能;TXFIFO复位;清除 TXFF INT 中断位;TXFIFO 中 断不使能;
		 SpiaRegs.SPIFFRX.all=0x204f;//重新使能接收 FIFO 操作;清RXFIFO中断标志位;中断不使能。
		 for ( i=0;i<(Len+1);i++ )
		 {

			SpiaRegs.SPITXBUF=sdata[i];        //发送数据
			 while(SpiaRegs.SPIFFRX.bit.RXFFST !=1) { }  //接收FIFO为空时,等待!
			 // 检查返回数据
			 rdata_SPI[i] = SpiaRegs.SPIRXBUF;
			 if(rdata_SPI[0]==0xbb)   //收到正确的帖头
			 {
				 if(i>0)
				 {
					rdata[i-1]=rdata_SPI[i];
				 }
			 }
			 else
			 {
				 ErrorCount++;
				 if (ErrorCount>10)
				 {
					 ErrorCount=0;
					 SpiaRegs.SPISTS.all=0x0000;   //所有状态位清零
					 SpiaRegs.SPICCR.bit.SPISWRESET=1;//软复位
					 SpiaRegs.SPIFFTX.all=0xE040;//使能SPIFIFO功能;TXFIFO复位;清除 TXFF INT 中断位;TXFIFO 中 断不使能;
					 SpiaRegs.SPIFFRX.all=0x204f;//重新使能接收 FIFO 操作;清RXFIFO中断标志位;中断不使能。

				 }
			 }
		 }
		 while(SpiaRegs.SPIFFRX.bit.RXFFST !=0)
		 {
			 rdata_SPI[++i] = SpiaRegs.SPIRXBUF;
		 }
     }

}

二、STM32代码

我的STM32使用HAL库,SPI的通信方式使用DMA通道,如果程序对控制时间不敏感,使用查询方式也是可以的。但不要使用中断方式,我曾测试过中断方式,SPI通信的错位、乱序机率大增,而且我还找不到方法消除。

SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi2_tx;
DMA_HandleTypeDef hdma_spi2_rx;

/* SPI2 init function */
void MX_SPI2_Init(void)
{

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_SLAVE;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

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

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

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI2 GPIO Configuration
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    PB15     ------> SPI2_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_14;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* SPI2 DMA Init */
    /* SPI2_TX Init */
    hdma_spi2_tx.Instance = DMA1_Channel5;
    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_HALFWORD;
    hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi2_tx.Init.Mode = DMA_NORMAL;
    hdma_spi2_tx.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi2_tx);

    /* SPI2_RX Init */
    hdma_spi2_rx.Instance = DMA1_Channel4;
    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_HALFWORD;
    hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi2_rx.Init.Mode = DMA_NORMAL;
    hdma_spi2_rx.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi2_rx);

  /* USER CODE BEGIN SPI2_MspInit 1 */

  /* USER CODE END SPI2_MspInit 1 */
  }
}

void SpiReset(void)//SPI重启
{
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);
	HAL_SPI_DeInit(&hspi2);
	MX_SPI2_Init();	
	Stat=HAL_SPI_TransmitReceive_DMA(&hspi2, (uint8_t*)y,(uint8_t*)SpiRecv, 9);
	if (Stat!=HAL_OK)
		HAL_NVIC_SystemReset();
	HAL_Delay(500);
	LastSpiRecvTick=SystemTimeTick;
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);
}


void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	int i;
	static int ErrorCount=0;
	
	y[0]=0xbb;
	for (i=1;i<9;i++)
		y[i]++;
	
	//数据传输错误判断
	if (SpiRecv[0]!=0x00AA)
		ErrorCount++;
	else
	{
		ErrorCount=0;
		LastSpiRecvTick=SystemTimeTick;
	}
	if (ErrorCount>10)//重启spi
	{
		ErrorCount=0;
		SpiReset();
	}	

	HAL_SPI_TransmitReceive_DMA(&hspi2, (uint8_t*)y,(uint8_t*)SpiRecv, 9);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);
}

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{	
	SpiReset();
}

STM32端同样要注意SPI通信的数据错位、乱序的问题,一定要做好超时、错误后的复位工作。

 

标签:spi2,DMA,HAL,DSP,STM32,SPI,Init,GPIO
来源: https://blog.csdn.net/lingcoln/article/details/111055092

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有