Dear community,
i am using the TM4C1231 Tiva part, and i'm faced with a tricky situation for that i'm hoping for some of your advise. I am working with a PWM LED driver part, that uses a serial SPI-like communication scheme to transmit the PWM/grayscale data to it. However, this part seems to be designed to be used rather with programmable hardware than with a microcontroller.
In it's comminication scheme, 16-bit data is transmitted synchronously over two wires, CLK and DATA in a SSI/SPI fashion. However, a third control line is used to signal commands to the driver IC, such as latching data, enabling/disabling outputs and more. This control line is also synchronized to the CLK signal, and the number of CLK pulses it goes high after determines the command. (For more information feel free to look at the LED1642 datasheet).
My only solution so far to support this synchronous transmission scheme is to use soft-SPI plus the control line, which has the two obvious downsides: 1. The speed is low, 2. The CPU is constantly busy transmitting display data.
Initially I was hoping to get the serial communication to be performed by the uDMA module in the background, but even using SPI without uDMA doesn't work because of the control line scheme of the driver IC. So i was hoping to synchronize two SPI channels together using uDMA, using two buffers, the one containing the actual PWM/grayscale data, transferred over SSI0/uDMA, and the other containing the corresponding control command data, transferred over SSI2/uDMA. However i already noticed, that the SSI transmissions don't start synchronously, there seems to be a gap of around 1 clock cycle between the starts. (Unfortunately, i don't have precise measurement equipment - a 24MHz logic analyzer is all i have, yet).
So having the uDMA controller synchronize the transmissions doesn't seem to work, unless i would have a guaranteed delay between the starts of the SSI transmission. (In this case, i could calculate the shifts into my control-line-buffer). But i doubt that there is a guarantee that can be made.
Is there anything else i can to to synchronize this third control line into the SPI transmission, that is performed via uDMA? Can you guys think of anything, except having to find another PWM driver and re-design?
Appending also the code that i'm using to try out the parallel SSI transmission.
Thanks,
Janos
#include <stdbool.h> #include <stdint.h> #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_ssi.h" #include "inc/hw_udma.h" #include "driverlib/pin_map.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/udma.h" #include "driverlib/gpio.h" #include "driverlib/ssi.h" #define MEM_BUFFER_SIZE 16 uint8_t ui8ControlTable[1024] __attribute__ ((aligned(1024))); static uint8_t bufferSSI0[MEM_BUFFER_SIZE]; static uint8_t bufferSSI2[MEM_BUFFER_SIZE]; #define SPI_CLK_SPEED_Hz 12000000 void SSI0_Init(void) { SysCtlPeripheralEnable(PERIPH(SSI0)); SysCtlPeripheralEnable(PERIPH_GPIO(A)); GPIOPinConfigure(GPIO_PA2_SSI0CLK); GPIOPinConfigure(GPIO_PA5_SSI0TX); GPIOPinTypeSSI(PORT(A), PIN(5) | PIN(2)); SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, SPI_CLK_SPEED_Hz, 8); SSIEnable(SSI0_BASE); uint32_t tmp; while (SSIDataGetNonBlocking(SSI0_BASE, &tmp)) { } } void SSI2_Init(void) { SysCtlPeripheralEnable(PERIPH(SSI2)); SysCtlPeripheralEnable(PERIPH_GPIO(B)); GPIOPinConfigure(GPIO_PB4_SSI2CLK); GPIOPinConfigure(GPIO_PB7_SSI2TX); GPIOPinTypeSSI(PORT(B), PIN(7) | PIN(4)); SSIConfigSetExpClk(SSI2_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, SPI_CLK_SPEED_Hz, 8); SSIEnable(SSI2_BASE); uint32_t tmp; while (SSIDataGetNonBlocking(SSI2_BASE, &tmp)) { } } void SSI0_uDMA_Enable(uint8_t *source, uint32_t len) { SSIDMAEnable(SSI0_BASE, SSI_DMA_TX); uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0TX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI0TX, UDMA_ATTR_USEBURST); uDMAChannelControlSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, source, (void *)(SSI0_BASE + SSI_O_DR), len); } void SSI2_uDMA_Enable(uint8_t *source, uint32_t len) { SSIDMAEnable(SSI2_BASE, SSI_DMA_TX); uDMAChannelAssign(UDMA_CH13_SSI2TX); uDMAChannelAttributeDisable(UDMA_CH13_SSI2TX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); uDMAChannelAttributeEnable(UDMA_CH13_SSI2TX, UDMA_ATTR_USEBURST); uDMAChannelControlSet(UDMA_CH13_SSI2TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); uDMAChannelTransferSet(UDMA_CH13_SSI2TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, source, (void *)(SSI2_BASE + SSI_O_DR), len); } int main(void) { SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_INT | SYSCTL_XTAL_16MHZ); unsigned int i; for (i = 0; i < MEM_BUFFER_SIZE; i++) { bufferSSI0[i] = i; bufferSSI2[i] = MEM_BUFFER_SIZE - i; } SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA); uDMAEnable(); uDMAControlBaseSet(ui8ControlTable); SysCtlPeripheralEnable(PERIPH_GPIO(A)); GPIOPinTypeGPIOOutput(PORT(A), PIN(3)); GPIOPinWrite(PORT(A), PIN(3), 0); SSI0_Init(); SSI2_Init(); SSI0_uDMA_Enable(bufferSSI0, MEM_BUFFER_SIZE); SSI2_uDMA_Enable(bufferSSI2, MEM_BUFFER_SIZE); uDMAChannelEnable(UDMA_CHANNEL_SSI0TX); uDMAChannelEnable(UDMA_CH13_SSI2TX); while (1) { } }