SPI Interface - some nice addition to my Drive Project

SPI Interface - some nice addition to my Drive Project

This is an example of SPI Interface configuration and usage based on TI F28069 Launchpad.

I managed to tune the SPI to make it work. Initial idea was to talk to a Driver chip on an option “power” board, but after assessing its scheme, I found that all 4 channels of SPI are dead-short to ground/supply, which makes the driver totally unreachable through SPI. So, I simply looped MDO (Master Data Out) pin of the Microcontroller (MC) to it’s MDI pin, thus shifting bits from output register to the input register. This is effectively identical to talking to another device, provided it is configured properly, and I used SPI configuration and Data structure matching those of driver chip as per it’s datasheet.


You can find working file in my GitHub, along the rest of my pet project of controlled PWM 3 phase voltage source.

SPI operation principle

SPI is for Synchronous Peripheral Interface, which is one of the easiest types of communication protocols, mostly used for a fast short-range serial communication between on-board chips and peripheral devices. Despite very simple structure, SPI allows very fast full-duplex communication, which allows quite significant amount of information to be transmitted between on-board devices, mostly for diagnosis, fault-reporting and configuration purposes.

The principle is based on a shifting register: the data to be transmitted by Master M is put to a data register, from which on each clock signal tick the data is shifted out of this register, generally MSB out first, bit-by-bit. When the specified number of bits (character length 1-16) is shifted out, the data register is cleared and ready for load of a next character.

Simultaneously with shifting data out from the left (MSB out first), response data from the slave comes in from the right, taking place of just shifted bits. Thus, in the end of a character transmission, the data register is filled with input character of the same length.

Then, after the character was fully received, the in-data is latched to the RX BUFfer register.

In general case the length of the character is less then the size of the data register (16 bits), so not-used bits shall be masked out. Due to MSB comes out first, the out data shall be left-justified. And respectively, the input data will be right-justified (keeping the normal bit order), so MSBs shall be masked out.

In my example the MDO pin was connected to MDI pin, so the data coming in matches the one coming out. Also, the DRV8301 driver chip uses 16-bit character, which is quite convenient due to we shall not mask any bits – full register is used.


Apart from 2 information channels MDO and MDI essential part of SPI are clock SPI CLK and chip select SPI CS signals.

There are 3 stages of initiating SPI transmission in TI processor:

-????????? Switch on CLK to SPI module in the processor core – this is done during initial CPU configuration, as part of overall peripheral CLK initialization (all periphery is activated due to power savings of processor via selective activation of periphery is not relevant for drive application)

-????????? Switch ON SPI operation – via a bit strange method of sending NON-Reset command to a corresponding register: SpiaRegs.SPICCR.bit.SPISWRESET? = 0; (so while SPI is non-reseted it is active, and in case of =1, then it is in permanent reset and not working – not very intuitive as for me, but it is as it is in TI).

-????????? Start the actual SPI transmission mode: SpiaRegs.SPICTL.bit.TALK = 1;

The last command makes SPI to take care for the CLK and CS signals automatically. But if we look under the lead, we’ll see that next steps are:

-????????? Activate /CS signal (active load) – slave gets ready to receive the first bit after first clock

-????????? Send the first clock up, then down, while setting MDO bit ready to transmission.

Actual CLK fronts and setting/latching of MDO/MDI bits depend on CLK_PHASE and CLKPOLARITY. Each of them has 2 variants, thus forming 4 possible combinations. In my case DRV8301 manual specifies next CLK and SDO/SDI combinations:


So, setting of SDO bit occurs after rising edge of SCLK, while latching of SDI bit occurs during falling edge of SCLK. We just need to select the right combination of phase and polarity for the master to match this setup in a key-to-lock style. In this MC this is achieved with SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; and SpiaRegs.SPICTL.bit.CLK_PHASE?? = 0;

Also, we shall ensure that all the timings specified in the slave manual are kept in order to ensure that all transient processes will finish and the logical value will be fixed in steady state long enough before and after it’s latching in the receiving device. Some time shall pass after activation of the /CS signal before the first CLK as well.

So, accounting all of the above, we come to next edges and timings configuration:

SPICLK Up

-????????? SDO ready +20ns after SPICLK Up till SPICLK Down +40ns

-????????? MDO ready +10ns after SPICLK Up till SPICLK Down +78ns

SPICLK Down

-????????? MI latches SDO -26ns to SPICLK Down

-????????? SI latches MDO -20ns +30ns to SPICLK Down

As you can see, minimum times for keeping valid signals are easily met even at the fastest possible SPI CLK rate, which is LSPCLK / 4, where LSPCLK itself is SYSCLKOUT / 4, i.e. the main SYSCLK frequency of MC divided by 4 x 4 = 16, namely 90 MHz / 16 = 5.625 MHz = 176 ns for full clock period and 88 ns for clock being kept up or down. This is around twice the minimum allowable cycle time of DRV8301 SPI slave.

FIFO and Buffer operation

SPI has a RX BUF and TX BUF registers, which fill/latch data to/from SPI DAT register, which is the actual shift register from which/to which the information transmits.

Writing to TX BUF initiates the transmission, so the data immediately falls down to SPI DAT.

In addition to TX/RX buff there is also 4-level TX/RX FIFO registers. In our example it’s really easy to transmit a single character in just 1 word, so FIFO is not used.

Interrupts

When the character is fully transmitted, MC raises an Interrupt INT flag, which helps to identify that the data is ready to be served, and also initiates an interrupt, provided it’s activated in the corresponding group, and the interrupt group itself is activated. Also, you need to enable INT in the SPI itself:

SpiaRegs.SPICTL.bit.SPIINTENA?? = 1;

PieCtrlRegs.PIEIER6.bit.INTx1 = 1;???? //Enable SPI INT SPIRXA

IER |= M_INT6; //Enable Group 6 Interrupts – SPI

PieVectTable.SPIRXINTA???? = &SpiaINTHandler;????? //SPI Interrupt handler

So, when the INT_FLG is raised, the interrupt occurs and the interrupt handler serves the data. Reading the RX BUF erases SPI INT FLAG. We also shall stop the transmission by reseting TALK register.

Data being transmitted over SPI can be of whatever meaning. In this case data structure is as follows:

0b1 0001 00000000000, where

1 – Read data

0001 – Register address (slave status register)

00000000000 – dummy data, not used for read command

?

Periphery init

In order to make SPI work you shall just set appropriate value for corresponding GPIO MUX, then all the behavior of the /CS , MDI, MDO and CLK pins will be managed by SPI sub core of the MC. The only thing to put attention to is the necessity of a pullup, which depends on the slave configuration (need to check the manual), and also don’t forget to switch off the qualification, which is not used in peripheral mode of a GPIO pin (it’s only used in I/O mode, but by default is active, so requires explicit deactivation).

The full GPIO config is pretty simple and is shown below:

//GPIO config

??? GpioCtrlRegs.GPAPUD.bit.GPIO19? = 1;??? // SPISTEA Pull-up off

??? GpioCtrlRegs.GPAPUD.bit.GPIO18? = 1;??? // SPICLKA Pull-up off

??? GpioCtrlRegs.GPAPUD.bit.GPIO17? = 1;??? // SPISOMIA Pull-up off

??? GpioCtrlRegs.GPAPUD.bit.GPIO16? = 1;??? // SPISIMOA Pull-up off

?

??? GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 1;??? // /SPISTEA

??? GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 1;??? // SPICLKA

??? GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1;??? // SPISOMIA

??? GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1;??? // SPISIMOA

?

??? //Qualification off - async mode for peripheral control

??? GpioCtrlRegs.GPAQSEL2.bit.GPIO19 = 3; // Asynch input GPIO19 SPISTEA

??? GpioCtrlRegs.GPAQSEL2.bit.GPIO18 = 3; // Asynch input GPIO18 SPICLKA

??? GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3; // Asynch input GPIO17 SPISOMIA

??? GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3; // Asynch input GPIO16 SPISIMOA

?

Summary

?

I managed to tune the fully working SPI communication. Unfortunately, talking to an on-board SPI Slave was not physically possible due to circuitry that pulls down all SPI pins of the slave, but the operation from Master out to Master in works properly. All timings match, form of latching the data bits and interaction of latches and clk edges were carefully studied, as well as appropriate pin configuration and data structure.

SPI is a very nice and easy communication protocol, but also has its own nuances that require attention and careful configuration.

要查看或添加评论,请登录

Stanislav Petrashek的更多文章

社区洞察

其他会员也浏览了