DSP

DSP与STM32的SPI通信调试及浮点数据传输调试

2019-07-13 09:49发布

        硬件环境:DSP为TMS320C6722,STM32为STM32F103ZG,两控制芯片为SPI三线连接,即SPI_SOMI,SPI_SIMO,SPI_CLK三线.         首先整体简述下传输过程,DSP与STM32为SPI三线连接,无片选信号;DSP有一GPIO引脚连接至STM32外部中断引脚(在此处称为DataReady引脚);SPI虽然为全双工,但是在此项目中只需要DSP传输给STM32的数据,而不关心STM32传输给DSP的数据。STM32作为主机,DSP作为从机。为了节约CPU,从主机均使用SPI的DMA传输。数据传输的整体流程是这样的:DSP准备好数据后,会通过DataReady引脚由低电平变为高电平来触发STM32的外部中断,STM32在外部中断中将DMA开启,然后数据开始传输,STM32传输完成进入DMA传输完成中断,关闭DMA,STM32对收到的数据进行处理。         下面简述DSP这边需要做的工作:         熟悉C672x系列的人都知道,DMA在此系列芯片中占有相当大的作用(在C672x中称为dMAX)。DMA在C672x中可以进行数据传输、接收外部中断信号、触发CPU中断等等。         对SPI及DMA进行配置:先对SPI进行配置,具体流程参考C672x的SPI手册(下面我也会贴出代码);然后对DMA的事件入口及传输入口进行配置。          #define SPIGCR0 0x0000 #define SPIGCR1 0x0001 #define SPIINT0 0x0002 #define SPILVL 0x0003 #define SPIFLG 0x0004 #define SPIPC0 0x0005 #define SPIPC1 0x0006 #define SPIPC2 0x0007 #define SPIPC3 0x0008 #define SPIPC4 0x0009 #define SPIDAT0 0x000E #define SPIDAT1 0x000F #define SPIBUF 0x0010 #define SPIDELAY 0x0012 #define SPIFMT0 0x0014 #define SPIFMT1 0x0015 #define SPIFMT2 0x0016 #define SPIFMT3 0x0017 #define DEPR 0x0000 #define DEER 0x0001 #define DEDR 0x0002 #define DEHPR 0x0003 #define DELPR 0x0004 #define DEFR 0x0005 #define DTCR0 0x001F #define TransferEntry0 0x0000 #define TransferEntry1 0x0001 #define TransferEntry2 0x0002 #define TransferEntry3 0x0003 #define TransferEntry4 0x0004 #define TransferEntry5 0x0005 #define TransferEntry6 0x0006 #define TransferEntry7 0x0007 #define DMAX_DATA_TRANSFER_COUNT 8 volatile unsigned int * spi0_ptr = (unsigned int *) 0x47000000; volatile unsigned int * dMAX_ptr = (unsigned int *) 0x60000008; volatile unsigned int * SPI0RX_EventEntry = (unsigned int *) 0x62008034; volatile unsigned int * SPI0RX_TransferEntry = (unsigned int *) 0x620081D4; Uint16 srcData[DMAX_DATA_TRANSFER_COUNT]; Uint16 dstData[DMAX_DATA_TRANSFER_COUNT]; void spi0_configuration(void) { spi0_ptr[SPIGCR0] = 0x00000001; //SPI0 is Released from Software Reset spi0_ptr[SPIGCR1] = 0x00000000; //SPI0 Disable,LOOPBACK Disable,Active Mode,Slave Mode spi0_ptr[SPIPC0] = 0x00000E00; //SOMI,SIMO,CLK default as SPI Functional Pins spi0_ptr[SPIPC1] = 0x00000000; //SOMI,SIMO,CLK default as SPI Functional Pins spi0_ptr[SPIDAT1] = 0x00000000; //select SPIFMAT0 spi0_ptr[SPIFMT0] = 0x00026310; //MSB,Polarity=1,Phase=0,Prescale=99(1MHz),Charlen=16 spi0_ptr[SPIINT0] = 0x00010000; //only DMA enabled spi0_ptr[SPILVL] = 0x00000000; //all mapped to interrupted to INT0 //Configure dMAX dMAX_ptr[DEDR] |= 0x00002000; //Disable Event13 dMAX_ptr[DEFR] |= 0x00002000; //Write 1 to Clear Flag dMAX_ptr[DEPR] |= 0x00002000; //High Polarity dMAX_ptr[DELPR]|= 0x00002000; //Low priority SPI0RX_EventEntry[0x0000] = 0x1201D4A2; //Configure Event Entry13 //dMAX Transfer Entry SPI0RX_TransferEntry[TransferEntry0] = (Uint32)srcData; SPI0RX_TransferEntry[TransferEntry1] = (Uint32)dstData; SPI0RX_TransferEntry[TransferEntry2] = DMAX_DATA_TRANSFER_COUNT; SPI0RX_TransferEntry[TransferEntry3] = DMAX_DATA_TRANSFER_COUNT; SPI0RX_TransferEntry[TransferEntry4] = (Uint32)srcData; SPI0RX_TransferEntry[TransferEntry5] = (Uint32)dstData; SPI0RX_TransferEntry[TransferEntry6] = (Uint32)srcData; SPI0RX_TransferEntry[TransferEntry7] = (Uint32)dstData; dMAX_ptr[DEER] |= 0x00002000; //Enable Event13 spi0_ptr[SPIGCR1] |= 0x01000000; //Enable SPI0 }         我没有用TI公司提供的CSL库,一开始是倾向于用CSL库的,但是在调试过程中发现麻烦重重,有些繁琐,而且好多函数不可见,封装在了.lib库里,远不如自己写驱动更简单方便。在main函数中调用上面的驱动函数后,只要STM32一开启DMA,就会有数据进入到C6722的SPI数据寄存器中,C6722就会触发SPI的DMA,传输开始。而且在传输过程中不需要CPU干预。         还有一个问题需要注意。SPI0的三个引脚SPI_SOMI,SPI_SIMO,SPI_CLK跟C6722的Parallel Flash启动有关,上电时刻三个引脚的状态直接决定Parallel Flash启动能不能成功。刚开始调试时,DSP与STM32均下载好程序后,发现DSP无法启动,后来在DSP的仿真状态下查看CFGPIN0寄存器发现SPI_CLK的引脚状态为1(若想Parallel Flash启动成功,SPI_CLK的引脚状态应该为0)。后来仔细想,在STM32中没放程序的时候DSP启动没问题,所以可能是STM32的SPI驱动程序使得SPI_CLK的引脚状态在DSP捕获其时发生了变化,所以我在STM32调用SPI的驱动程序之前加了个延时,然后就好了,DSP可以正常启动。         接下来是STM32这方面需要做的工作:         STM32网上资料较多,而且也用的极其大众化,所以直接贴驱动代码。           void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); SPI_Cmd(SPI1, DISABLE); SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; SPI_InitStructure.SPI_CPOL=SPI_CPOL_High; SPI_InitStructure.SPI_CPHA=SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS=SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial=7; SPI_Init(SPI1, &SPI_InitStructure); SPI1->CR1 |= 1<<9 ; SPI1->CR1 |= 1<<8 ; SPI1->CR2 |= 1<<0; SPI1->CR2 |= 1<<1; SPI_Cmd(SPI1, ENABLE); //ʹÄÜSPIÍâÉè } void SPI1_DMA_Configuration(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel2); DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel2, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(SPI1_TX_Buff); DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel3, &DMA_InitStructure); } void EXTI15_10_IRQHandler(void) //External Interrupt(Signal from DSP) { if(EXTI_GetITStatus(EXTI_Line12)!=RESET) { EXTI_ClearITPendingBit(EXTI_Line12); DMA_Cmd(DMA1_Channel2, ENABLE); //Enable DMA,Start Transfer DMA_Cmd(DMA1_Channel3, ENABLE); GPIO_ResetBits(GPIOE, GPIO_Pin_7); } } void DMA1_Channel2_IRQHandler(void) //Transfer Data Finished Interrupt { if(DMA_GetITStatus(DMA1_IT_TC2)!=RESET) { DMA_ClearITPendingBit(DMA1_IT_TC2); DMA_Cmd(DMA1_Channel2, DISABLE); DMA_Cmd(DMA1_Channel3, DISABLE); DMA1_Channel2->CNDTR = SPI1_ReciveLength; DMA1_Channel3->CNDTR = SPI1_ReciveLength; Parse_Receive_Data(); //Parse the Data Received from DSP } }          DMA的开启工作及传输完成所做的工作在代码中均有体现,不多说了。                  下面说下浮点数据的传输。用C6722就是因为其强大的数据处理能力,所以在传输给STM32的数据中自然缺不了浮点数。         关于浮点数据的存储可以参考下面的链接:http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html         我们以DSP传送浮点数data=8.25到STM32为例,简述下所要做的工作。         DSP方面:若所有传送的数据都要放入Uint16  srcData[  ] 数组中,假设data放入数组最初的位置。浮点数在DSP占据32位,所以要存放在 srcData[ 0 ] 和 srcData[ 1 ] 中.         处理如下:         Uint16  *temp;         temp = (Uint16 *)&data;         srcData[ 0 ] =*temp;         srcData[ 1 ] =*(temp+1);         这样就将一个浮点数存入到了srcData[  ] 数组中。                  STM32方面:SPI的DMA传输完成后,会将数据存放在uint16_t    dstData[   ]数组中,我们要做的是将浮点数data提取出来。假设data传送过来存放到了dstData[ 0 ]和dstData[ 1 ]  中。处理如下:         float  data_copy;         uint16_t  *temp;         temp = (uint16_t  *)&data_copy;         *temp=dstData[ 0 ];         *(temp+1)=dstData[ 1 ];         这样就将接收到的8.25存放到了 data_copy中。注意,千万不能直接这样做:                float  data_copy;         data_copy=dstData[ 1 ]<<16|dstData[ 0 ];         我们知道,8.25存储在内存中是0x41040000,若按上面的方法做,因为dstData[  ]为16位无符号整形,所以直接赋给浮点型后data_copy=0x4E820800(以浮点数的方式存储数值大小为无符号整形0x41040000的数据)。因此定义一个指针,指向data_copy的存储地址,这样就不会出错了。