#include #include #include "transpose.h" /* This file contains conversion routines that pre-format the brightness data * received from the UART such that the interrupt service routines only need to * push it out the SPI without further computation, making these ISRs nice and * tight. * * To understand this code note the multiplexing scheme used on the board. The * circuit contains two MBI5026 shift-register LED drivers of 16 channels each * cascaded. Effectively this behaves like a 32-channel LED driver fed data * serially. Each output is connected to a single digit's COM pin. All digit's * segment anode pins are connected together in a large bus fed by one of the * two auxiliary shift registers. * * The firmware is selecting each segment in turn with a full BCM cycle for each * segment before the next one is selected. */ /* This array maps the 32 adressable digits on a board to the 32 bits shifted * out to the LED drivers. */ uint8_t digit_map[33] = { 0, 1, 2, 3, 28,29,30,31, 4, 5, 6, 7, 24,25,26,27, 8, 9,10,11, 20,21,22,23, 12,13,14,15, 16,17,18,19 }; /* This function produces a 10-bit output buffer ready for the modulation ISRs * from 10-bit input data encoded for the UART. For the precise data format, see * transpose.h. * * On the UART side we have digits in the order defined in digit_map, 10 byte * per digit. The first 8 bytes are the 8 LSBs of each segments brightness value * in the order [A, B, C, D, E, F, G, DECIMAL_POINT]. The two MSBs to make each * value 10-bit are bit-packed into the remaining two bytes in big-endian byte * order starting from DP. * * On the display frame buffer side, data is stored in multiplexing order: * first digits, then time/bits and finally segments. So for each segment you * have a large buffer containing all the bit periods and digits, and for each * bit period you have 32 bits for all 32 digits. */ void transpose_data(volatile uint8_t *rx_buf, volatile struct framebuf *out_fb) { /* FIXME this can probably be removed. */ memset((uint8_t *)out_fb, 0, sizeof(*out_fb)); /* 8 MSB loop */ struct data_format *rxp = (struct data_format *)rx_buf; for (int bit=0; bit<8; bit++) { /* bits */ uint32_t bit_mask = 1U<frame[bit+2].data; uint8_t *start_inp = rxp->high; for (volatile uint32_t *outp=frame_data; outp> bit << digit_map[digit]; inp += sizeof(struct data_format); } *outp = acc; } } /* 2 packed LSB loop */ for (int bit=0; bit<2; bit++) { /* bits */ volatile uint32_t *frame_data = out_fb->frame[bit].data; for (int seg=0; seg<8; seg++) { /* segments */ uint16_t *inp = &rxp->low; uint32_t mask = 1 << bit << (seg*2); uint32_t acc = 0; for (int digit=0; digit<32; digit++) { acc |= (*inp & mask) >> bit >> (seg*2) << digit_map[digit]; inp += sizeof(struct data_format)/sizeof(uint16_t); } frame_data[seg] = acc; } } /* Global analog brightness value */ out_fb->brightness = ((volatile struct framebuf *)rx_buf)->brightness; } /* This function was used for testing transpose_data. It does precisely the * reverse operation. */ void untranspose_data(struct framebuf *fb, uint8_t *txbuf) { memset(txbuf, 0, sizeof(*fb)); struct data_format *tx = (struct data_format *) txbuf; for (size_t i=0; i<32; i++) { /* digit */ for (size_t j=0; j<8; j++) { /* segment */ for (size_t k=0; k<8; k++) { /* bit */ tx[i].high[j] |= (fb->frame[k+2].data[j] & (1<frame[k].data[j] & (1<