Davinci GPIO模拟SPI的实现

开发环境:DVEVM 1.2/DVSDK 1.2/SEED DM6446开发板/CCS3.3/F28xx开发板

DM6446的DSP可访问的外设较少,仅包括VICP、EDMA、ASP、2 Timers,而ARM则可访问几乎所有的外设(除了VICP)。如果CODEC算法端需要在运算中直接访问外部DSP就比较困难,我们在实际工作中就遇到了这样的问题。本文中的示例采用GPIO模拟SPI,实现了DM6446的DSP直接与TMS320F28xx通信。

在实现了两块TMS320F28xx开发板通过SPI通信(请参考TMS320F28xx SPI master/slave example )后,在本例中用GPIO模拟SPI的代码就比较容易实现了。

本文中DM6446的示例代码是在DVEVM开发库自带示例scale的基础上修改而来,安装DVEVM 1.2之后,scale相关代码可在<DVEVM>/codec_engine_1_10_01/examples/ 找到。另外需要用到bsl中的几个文件:davincievm.c、davincievm.h、davincievm_gpio.c、davincievm_gpio.h,这些文件在DM6446安装光盘CD1/03.Example of Program/03.Hardwaretest/SEED-Davinci_EVM_testHardware/lib/davincievmbsl/。

关于文中使用的GPIO引脚和PINMUX1寄存器请参考《TMS320DM6446 Digital Media System-on-Chip》中的3.6.2 Multiplexed Pin Configurations和3.6.5 PINMUX1 Register Description。

首先定义使用的GPIO引脚。

#define SPISTE 37 // GPIO37 #define SPICLK 39 // GPIO39 #define SPISOMI 40 // GPIO40 #define SPISIMO 41 // GPIO41

SPI的初始化函数spi_init(),该函数在SCALE_TI_initObj()中调用。

void spi_init() { Int16 rtn; if (curTrace.modName == NULL) { /* initialize GT (tracing) */ GT_create(&curTrace, GTNAME); } GT_0trace(curTrace, GT_ENTER, "spi_init> Enter/n"); // 设置PINMUX1寄存器 pinmux_reg1 = (pinmux_reg1_t *) &PINMUX1; pinmux_reg1->SPI = 0; GT_1trace(curTrace, GT_ENTER, "spi_init> PINMUX1 = 0x%x/n", PINMUX1); // 初始化GPIO rtn = DAVINCIEVM_GPIO_init(); GT_1trace(curTrace, GT_ENTER, "spi_init> DAVINCIEVM_GPIO_init() = %d/n", rtn); // 初始化使用的GPIO引脚 DAVINCIEVM_GPIO_setDirection(SPISTE, GPIO_OUT); DAVINCIEVM_GPIO_setDirection(SPICLK, GPIO_OUT); DAVINCIEVM_GPIO_setDirection(SPISOMI, GPIO_IN); DAVINCIEVM_GPIO_setDirection(SPISIMO, GPIO_OUT); DAVINCIEVM_GPIO_setOutput(SPISTE, 1); // 片选拉高 DAVINCIEVM_GPIO_setOutput(SPICLK, 1); // CLK拉高 DAVINCIEVM_GPIO_setOutput(SPISIMO, 1); // 输出拉高 DAVINCIEVM_waitusec(delay); GT_0trace(curTrace, GT_ENTER, "spi_init> return/n"); }

SPI数据接收发送函数spi_xmit(),该函数通过拉高拉低SPICLK,并配合一定的延时来模拟SPI时钟,达到收发数据的目的。

Uint16 spi_xmit(Uint16 send_data) { Int32 i = 0; Uint16 read_bit = 0; Uint16 read_data = 0; Uint16 mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000}; for (i=15; i>=0; i--) { DAVINCIEVM_GPIO_setOutput(SPICLK, 0); // CLK拉低 DAVINCIEVM_GPIO_setOutput(SPISIMO, ((send_data & mask[i]) >> i)); DAVINCIEVM_waitusec(delay); read_bit = DAVINCIEVM_GPIO_getInput(SPISOMI); if (0 != read_bit) { read_data |= (1<<i); // 依次存高位 } DAVINCIEVM_GPIO_setOutput(SPICLK, 1); // CLK拉高 DAVINCIEVM_waitusec(delay); } return read_data; }

两个设置片选的函数,分别在通信前后调用。

void spi_enable() { DAVINCIEVM_GPIO_setOutput(SPISTE, 1); // 片选拉高 DAVINCIEVM_GPIO_setOutput(SPICLK, 1); // CLK拉高 DAVINCIEVM_GPIO_setOutput(SPISIMO, 1); // 输出拉高 DAVINCIEVM_waitusec(delay); DAVINCIEVM_GPIO_setOutput(SPISTE, 0); // 片选拉低,选中 DAVINCIEVM_waitusec(delay); } void spi_disable() { DAVINCIEVM_GPIO_setOutput(SPISTE, 1); // 片选拉高,清掉 DAVINCIEVM_waitusec(delay); }

延时delay可视需要的波特率设定,测试中我设为10。

Uint32 delay = 10;

spi_task()演示了如何调用以上SPI接口,该函数与TMS320F28xx SPI master/slave example 中的spi_master的spi_task基本一致,只是加入了简单的超时控制。spi_task()在scale的SCALE_TI_process()中调用。

void spi_task() { Uint16 sdata = 0; // send data Uint16 rdata = 0; // received data Uint16 i = 0, j = 0, n = 0; Uint32 loop_count = 0; const Uint16 len = 100; spi_enable(); for (j=0; j<10; j++) { rdata = spi_xmit(SPI_MASTER_XMIT); // 发送数据头 if (rdata == SPI_SLAVE_WAIT) { // 传输数据 for (i=0; i<len; i++) { sdata = ((Uint16*)ai)[i]; rdata = spi_xmit(sdata); } // 等待从机回传处理结果 for (n=0; n<10; n++) { rdata = spi_xmit(SPI_MASTER_WAIT); if (rdata == SPI_SLAVE_XMIT) { rdata = spi_xmit(SPI_MASTER_WAIT); GT_1trace(curTrace, GT_ENTER, "spi_xmit> rdata = %d/n", rdata); goto lable_return; } DAVINCIEVM_waitusec(1000); } } else { GT_1trace(curTrace, GT_ENTER, "spi_xmit> receive error code 0x%x/n", rdata); } loop_count++; GT_1trace(curTrace, GT_ENTER, "spi_xmit> loop_count = %d/n", loop_count); DAVINCIEVM_waitusec(1000); } lable_return: spi_disable(); }

 

你可能感兴趣的:(Davinci GPIO模拟SPI的实现)