use uDMA for drivings leds
WIP. It is working, but needs some love(tm)
This commit is contained in:
parent
a08935022f
commit
632545e338
1
Makefile
1
Makefile
|
@ -29,6 +29,7 @@ CFLAGS += -DRES_LADDER_ADC_LINE=ADC_LINE\(2\)
|
|||
CFLAGS += -DENABLE_LCD=1
|
||||
|
||||
CFLAGS += -DENABLE_WS2812=1
|
||||
CFLAGS += -DWS2812_DMA=1
|
||||
CFLAGS += -DWS2812_SPI_PORT=$(WS2812_SPI_PORT)
|
||||
|
||||
CFLAGS += -DENABLE_NRF_COMM=1
|
||||
|
|
54
main.c
54
main.c
|
@ -62,6 +62,11 @@ mm545x_t mm545p = {0};
|
|||
|
||||
#if ENABLE_WS2812
|
||||
#include "ws2812.h"
|
||||
|
||||
#if WS2812_DMA
|
||||
#include "ssi_udma.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define TEST_RX_MSG 1
|
||||
|
@ -384,27 +389,47 @@ char ws2812_thread_stack[THREAD_STACKSIZE_MAIN];
|
|||
ws2812_rgb_t kit_leds [8] = {{0}};
|
||||
static char big_buffer[sizeof(kit_leds)*4] = {0};
|
||||
|
||||
#if WS2812_DMA
|
||||
uint8_t dma_done = 0;
|
||||
#endif
|
||||
|
||||
int kit_eye = 3;
|
||||
|
||||
void *ws2812_thread(void *arg){
|
||||
msg_t msg_q[1];
|
||||
int ws2812_loop_period = 1000000;
|
||||
|
||||
ws2812_pid = thread_getpid();
|
||||
|
||||
#if ENABLE_WS2812
|
||||
#if WS2812_DMA
|
||||
ssi_udma_t ssi_udmap;
|
||||
ssi_udma_init(&ssi_udmap, (uint8_t*)big_buffer, sizeof(big_buffer), &dma_done, ws2812_pid);
|
||||
#else
|
||||
ws2812_t ws2812p;
|
||||
ws2812_init(&ws2812p, WS2812_SPI_PORT );
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
msg_init_queue(msg_q, 1);
|
||||
|
||||
ws2812_pid = thread_getpid();
|
||||
msg_t m;
|
||||
|
||||
int current_button_state = 0;
|
||||
int pending_msg = 0;
|
||||
|
||||
#if WS2812_DMA
|
||||
int ready_to_tx = 0;
|
||||
#endif
|
||||
|
||||
while(1){
|
||||
int i;
|
||||
|
||||
#if WS2812_DMA
|
||||
if (msg_receive(&m)){
|
||||
#else
|
||||
if (msg_try_receive(&m) == 1){
|
||||
#endif
|
||||
|
||||
switch(m.type){
|
||||
case WS2812_BUTTON_STATE:
|
||||
if (pending_msg){
|
||||
|
@ -414,11 +439,17 @@ void *ws2812_thread(void *arg){
|
|||
pending_msg = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WS2812_NEW_RFMSG:
|
||||
if (! pending_msg){
|
||||
pending_msg = 1;
|
||||
ws2812_loop_period /= 10;
|
||||
}
|
||||
break;
|
||||
|
||||
case SSI_UDMA_FINISHED:
|
||||
ready_to_tx = 1;
|
||||
// nothing, simply continue
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -438,7 +469,21 @@ void *ws2812_thread(void *arg){
|
|||
kit_leds[i].b = 255 / (1 << abs(i-pos)) ;
|
||||
}
|
||||
}
|
||||
#if ENABLE_WS2812
|
||||
|
||||
#if WS2812_DMA
|
||||
ws2812_fill_rgb(kit_leds, sizeof(kit_leds)/sizeof(ws2812_rgb_t), big_buffer);
|
||||
#else
|
||||
ws2812_write_rgb(&ws2812p, kit_leds, sizeof(kit_leds)/sizeof(ws2812_rgb_t), big_buffer);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if WS2812_DMA
|
||||
if (ready_to_tx){
|
||||
ready_to_tx = 0;
|
||||
ssi_udma_restart_tx(&ssi_udmap);
|
||||
}
|
||||
#endif
|
||||
|
||||
kit_eye++;
|
||||
if (kit_eye == 9){
|
||||
|
@ -448,7 +493,10 @@ void *ws2812_thread(void *arg){
|
|||
} else if (kit_eye == 0){
|
||||
kit_eye++;
|
||||
}
|
||||
|
||||
#if ! WS2812_DMA
|
||||
xtimer_usleep(ws2812_loop_period);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* while (msg_receive(&m)) { */
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* heavily based on njneerW/WS2812_drv project
|
||||
*/
|
||||
|
||||
#include "periph/spi.h"
|
||||
|
||||
#include "ssi_udma.h"
|
||||
#include "thread.h"
|
||||
#include "msg.h"
|
||||
#include "ws2812.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static ssi_udma_t *g_dev = NULL;
|
||||
|
||||
/* static uint8_t *g_pui8DoneVar = NULL; */
|
||||
/* static uint8_t *g_pui8SPIArray; */
|
||||
/* static uint16_t g_ui16SPIArraySize; */
|
||||
static unsigned char ucControlTable[1024] __attribute__ ((aligned(1024)));
|
||||
|
||||
void ssi_udma_restart_tx(ssi_udma_t *dev){
|
||||
ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
|
||||
UDMA_SIZE_8 | UDMA_SRC_INC_8 |
|
||||
UDMA_DST_INC_NONE | UDMA_ARB_8);
|
||||
ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
|
||||
UDMA_MODE_BASIC, dev->data,
|
||||
(void *)(SSI1_BASE + SSI_O_DR),
|
||||
dev->data_len);
|
||||
if(dev->done != NULL)
|
||||
{
|
||||
*dev->done = 1;
|
||||
}
|
||||
ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI1TX);
|
||||
}
|
||||
|
||||
void isr_ssi1(void){
|
||||
unsigned long ulStatus;
|
||||
static unsigned char ucPing = 0;
|
||||
static unsigned char ucZero = 0;
|
||||
|
||||
//
|
||||
// Read the interrupt status of the UART.
|
||||
//
|
||||
ulStatus = ROM_SSIIntStatus(SSI1_BASE, 1);
|
||||
|
||||
//
|
||||
// Clear any pending status, even though there should be none since no UART
|
||||
// interrupts were enabled. If UART error interrupts were enabled, then
|
||||
// those interrupts could occur here and should be handled. Since uDMA is
|
||||
// used for both the RX and TX, then neither of those interrupts should be
|
||||
// enabled.
|
||||
//
|
||||
ROM_SSIIntClear(SSI1_BASE, ulStatus);
|
||||
|
||||
//
|
||||
// If the UART0 DMA TX channel is disabled, that means the TX DMA transfer
|
||||
// is done.
|
||||
//
|
||||
if(!ROM_uDMAChannelIsEnabled(UDMA_CHANNEL_SSI1TX))
|
||||
{
|
||||
|
||||
//
|
||||
// Start another DMA transfer to UART0 TX.
|
||||
//
|
||||
if(ucPing)
|
||||
{
|
||||
ucPing = 0;
|
||||
if (g_dev->listener != KERNEL_PID_UNDEF){
|
||||
msg_t m;
|
||||
m.type = SSI_UDMA_FINISHED;
|
||||
msg_send_int(&m, g_dev->listener);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Send out enough zero's to inform the LEDs that the previous
|
||||
// message is complete. uDMA is a bit overkill for this... meh.
|
||||
//
|
||||
ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
|
||||
UDMA_SIZE_8 | UDMA_SRC_INC_NONE |
|
||||
UDMA_DST_INC_NONE | UDMA_ARB_8);
|
||||
ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
|
||||
UDMA_MODE_BASIC, &ucZero,
|
||||
(void *)(SSI1_BASE + SSI_O_DR),
|
||||
2);
|
||||
ucPing = 1;
|
||||
}
|
||||
//
|
||||
// The uDMA TX channel must be re-enabled.
|
||||
//
|
||||
ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI1TX);
|
||||
}
|
||||
}
|
||||
|
||||
void isr_udma_error(void)
|
||||
{
|
||||
unsigned long ulStatus;
|
||||
|
||||
//
|
||||
// Check for uDMA error bit
|
||||
//
|
||||
ulStatus = ROM_uDMAErrorStatusGet();
|
||||
|
||||
//
|
||||
// If there is a uDMA error, then clear the error and continue. If we're
|
||||
// still debugging our project, we want to infinte loop here so we can
|
||||
// investiage the failure cause.
|
||||
//
|
||||
if(ulStatus)
|
||||
{
|
||||
ROM_uDMAErrorStatusClear();
|
||||
//while(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ssi_udma_register(ssi_udma_t *dev, unsigned int pid){
|
||||
dev->listener = pid;
|
||||
}
|
||||
|
||||
void
|
||||
ssi_udma_init(ssi_udma_t *dev, uint8_t *pui8SPIData, uint16_t ui16DataSize,
|
||||
uint8_t *pui8DoneVar, unsigned int pid)
|
||||
{
|
||||
int i;
|
||||
dev->done = pui8DoneVar;
|
||||
dev->data = pui8SPIData;
|
||||
dev->data_len = ui16DataSize;
|
||||
if (pid > 0)
|
||||
dev->listener = pid;
|
||||
|
||||
g_dev = dev;
|
||||
//
|
||||
// zero out SPI data array
|
||||
//
|
||||
for(i=0;i<ui16DataSize;i++)
|
||||
{
|
||||
// FIXME
|
||||
//WSArrayInit(pui8SPIData, ui16DataSize);
|
||||
}
|
||||
|
||||
//
|
||||
// Enable the uDMA controller at the system level. Enable it to continue
|
||||
// to run while the processor is in sleep.
|
||||
//
|
||||
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
|
||||
ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
|
||||
|
||||
//
|
||||
// Enable the uDMA controller error interrupt. This interrupt will occur
|
||||
// if there is a bus error during a transfer.
|
||||
//
|
||||
ROM_IntEnable(INT_UDMAERR);
|
||||
|
||||
//
|
||||
// Enable the uDMA controller.
|
||||
//
|
||||
ROM_uDMAEnable();
|
||||
|
||||
//
|
||||
// Point at the control table to use for channel control structures.
|
||||
//
|
||||
ROM_uDMAControlBaseSet(ucControlTable);
|
||||
|
||||
//
|
||||
// Enable the SPI peripheral, and configure it to operate even if the CPU
|
||||
// is in sleep.
|
||||
//
|
||||
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
|
||||
ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_SSI1);
|
||||
|
||||
//
|
||||
// Set PF1 to SSI1TX
|
||||
//
|
||||
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
|
||||
ROM_GPIOPinConfigure(GPIO_PF1_SSI1TX);
|
||||
ROM_GPIOPinTypeSSI(GPIO_PORTF_BASE, GPIO_PIN_1);
|
||||
|
||||
//
|
||||
// Configure the SPI communication parameters. We could use 4 instead of
|
||||
// 8 here and be more memory efficient (as each spi bit is encoded into 4
|
||||
// bits on the SPI line), but the uDMA only allows for 8 bit increment of
|
||||
// source data
|
||||
//
|
||||
ROM_SSIConfigSetExpClk(SSI1_BASE, ROM_SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
|
||||
SSI_MODE_MASTER, 2500000, 8);
|
||||
|
||||
//
|
||||
// Enable the SSI for operation, and enable the uDMA interface for both the
|
||||
// TX channel
|
||||
//
|
||||
ROM_SSIEnable(SSI1_BASE);
|
||||
ROM_SSIDMAEnable(SSI1_BASE, SSI_DMA_TX);
|
||||
|
||||
//
|
||||
// Enable the SSI peripheral interrupts. Note that no SSI interrupts
|
||||
// were enabled, but the uDMA controller will cause an interrupt on the
|
||||
// SSI interrupt signal when a uDMA transfer is complete. uDMA interrupt
|
||||
// handler is really only used if something goes horribly wrong.
|
||||
//
|
||||
ROM_IntEnable(INT_SSI1);
|
||||
|
||||
//
|
||||
// Put the attributes in a known state for the uDMA SSI1TX channel. These
|
||||
// should already be disabled by default.
|
||||
//
|
||||
ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI1TX,
|
||||
UDMA_ATTR_ALTSELECT |
|
||||
UDMA_ATTR_HIGH_PRIORITY |
|
||||
UDMA_ATTR_REQMASK);
|
||||
|
||||
//
|
||||
// Configure the control parameters for the SSI TX. The uDMA SSI TX
|
||||
// channel is used to transfer a block of data from a buffer to the periph.
|
||||
// The data size is 8 bits. The source address increment is 8-bit bytes
|
||||
// since the data is coming from a uint8_t buffer. The destination
|
||||
// increment is none since the data is to be written to the SSI data
|
||||
// register. The arbitration size is set to 4, which doesn't matter much
|
||||
// since we're only running one uDMA transfer. If we were running multiple
|
||||
// transfers through the uDMA engine, we would want to increase this enough
|
||||
// to insure that the full LED strip worth of data goes out before the uDMA
|
||||
// engine services another channel.
|
||||
// TODO: see last sentence.
|
||||
//
|
||||
ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
|
||||
UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
|
||||
UDMA_ARB_8);
|
||||
|
||||
//
|
||||
// Set up the transfer parameters for the uDMA SSI TX channel. This will
|
||||
// configure the transfer source and destination and the transfer size.
|
||||
// Basic mode is used because the peripheral is making the uDMA transfer
|
||||
// request. The source is the TX buffer and the destination is the SSI
|
||||
// data register.
|
||||
//
|
||||
ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI1TX | UDMA_PRI_SELECT,
|
||||
UDMA_MODE_BASIC, pui8SPIData,
|
||||
(void *)(SSI1_BASE + SSI_O_DR),
|
||||
ui16DataSize);
|
||||
|
||||
*pui8DoneVar = 0;
|
||||
|
||||
//
|
||||
// Now both the uDMA SSI TX and channel is primed to start a transfer. As
|
||||
// soon as the channel is enabled, the peripheral will issue a transfer
|
||||
// request and the data transfers will begin.
|
||||
//
|
||||
ROM_IntMasterEnable();
|
||||
ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI1TX);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#define SSI_UDMA_FINISHED 2
|
||||
|
||||
typedef struct {
|
||||
unsigned int listener;
|
||||
unsigned int state;
|
||||
uint8_t *data;
|
||||
uint16_t data_len;
|
||||
uint8_t *done;
|
||||
} ssi_udma_t;
|
||||
|
||||
void ssi_udma_restart_tx(ssi_udma_t *dev);
|
||||
void ssi_udma_register(ssi_udma_t *dev, unsigned int pid);
|
||||
|
||||
void ssi_udma_init(ssi_udma_t *dev, uint8_t *pui8SPIData, uint16_t ui16DataSize,
|
||||
uint8_t *pui8DoneVar, unsigned int pid);
|
8
ws2812.c
8
ws2812.c
|
@ -42,8 +42,8 @@ int ws2812_write(ws2812_t *dev, char *b, unsigned len) {
|
|||
#define W01 0x8E
|
||||
#define W10 0xE8
|
||||
#define W11 0xEE
|
||||
|
||||
int ws2812_write_rgb(ws2812_t *dev, ws2812_rgb_t *leds, unsigned len, char* buffer){
|
||||
|
||||
void ws2812_fill_rgb(ws2812_rgb_t *leds, unsigned len, char* buffer){
|
||||
int out_count = 0;
|
||||
char *bytes = (char*)leds;
|
||||
int led_idx;
|
||||
|
@ -77,5 +77,9 @@ int ws2812_write_rgb(ws2812_t *dev, ws2812_rgb_t *leds, unsigned len, char* buff
|
|||
out_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ws2812_write_rgb(ws2812_t *dev, ws2812_rgb_t *leds, unsigned len, char* buffer){
|
||||
ws2812_fill_rgb(leds, len, buffer);
|
||||
return ws2812_write(dev, buffer, len*sizeof(ws2812_rgb_t)*8/2);
|
||||
}
|
||||
|
|
1
ws2812.h
1
ws2812.h
|
@ -17,3 +17,4 @@ typedef struct __attribute__((packed)) {
|
|||
int ws2812_init(ws2812_t *dev, spi_t spi);
|
||||
int ws2812_write(ws2812_t *dev, char *b, unsigned len);
|
||||
int ws2812_write_rgb(ws2812_t *dev, ws2812_rgb_t *leds, unsigned len, char* buffer);
|
||||
void ws2812_fill_rgb(ws2812_rgb_t *leds, unsigned len, char* buffer);
|
||||
|
|
Loading…
Reference in New Issue