硬件环境: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的存储地址,这样就不会出错了。