// Copyrighted Mattias Ekholm /* TODO: * Add SPI slave on second USART for RaspberryPi connectivity */ #ifndef __SDCC_mcs51 #error Only tested on the Wixel #endif #include #include #include #include #include #include #include /* Bit size for the WS2812 is 1.25us +/- 600ns * 0-bit: H: 350ns +/- 150ns L: 800ns +/- 150us = 417H + 417L + 417L = 100 * 1-bit: HL 700ns +/- 160ns L: 600ns +/- 150us = 417H + 417H + 417L = 110 * Reset: L for > 50us * 3MHz -> 333ns per bit * 1250ns / 3 -> 417 ns per "part" * Baud rate 15 17 -> 800000Hz * Baud rate 16 153 (or 154) 2400000Hz */ /* 3 bits require more CPU cycles but preservers some memory * 4 bits is faster on CPU cycles but waste some memory * 3 bit version is possible to optimize... */ #define N_WS2812_BITS 4 // we use 3 bits for representation of WS2812 data #define N_COLOR_BITS 8 // number of bits per color #define N_DIODS 240 // total number of diods in array #define N_COLORS 3 // number of colors per diod /* Each bit represented on the wixel data format requires N_BYTES of data for * internal representation on Wizel */ #define N_BYTES (N_DIODS * N_COLORS * N_WS2812_BITS) // * N_COLORS_BITS / 8 #define WS2812_RESET_LENGTH 15 // 50 / (8 * 1.25 / N_WS2812) // The wixel uses a different color format than "usually" #define R_POS 1 #define G_POS 0 #define B_POS 2 // bit pattern that represents the WS2812 bits #if N_WS2812_BITS == 3 #define WS2812_0 0b100 #define WS2812_1 0b110 #else #define WS2812_0 0b1000 #define WS2812_1 0b1100 #endif /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * DMA Data structures * we use this "more" convenient structure for DMA configuration */ typedef struct DmaConfig DmaConfig; #define DMA_ADDR(a) (((uint16_t) (a) << 8) | ((uint16_t) (a) >> 8)) enum { DMA_TrigNONE = 0, DMA_TrigPREV = 1, DMA_TrigT1_CH0 = 2, DMA_TrigT1_CH1, DMA_TrigT1_CH2, DMA_TrigT2_OVFL = 6, DMA_TrigT3_CH0 = 7, DMA_TrigT3_CH1, DMA_TrigT4_CH0, DMA_TrigT4_CH1, DMA_TrigIOC_0 = 12, DMA_TrigIOC_1, DMA_TrigURX0 = 14, DMA_TrigUTX0, DMA_TrigURX1, DMA_TrigUTX1, DMA_TrigFLASH = 18, DMA_TrigRADIO = 19, DMA_TrigADC_CHALL = 20, DMA_TrigADC_CH0, DMA_TrigADC_CH1, DMA_TrigADC_CH2, DMA_TrigADC_CH3, DMA_TrigADC_CH4, DMA_TrigADC_CH5, DMA_TrigADC_CH6, DMA_TrigADC_CH7, DMA_TrigENC_DW = 29, DMA_TrigENC_UP = 30, }; enum { DMA_Inc0 = 0b00, // Incremented by 0 DMA_Inc1 = 0b01, // Incremented by 1 DMA_Inc2 = 0b10, // Incremented by 2 DMA_IncN1 = 0b11, // Incremented by -1 }; enum { DMA_LengthVLEN = 0b000, // Length is LENL and LENH DMA_LengthN0 = 0b010, // First byte + 0 DMA_LengthN1 = 0b001, // First byte + 1 DMA_LengthN2 = 0b011, // First byte + 2 DMA_LengthN3 = 0b100, // First byte + 3 }; enum { DMA_PrioLOW = 0, DMA_PrioNormal = 1, DMA_PrioHigh = 2, }; enum { DMA_TransSingle = 0b00, DMA_TransBlock = 0b01, DMA_TransRepeatSingle = 0b10, DMA_TransRepeatBlock = 0b11, }; // Note the reverse order of the bitfields in each byte! struct DmaConfig { unsigned SRCADDR : 16; unsigned DESTADDR : 16; unsigned LENH : 5; unsigned VLEN : 3; unsigned LENL : 8; unsigned TRIG : 5; unsigned TMODE : 2; unsigned WORDSIZE : 1; unsigned PRIORITY : 2; unsigned M8 : 1; unsigned IRQMASK : 1; unsigned DESTINC : 2; unsigned SRCINC : 2; }; /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * Our data for DMA transfers */ static const int DMA_Channel = 0b00000001; // Channel 0 uint8_t XDATA dmaTxPacket[N_BYTES + WS2812_RESET_LENGTH]; /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */ // uint32_t XDATA colors[N_DIODS]; // just for making it easier to add double buffering some day... #define ws2812 dmaTxPacket /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * Color update code */ #if N_WS2812_BITS == 3 #define setBits(pos, val) \ do { \ int bit; \ \ for (int i = 0; i < N_WS2812_BITS; ++i) { \ bit = ((pos) * N_WS2812_BITS) + i; \ ws2812[bit >> 3] &= ~(1 << (7 - (bit & 0x7))); \ ws2812[bit >> 3] |= !!((val) & (1 << (N_WS2812_BITS - 1 - i))) << (7 - (bit & 0x7)); \ } \ } while (0) #else #define setBits(pos, val) \ do { \ ws2812[(pos) >> 1] &= ~(0xf << 4 * (1 - ((pos) & 1))); \ ws2812[(pos) >> 1] |= (val) << 4 * (1 - ((pos) & 1)); \ } while(0) #endif /* Function that update the color for the given diod */ void setDiod( uint8_t diod, uint32_t color) // RGB { uint32_t pos; uint8_t val; int i; for (i = 0; i < 8; ++i) { pos = ((diod * N_COLORS + R_POS) * N_COLOR_BITS + i); val = (color & (0x800000 >> i)) ? WS2812_1 : WS2812_0; setBits(pos, val); pos = ((diod * N_COLORS + G_POS) * N_COLOR_BITS + i); val = (color & (0x008000 >> i)) ? WS2812_1 : WS2812_0; setBits(pos, val); pos = ((diod * N_COLORS + B_POS) * N_COLOR_BITS + i); val = (color & (0x000080 >> i)) ? WS2812_1 : WS2812_0; setBits(pos, val); } } /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */ void init(void) { int i; static DmaConfig XDATA dma0Config; // configure USART0 P0SEL |= 0b00101100; // set pins to peripheral function U0GCR |= 0b00100000; // chip error, can't align clock to bit start U0CSR &= ~0xa0; // Baud rate, need to be tuned for exact frequency, every wixel is not the same :) #if N_WS2812_BITS == 3 U0GCR |= 16; U0BAUD = 155; #else U0GCR |= 17; U0BAUD = 21; #endif DMA0CFG = (uint16)&dma0Config; /* Set up DMA to use USART Tx complete as trigger and send one byte * from the buffer on each trigger. */ memset(&dma0Config, 0, sizeof(dma0Config)); dma0Config.SRCADDR = DMA_ADDR(dmaTxPacket); dma0Config.DESTADDR = DMA_ADDR(XDATA_SFR_ADDRESS(U0DBUF)); dma0Config.LENH = (sizeof(dmaTxPacket) >> 8) & 0x0f; dma0Config.LENL = (sizeof(dmaTxPacket) >> 0) & 0xff; dma0Config.TMODE = DMA_TransSingle; dma0Config.TRIG = DMA_TrigUTX0; dma0Config.SRCINC = DMA_Inc1; dma0Config.DESTINC = DMA_Inc0; dma0Config.PRIORITY = DMA_PrioHigh; U0DBUF = 0x0; /* Set all Diods to off state */ for (i = 0; i < N_DIODS; ++i) { setDiod(i, 0); } } /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */ /* This is just some silly demo pattern * Note that we use RGB format here while Wixel uses GRB */ static uint8_t XDATA d[] = {N_DIODS / 8, N_DIODS / 7, N_DIODS / 6, N_DIODS / 5, N_DIODS / 4, N_DIODS / 3, N_DIODS / 2}; static uint32_t XDATA c[] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xffff00, 0xff00ff, 0x00ffff}; static uint32_t XDATA m[] = {0x010101, 0x0f0f0f, 0x2a2a2a, 0xffffff, 0x2a2a2a, 0x010101}; void service(void) { int i; int j; // Wait for DMA transfer to complete while (DMAARM & DMA_Channel) { // If yellow diod toggle we are forced to wait on DMA transfer to complete // an easy way to notice that the inter frame delay is too short LED_YELLOW_TOGGLE(); return; } // We flash the red led when we initiates a DMA transfer LED_RED(true); // Update the diods with the demo pattern for (i = 0; i < sizeof(d); ++i) { for (j = 0; j < sizeof(m) / sizeof(*m); ++j) { setDiod(d[i] + j, c[i] & m[j]); } d[i] = (d[i] + 1) % (N_DIODS - sizeof(m) / sizeof(*m)); } // Initiate the next DMA transfer DMAARM = DMA_Channel; DMAREQ = DMA_Channel; LED_RED(false); } /* ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */ void main(void) { static int32_t currMs; uint32_t lastMs = 0; systemInit(); usbInit(); radioQueueInit(); init(); service(); while(1) { currMs = getMs(); boardService(); usbComService(); // for 240 diods // if 3 bits per bit 105 ms ~ 10 Hz // if 4 bits per bit 26 ms ~ 38 hz if (lastMs + 40 < currMs) { service(); lastMs = currMs; } } }