beta with artnet protocol

This commit is contained in:
Tropicananass 2021-05-01 00:06:38 +01:00
parent a01770282d
commit e8b99665be
8 changed files with 471 additions and 257 deletions

149
RpiLedBars/.clang-format Normal file
View File

@ -0,0 +1,149 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
...

2
RpiLedBars/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
bin
obj

31
RpiLedBars/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,31 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/pixled",
"args": [
"-n", "5",
// "-t"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "${workspaceFolder}/sgdb.sh"
}
]
}

View File

@ -2,7 +2,7 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"type": "cppbuild", "type": "shell",
"label": "Build project", "label": "Build project",
"command": "make", "command": "make",
"options": { "options": {
@ -15,6 +15,10 @@
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
}, },
"presentation": {
"reveal": "always",
"panel": "new"
},
"detail": "compiler: /usr/bin/gcc" "detail": "compiler: /usr/bin/gcc"
}, },
{ {

View File

@ -1,5 +1,5 @@
CC=gcc CC=gcc
CFLAGS=-Wall -g -DDEBUG CFLAGS=-Wall -g #-DDEBUG
LDFLAGS=#-lpthread LDFLAGS=#-lpthread
SRCDIR=src SRCDIR=src
OBJDIR=obj OBJDIR=obj
@ -8,7 +8,7 @@ SRC=$(notdir $(wildcard $(SRCDIR)/*.c))
OBJ=$(SRC:.c=.o) OBJ=$(SRC:.c=.o)
BIN=pixled BIN=pixled
all: $(addprefix $(BINDIR)/, $(BIN)) all: clean $(addprefix $(BINDIR)/, $(BIN))
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(OBJDIR)/%.o: $(SRCDIR)/%.c
if [ ! -d $(OBJDIR) ]; then mkdir "$(OBJDIR)"; fi if [ ! -d $(OBJDIR) ]; then mkdir "$(OBJDIR)"; fi
@ -20,4 +20,5 @@ $(BINDIR)/$(BIN) : $(addprefix $(OBJDIR)/, $(OBJ))
clean: clean:
rm -rf $(BINDIR)/* $(OBJDIR)/* rm -rf $(BINDIR)/* $(OBJDIR)/*
rmdir $(BINDIR) $(OBJDIR) if [ -d $(OBJDIR) ]; then rmdir "$(OBJDIR)"; fi
if [ -d "$(BINDIR)" ]; then rmdir "$(BINDIR)"; fi

3
RpiLedBars/sgdb.sh Executable file
View File

@ -0,0 +1,3 @@
#! /bin/bash
sudo /usr/bin/gdb "$@"

View File

@ -16,7 +16,7 @@
// //
// Location of peripheral registers in physical memory // Location of peripheral registers in physical memory
#define PHYS_REG_BASE PI_01_REG_BASE #define PHYS_REG_BASE PI_23_REG_BASE
#define PI_01_REG_BASE 0x20000000 // Pi Zero or 1 #define PI_01_REG_BASE 0x20000000 // Pi Zero or 1
#define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3 #define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3
#define PI_4_REG_BASE 0xFE000000 // Pi 4 #define PI_4_REG_BASE 0xFE000000 // Pi 4

View File

@ -30,42 +30,45 @@
// v0.11 JPB 29/9/20 Added enable_dma before transfer (in case still active) // v0.11 JPB 29/9/20 Added enable_dma before transfer (in case still active)
// Corrected DMA nsamp value (was byte count) // Corrected DMA nsamp value (was byte count)
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "rpi_dma_utils.h" #include "rpi_dma_utils.h"
#include "rpi_smi_defs.h" #include "rpi_smi_defs.h"
#include <arpa/inet.h>
#include <ctype.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#if PHYS_REG_BASE==PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz) #if PHYS_REG_BASE == PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz)
#define SMI_TIMING 10, 15, 30, 15 // 400 ns cycle time #define SMI_TIMING 10, 15, 30, 15 // 400 ns cycle time
#else // Timings for RPi v0-3 (1 GHz) #else // Timings for RPi v0-3 (1 GHz)
#define SMI_TIMING 10, 10, 20, 10 // 400 ns cycle time #define SMI_TIMING 10, 10, 20, 10 // 400 ns cycle time
#endif #endif
#define TX_TEST 0 // If non-zero, use dummy Tx data #define TX_TEST 0 // If non-zero, use dummy Tx data
#define LED_D0_PIN 8 // GPIO pin for D0 output #define LED_D0_PIN 8 // GPIO pin for D0 output
#define LED_NCHANS 8 // Number of LED channels (8 or 16) #define LED_NCHANS 8 // Number of LED channels (8 or 16)
#define LED_NBITS 24 // Number of data bits per LED #define LED_NBITS 24 // Number of data bits per LED
#define LED_PREBITS 4 // Number of zero bits before LED data #define LED_PREBITS 4 // Number of zero bits before LED data
#define LED_POSTBITS 4 // Number of zero bits after LED data #define LED_POSTBITS 4 // Number of zero bits after LED data
#define BIT_NPULSES 3 // Number of O/P pulses per LED bit #define BIT_NPULSES 3 // Number of O/P pulses per LED bit
#define CHAN_MAXLEDS 50 // Maximum number of LEDs per channel #define CHAN_MAXLEDS 50 // Maximum number of LEDs per channel
#define CHASE_MSEC 100 // Delay time for chaser light test #define CHASE_MSEC 100 // Delay time for chaser light test
#define REQUEST_THRESH 2 // DMA request threshold #define REQUEST_THRESH 2 // DMA request threshold
#define DMA_CHAN 10 // DMA channel to use #define DMA_CHAN 10 // DMA channel to use
// Length of data for 1 row (1 LED on each channel) // Length of data for 1 row (1 LED on each channel)
#define LED_DLEN (LED_NBITS * BIT_NPULSES) #define LED_DLEN (LED_NBITS * BIT_NPULSES)
// Transmit data type, 8 or 16 bits // Transmit data type, 8 or 16 bits
#if LED_NCHANS > 8 #if LED_NCHANS > 8
#define TXDATA_T uint16_t #define TXDATA_T uint16_t
#else #else
#define TXDATA_T uint8_t #define TXDATA_T uint8_t
#endif #endif
// Structures for mapped I/O devices, and non-volatile memory // Structures for mapped I/O devices, and non-volatile memory
@ -73,10 +76,10 @@ extern MEM_MAP gpio_regs, dma_regs;
MEM_MAP vc_mem, clk_regs, smi_regs; MEM_MAP vc_mem, clk_regs, smi_regs;
// Pointers to SMI registers // Pointers to SMI registers
volatile SMI_CS_REG *smi_cs; volatile SMI_CS_REG *smi_cs;
volatile SMI_L_REG *smi_l; volatile SMI_L_REG *smi_l;
volatile SMI_A_REG *smi_a; volatile SMI_A_REG *smi_a;
volatile SMI_D_REG *smi_d; volatile SMI_D_REG *smi_d;
volatile SMI_DMC_REG *smi_dmc; volatile SMI_DMC_REG *smi_dmc;
volatile SMI_DSR_REG *smi_dsr; volatile SMI_DSR_REG *smi_dsr;
volatile SMI_DSW_REG *smi_dsw; volatile SMI_DSW_REG *smi_dsw;
@ -85,18 +88,18 @@ volatile SMI_DCA_REG *smi_dca;
volatile SMI_DCD_REG *smi_dcd; volatile SMI_DCD_REG *smi_dcd;
// Ofset into Tx data buffer, given LED number in chan // Ofset into Tx data buffer, given LED number in chan
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n))) #define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
// Size of data buffers & NV memory, given number of LEDs per chan // Size of data buffers & NV memory, given number of LEDs per chan
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS) #define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T)) #define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS)) #define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
const char artnetId[] = "Art-Net";
// RGB values for test mode (1 value for each of 16 channels) // RGB values for test mode (1 value for each of 16 channels)
int on_rgbs[16] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, int on_rgbs[16] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040,
0xff4040, 0x40ff40, 0x4040ff, 0x404040, 0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040};
0xff0000, 0x00ff00, 0x0000ff, 0xffffff,
0xff4040, 0x40ff40, 0x4040ff, 0x404040};
int off_rgbs[16]; int off_rgbs[16];
#if TX_TEST #if TX_TEST
@ -104,11 +107,12 @@ int off_rgbs[16];
TXDATA_T tx_test_data[] = {1, 2, 3, 4, 5, 6, 7, 0}; TXDATA_T tx_test_data[] = {1, 2, 3, 4, 5, 6, 7, 0};
#endif #endif
TXDATA_T *txdata; // Pointer to uncached Tx data buffer TXDATA_T *txdata; // Pointer to uncached Tx data buffer
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
int testmode, chan_ledcount=1; // Command-line parameters int testmode, chan_ledcount = 1; // Command-line parameters
int rgb_data[CHAN_MAXLEDS][LED_NCHANS]; // RGB data int rgb_data[CHAN_MAXLEDS][LED_NCHANS]; // RGB data
int chan_num; // Current channel for data I/P int chan_num; // Current channel for data I/P
int udpSocket;
void rgb_txdata(int *rgbs, TXDATA_T *txd); void rgb_txdata(int *rgbs, TXDATA_T *txd);
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan); int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
@ -121,269 +125,289 @@ void init_smi(int width, int ns, int setup, int hold, int strobe);
void setup_smi_dma(MEM_MAP *mp, int nsamp); void setup_smi_dma(MEM_MAP *mp, int nsamp);
void start_smi(MEM_MAP *mp); void start_smi(MEM_MAP *mp);
int main(int argc, char *argv[]) int main(int argc, char *argv[]) {
{ // setup
int args=0, n, oset=0; int args = 0, n, oset = 0;
memset(off_rgbs, 0, sizeof(int) * 16);
while (argc > ++args) // Process command-line args while (argc > ++args) // Process command-line args
{ {
if (argv[args][0] == '-') if (argv[args][0] == '-') {
{ switch (toupper(argv[args][1])) {
switch (toupper(argv[args][1])) case 'N': // -N: number of LEDs per channel
{ if (args >= argc - 1)
case 'N': // -N: number of LEDs per channel fprintf(stderr, "Error: no numeric value\n");
if (args >= argc-1) else
fprintf(stderr, "Error: no numeric value\n"); chan_ledcount = atoi(argv[++args]);
else break;
chan_ledcount = atoi(argv[++args]); case 'T': // -T: test mode
break; testmode = 1;
case 'T': // -T: test mode break;
testmode = 1; default: // Otherwise error
break; printf("Unrecognised option '%c'\n", argv[args][1]);
default: // Otherwise error printf("Options:\n"
printf("Unrecognised option '%c'\n", argv[args][1]); " -n num number of LEDs per channel\n"
printf("Options:\n" " -t Test mode (flash LEDs)\n");
" -n num number of LEDs per channel\n"\ return (1);
" -t Test mode (flash LEDs)\n"\ }
); } else if (chan_num < LED_NCHANS && hexdig(argv[args][0]) >= 0 &&
return(1); (n = str_rgb(argv[args], rgb_data, chan_num)) > 0) {
} chan_ledcount = n > chan_ledcount ? n : chan_ledcount;
} chan_num++;
else if (chan_num<LED_NCHANS && hexdig(argv[args][0])>=0 &&
(n=str_rgb(argv[args], rgb_data, chan_num))>0)
{
chan_ledcount = n > chan_ledcount ? n : chan_ledcount;
chan_num++;
}
} }
signal(SIGINT, terminate); }
map_devices(); signal(SIGINT, terminate);
init_smi(LED_NCHANS>8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING); map_devices();
map_uncached_mem(&vc_mem, VC_MEM_SIZE); init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
#if TX_TEST map_uncached_mem(&vc_mem, VC_MEM_SIZE);
oset = oset;
setup_smi_dma(&vc_mem, sizeof(tx_test_data)/sizeof(TXDATA_T));
#if LED_NCHANS <= 8
swap_bytes(tx_test_data, sizeof(tx_test_data));
#endif
memcpy(txdata, tx_test_data, sizeof(tx_test_data));
start_smi(&vc_mem);
usleep(10);
while (dma_active(DMA_CHAN))
usleep(10);
#else
setup_smi_dma(&vc_mem, TX_BUFF_LEN(chan_ledcount));
printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting",
chan_ledcount, chan_ledcount==1 ? "" : "s", LED_NCHANS);
if (testmode) setup_smi_dma(&vc_mem, TX_BUFF_LEN(chan_ledcount));
{ printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting", chan_ledcount,
while (1) chan_ledcount == 1 ? "" : "s", LED_NCHANS);
{
if (chan_ledcount < 2) int nBytes;
rgb_txdata(oset&1 ? off_rgbs : on_rgbs, tx_buffer); char buffer[1024];
else struct sockaddr_in serverAddr;
{ struct sockaddr_storage serverStorage;
for (n=0; n<chan_ledcount; n++) socklen_t addr_size;
{
rgb_txdata(n==oset%chan_ledcount ? on_rgbs : off_rgbs, /*Create UDP socket*/
&tx_buffer[LED_TX_OSET(n)]); udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
}
/*Configure settings in address struct*/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(0x1936);
serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*Bind socket with address struct*/
bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
/*Initialize size variable to be used later on*/
addr_size = sizeof serverStorage;
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
start_smi(&vc_mem);
// loops
if (testmode) {
while (1) {
if (chan_ledcount < 2)
rgb_txdata(oset & 1 ? off_rgbs : on_rgbs, tx_buffer);
else {
for (n = 0; n < chan_ledcount; n++) {
rgb_txdata(n == oset % chan_ledcount ? on_rgbs : off_rgbs, &tx_buffer[LED_TX_OSET(n)]);
}
}
oset++;
#if LED_NCHANS <= 8
swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
#endif
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
start_smi(&vc_mem);
usleep(CHASE_MSEC * 1000);
}
} else {
while (1) {
nBytes = recvfrom(udpSocket, buffer, 1024, 0, (struct sockaddr *)&serverStorage, &addr_size);
if (nBytes <= 530 && nBytes > 0) {
if (memcmp(buffer, artnetId, sizeof(artnetId)) == 0) {
uint16_t opcode = buffer[8] | (buffer[9] << 8);
if (opcode == 0x5000) {
// uint8_t sequence = buffer[12];
// uint16_t incomingUniverse = buffer[14] | (buffer[15] << 8);
uint16_t dmxDataLength = buffer[17] | (buffer[16] << 8);
char *dmxData = buffer + 18;
uint16_t maxBound =
(dmxDataLength / 3) < chan_ledcount ? (dmxDataLength / 3) : chan_ledcount;
for (size_t i = 0; i < maxBound; ++i) {
for (size_t j = 0; j < LED_NCHANS; ++j) {
char *rgb = dmxData + (i * 3);
rgb_data[i][j] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
}
rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
} }
oset++;
#if LED_NCHANS <= 8 #if LED_NCHANS <= 8
swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount)); swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
#endif #endif
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount)); memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
// enable_dma(DMA_CHAN);
start_smi(&vc_mem); start_smi(&vc_mem);
usleep(CHASE_MSEC * 1000); // usleep(10);
// while (dma_active(DMA_CHAN))
// usleep(10);
}
} }
}
} }
else }
{ terminate(0);
for (n=0; n<chan_ledcount; n++) return (0);
rgb_txdata(rgb_data[n], &tx_buffer[LED_TX_OSET(n)]);
#if LED_NCHANS <= 8
swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
#endif
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
enable_dma(DMA_CHAN);
start_smi(&vc_mem);
usleep(10);
while (dma_active(DMA_CHAN))
usleep(10);
}
#endif
terminate(0);
return(0);
} }
// Convert RGB text string into integer data, for given channel // Convert RGB text string into integer data, for given channel
// Return number of data points for this channel // Return number of data points for this channel
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan) int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan) {
{ int i = 0;
int i=0; char *p;
char *p;
while (chan<LED_NCHANS && i<CHAN_MAXLEDS && hexdig(*s)>=0) while (chan < LED_NCHANS && i < CHAN_MAXLEDS && hexdig(*s) >= 0) {
{ rgbs[i++][chan] = strtoul(s, &p, 16);
rgbs[i++][chan] = strtoul(s, &p, 16); s = *p ? p + 1 : p;
s = *p ? p+1 : p; }
} return (i);
return(i);
} }
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan // Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low // Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
void rgb_txdata(int *rgbs, TXDATA_T *txd) void rgb_txdata(int *rgbs, TXDATA_T *txd) {
{ int i, n, msk;
int i, n, msk;
// For each bit of the 24-bit RGB values.. // For each bit of the 24-bit RGB values..
for (n=0; n<LED_NBITS; n++) for (n = 0; n < LED_NBITS; n++) {
{ // Mask to convert RGB to GRB, M.S bit first
// Mask to convert RGB to GRB, M.S bit first msk = n == 0 ? 0x8000 : n == 8 ? 0x800000 : n == 16 ? 0x80 : msk >> 1;
msk = n==0 ? 0x8000 : n==8 ? 0x800000 : n==16 ? 0x80 : msk>>1; // 1st byte or word is a high pulse on all lines
// 1st byte or word is a high pulse on all lines txd[0] = (TXDATA_T)0xffff;
txd[0] = (TXDATA_T)0xffff; // 2nd has high or low bits from data
// 2nd has high or low bits from data // 3rd is a low pulse
// 3rd is a low pulse txd[1] = txd[2] = 0;
txd[1] = txd[2] = 0; for (i = 0; i < LED_NCHANS; i++) {
for (i=0; i<LED_NCHANS; i++) if (rgbs[i] & msk)
{ txd[1] |= (1 << i);
if (rgbs[i] & msk)
txd[1] |= (1 << i);
}
txd += BIT_NPULSES;
} }
txd += BIT_NPULSES;
}
} }
// Swap adjacent bytes in transmit data // Swap adjacent bytes in transmit data
void swap_bytes(void *data, int len) void swap_bytes(void *data, int len) {
{ uint16_t *wp = (uint16_t *)data;
uint16_t *wp = (uint16_t *)data;
len = (len + 1) / 2; len = (len + 1) / 2;
while (len-- > 0) while (len-- > 0) {
{ *wp = __builtin_bswap16(*wp);
*wp = __builtin_bswap16(*wp); wp++;
wp++; }
}
} }
// Return hex digit value, -ve if not hex // Return hex digit value, -ve if not hex
int hexdig(char c) int hexdig(char c) {
{ c = toupper(c);
c = toupper(c); return ((c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : -1);
return((c>='0' && c<='9') ? c-'0' : (c>='A' && c<='F') ? c-'A'+10 : -1);
} }
// Map GPIO, DMA and SMI registers into virtual mem (user space) // Map GPIO, DMA and SMI registers into virtual mem (user space)
// If any of these fail, program will be terminated // If any of these fail, program will be terminated
void map_devices(void) void map_devices(void) {
{ map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE); map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE); map_periph(&clk_regs, (void *)CLK_BASE, PAGE_SIZE);
map_periph(&clk_regs, (void *)CLK_BASE, PAGE_SIZE); map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
} }
// Catastrophic failure in initial setup // Catastrophic failure in initial setup
void fail(char *s) void fail(char *s) {
{ printf(s);
printf(s); terminate(0);
terminate(0);
} }
// Free memory segments and exit // Free memory segments and exit
void terminate(int sig) void terminate(int sig) {
{ int i;
int i;
printf("Closing\n"); printf("Closing\n");
if (gpio_regs.virt) close(udpSocket);
{
for (i=0; i<LED_NCHANS; i++) for (size_t i = 0; i < chan_ledcount; ++i) {
gpio_mode(LED_D0_PIN+i, GPIO_IN); rgb_txdata(off_rgbs, &tx_buffer[LED_TX_OSET(i)]);
} }
if (smi_regs.virt) swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
*REG32(smi_regs, SMI_CS) = 0; memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
stop_dma(DMA_CHAN); start_smi(&vc_mem);
unmap_periph_mem(&vc_mem); sleep(1);
unmap_periph_mem(&smi_regs);
unmap_periph_mem(&dma_regs); if (gpio_regs.virt) {
unmap_periph_mem(&gpio_regs); for (i = 0; i < LED_NCHANS; i++)
exit(0); gpio_mode(LED_D0_PIN + i, GPIO_IN);
}
if (smi_regs.virt)
*REG32(smi_regs, SMI_CS) = 0;
stop_dma(DMA_CHAN);
unmap_periph_mem(&vc_mem);
unmap_periph_mem(&smi_regs);
unmap_periph_mem(&dma_regs);
unmap_periph_mem(&gpio_regs);
exit(0);
} }
// Initialise SMI, given data width, time step, and setup/hold/strobe counts // Initialise SMI, given data width, time step, and setup/hold/strobe counts
// Step value is in nanoseconds: even numbers, 2 to 30 // Step value is in nanoseconds: even numbers, 2 to 30
void init_smi(int width, int ns, int setup, int strobe, int hold) void init_smi(int width, int ns, int setup, int strobe, int hold) {
{ int i, divi = ns / 2;
int i, divi = ns / 2;
smi_cs = (SMI_CS_REG *) REG32(smi_regs, SMI_CS); smi_cs = (SMI_CS_REG *)REG32(smi_regs, SMI_CS);
smi_l = (SMI_L_REG *) REG32(smi_regs, SMI_L); smi_l = (SMI_L_REG *)REG32(smi_regs, SMI_L);
smi_a = (SMI_A_REG *) REG32(smi_regs, SMI_A); smi_a = (SMI_A_REG *)REG32(smi_regs, SMI_A);
smi_d = (SMI_D_REG *) REG32(smi_regs, SMI_D); smi_d = (SMI_D_REG *)REG32(smi_regs, SMI_D);
smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC); smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC);
smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0); smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0);
smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0); smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0);
smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS); smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS);
smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA); smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA);
smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD); smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD);
smi_cs->value = smi_l->value = smi_a->value = 0; smi_cs->value = smi_l->value = smi_a->value = 0;
smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0; smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0;
if (*REG32(clk_regs, CLK_SMI_DIV) != divi << 12) if (*REG32(clk_regs, CLK_SMI_DIV) != divi << 12) {
{ *REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
*REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5); usleep(10);
usleep(10); while (*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7))
while (*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) ; ;
usleep(10); usleep(10);
*REG32(clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12); *REG32(clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
usleep(10); usleep(10);
*REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4); *REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
usleep(10); usleep(10);
while ((*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0) ; while ((*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0)
usleep(100); ;
} usleep(100);
if (smi_cs->seterr) }
smi_cs->seterr = 1; if (smi_cs->seterr)
smi_dsr->rsetup = smi_dsw->wsetup = setup; smi_cs->seterr = 1;
smi_dsr->rstrobe = smi_dsw->wstrobe = strobe; smi_dsr->rsetup = smi_dsw->wsetup = setup;
smi_dsr->rhold = smi_dsw->whold = hold; smi_dsr->rstrobe = smi_dsw->wstrobe = strobe;
smi_dmc->panicr = smi_dmc->panicw = 8; smi_dsr->rhold = smi_dsw->whold = hold;
smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH; smi_dmc->panicr = smi_dmc->panicw = 8;
smi_dsr->rwidth = smi_dsw->wwidth = width; smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH;
for (i=0; i<LED_NCHANS; i++) smi_dsr->rwidth = smi_dsw->wwidth = width;
gpio_mode(LED_D0_PIN+i, GPIO_ALT1); for (i = 0; i < LED_NCHANS; i++)
gpio_mode(LED_D0_PIN + i, GPIO_ALT1);
} }
// Set up SMI transfers using DMA // Set up SMI transfers using DMA
void setup_smi_dma(MEM_MAP *mp, int nsamp) void setup_smi_dma(MEM_MAP *mp, int nsamp) {
{ DMA_CB *cbs = mp->virt;
DMA_CB *cbs=mp->virt;
txdata = (TXDATA_T *)(cbs+1); txdata = (TXDATA_T *)(cbs + 1);
smi_dmc->dmaen = 1; smi_dmc->dmaen = 1;
smi_cs->enable = 1; smi_cs->enable = 1;
smi_cs->clear = 1; smi_cs->clear = 1;
smi_cs->pxldat = 1; smi_cs->pxldat = 1;
smi_l->len = nsamp * sizeof(TXDATA_T); smi_l->len = nsamp * sizeof(TXDATA_T);
smi_cs->write = 1; smi_cs->write = 1;
enable_dma(DMA_CHAN); enable_dma(DMA_CHAN);
cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP; cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP;
cbs[0].tfr_len = nsamp; cbs[0].tfr_len = nsamp;
cbs[0].srce_ad = MEM_BUS_ADDR(mp, txdata); cbs[0].srce_ad = MEM_BUS_ADDR(mp, txdata);
cbs[0].dest_ad = REG_BUS_ADDR(smi_regs, SMI_D); cbs[0].dest_ad = REG_BUS_ADDR(smi_regs, SMI_D);
} }
// Start SMI DMA transfers // Start SMI DMA transfers
void start_smi(MEM_MAP *mp) void start_smi(MEM_MAP *mp) {
{ DMA_CB *cbs = mp->virt;
DMA_CB *cbs=mp->virt;
start_dma(mp, DMA_CHAN, &cbs[0], 0); start_dma(mp, DMA_CHAN, &cbs[0], 0);
smi_cs->start = 1; smi_cs->start = 1;
} }
// EOF // EOF