diff --git a/RpiLedBars/.vscode/launch.json b/RpiLedBars/.vscode/launch.json index 1031ce1..fd94da6 100644 --- a/RpiLedBars/.vscode/launch.json +++ b/RpiLedBars/.vscode/launch.json @@ -10,7 +10,8 @@ "request": "launch", "program": "${workspaceFolder}/bin/pixled", "args": [ - "-n", "5", + "-n", + "5", // "-t" ], "stopAtEntry": false, @@ -25,7 +26,8 @@ "ignoreFailures": true } ], - "miDebuggerPath": "${workspaceFolder}/sgdb.sh" + "miDebuggerPath": "${workspaceFolder}/sgdb.sh", + "preLaunchTask": "${defaultBuildTask}" } ] } \ No newline at end of file diff --git a/RpiLedBars/.vscode/tasks.json b/RpiLedBars/.vscode/tasks.json index 17c3765..cbe976a 100644 --- a/RpiLedBars/.vscode/tasks.json +++ b/RpiLedBars/.vscode/tasks.json @@ -4,7 +4,7 @@ { "type": "shell", "label": "Build project", - "command": "make", + "command": "make clean; make", "options": { "cwd": "${workspaceFolder}" }, diff --git a/RpiLedBars/Makefile b/RpiLedBars/Makefile index 5cebba3..8e32a6e 100644 --- a/RpiLedBars/Makefile +++ b/RpiLedBars/Makefile @@ -8,7 +8,7 @@ SRC=$(notdir $(wildcard $(SRCDIR)/*.c)) OBJ=$(SRC:.c=.o) BIN=pixled -all: clean $(addprefix $(BINDIR)/, $(BIN)) +all: $(addprefix $(BINDIR)/, $(BIN)) $(OBJDIR)/%.o: $(SRCDIR)/%.c if [ ! -d $(OBJDIR) ]; then mkdir "$(OBJDIR)"; fi diff --git a/RpiLedBars/README.md b/RpiLedBars/README.md new file mode 100644 index 0000000..8cb5b89 --- /dev/null +++ b/RpiLedBars/README.md @@ -0,0 +1,24 @@ +RpiLedBars +--- + +LedBars version for raspberry pi + +contains : +* a python test program (main.py) +* a C program using : + * ws2812 with dma/smi from Jeremy P Bentham - https://iosoft.blog/category/neopixel/ + * artnet from Stephan Ruloff - https://github.com/rstephan/ArtnetnodeWifi + +biblio: +* Art-Net + * Protocol Spec : + * https://art-net.org.uk/how-it-works/ + * https://artisticlicence.com/WebSiteMaster/User%20Guides/art-net.pdf + * ESP32 library from Stephan Ruloff - https://github.com/rstephan/ArtnetnodeWifi +* ws28** rgb leds + * ws2812 datasheet - https://cdn-shop.adafruit.com/datasheets/WS2812.pdf + * ws2815 datasheet - https://pdf1.alldatasheet.com/datasheet-pdf/view/1134588/WORLDSEMI/WS2815B.html + * ws2812 with dma/smi from Jeremy P Bentham : + * ws2812 - https://iosoft.blog/2020/09/29/raspberry-pi-multi-channel-ws2812/ + * dma - https://iosoft.blog/2020/05/25/raspberry-pi-dma-programming/ + * smi - https://iosoft.blog/2020/07/16/raspberry-pi-smi/ \ No newline at end of file diff --git a/RpiLedBars/src/rpi_artnet.c b/RpiLedBars/src/rpi_artnet.c new file mode 100644 index 0000000..889e00f --- /dev/null +++ b/RpiLedBars/src/rpi_artnet.c @@ -0,0 +1,70 @@ +#include "rpi_artnet.h" + +#include +#include +#include +#include + +#include "rpi_artnet_utils.h" + +int udpSocket = -1; +char buffer[1024]; + +void sendPollReply(struct sockaddr_in srcAddr); + +void artnet_init() { + struct sockaddr_in serverAddr; + + /*Create UDP socket*/ + udpSocket = socket(PF_INET, SOCK_DGRAM, 0); + if (udpSocket < 0) { + perror("Opening socket failed"); + } + + /* Configure settings in address struct */ + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(ARTNET_PORT); + 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)); +} + +int artnet_read(artDmx_t **artDmx) { + struct sockaddr_in srcAddr; + socklen_t srcLen = sizeof(srcAddr); + ssize_t bufferLen = + recvfrom(udpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen); + + if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) { + artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer; + if (memcmp(artnetHeader->id, ARTNET_ID, sizeof(ARTNET_ID)) == 0) { + switch (artnetHeader->opCode) { + case OpDmx: + if (bufferLen >= 20) { + *artDmx = (artDmx_t *)buffer; + } + break; + case OpPoll: + sendPollReply(srcAddr); + break; + default: + break; + } + } + + return artnetHeader->opCode; + } + return -1; +} + +void sendPollReply(struct sockaddr_in srcAddr) { + /* Configure settings in address struct */ + srcAddr.sin_family = AF_INET; + srcAddr.sin_port = htons(ARTNET_PORT); + memset(srcAddr.sin_zero, '\0', sizeof(srcAddr.sin_zero)); + + sendto(udpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0, + (struct sockaddr *)&srcAddr, sizeof(srcAddr)); +} \ No newline at end of file diff --git a/RpiLedBars/src/rpi_artnet.h b/RpiLedBars/src/rpi_artnet.h new file mode 100644 index 0000000..1cf76ee --- /dev/null +++ b/RpiLedBars/src/rpi_artnet.h @@ -0,0 +1,10 @@ +#if !defined(__RPI_ARTNET_H__) +#define __RPI_ARTNET_H__ + +#include "rpi_artnet_packets.h" + +void artnet_init(); + +int artnet_read(artDmx_t **artDmx); + +#endif // __RPI_ARTNET_H__ diff --git a/RpiLedBars/src/rpi_artnet_op_codes.h b/RpiLedBars/src/rpi_artnet_op_codes.h new file mode 100644 index 0000000..821224f --- /dev/null +++ b/RpiLedBars/src/rpi_artnet_op_codes.h @@ -0,0 +1,128 @@ +#if !defined(__RPI_ARTNET_OP_CODES_H__) +#define __RPI_ARTNET_OP_CODES_H__ + +/* List of hex values and discriptions of Opcodes */ + +/* This is an ArtPoll packet, no other data is contained in this UDP packet */ +#define OpPoll 0x2000 + +/* This is an ArtPollReply Packet. It contains device status information. */ +#define OpPollReply 0x2100 + +/* Diagnostics and data logging packet. */ +#define OpDiagData 0x2300 + +/* Used to send text based parameter commands. */ +#define OpCommand 0x2400 + +/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single + * Universe. */ +#define OpOutput 0x5000 + +/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single + * Universe. */ +#define OpDmx 0x5000 + +/* This is an ArtNzs data packet. It contains non-zero start code (except RDM) DMX512 information + * for a single Universe. */ +#define OpNzs 0x5100 + +/* This is an ArtAddress packet. It contains remote programming information for a Node. */ +#define OpAddress 0x6000 + +/* This is an ArtInput packet. It contains enable – disable data for DMX inputs. */ +#define OpInput 0x7000 + +/* This is an ArtTodRequest packet. It is used to request a Table of Devices (ToD) for RDM + * discovery. */ +#define OpTodRequest 0x8000 + +/* This is an ArtTodData packet. It is used to send a Table of Devices (ToD) for RDM discovery. */ +#define OpTodData 0x8100 + +/* This is an ArtTodControl packet. It is used to send RDM discovery control messages. */ +#define OpTodControl 0x8200 + +/* This is an ArtRdm packet. It is used to send all non discovery RDM messages. */ +#define OpRdm 0x8300 + +/* This is an ArtRdmSub packet. It is used to send compressed, RDM Sub-Device data. */ +#define OpRdmSub 0x8400 + +/* This is an ArtVideoSetup packet. It contains video screen setup information for nodes that + * implement the extended video features. */ +#define OpVideoSetup 0xa010 + +/* This is an ArtVideoPalette packet. It contains colour palette setup information for nodes that + * implement the extended video features. */ +#define OpVideoPalette 0xa020 + +/* This is an ArtVideoData packet. It contains display data for nodes that implement the extended + * video features. */ +#define OpVideoData 0xa040 + +/* This is an ArtMacMaster packet. It is used to program the Node’s MAC address, Oem device type and + * ESTA manufacturer code. This is for factory initialisation of a Node. It is not to be used by + * applications. */ +#define OpMacMaster 0xf000 + +/* This is an ArtMacSlave packet. It is returned by the node to acknowledge receipt of an + * ArtMacMaster packet. */ +#define OpMacSlave 0xf100 + +/* This is an ArtFirmwareMaster packet. It is used to upload new firmware or firmware extensions to + * the Node. */ +#define OpFirmwareMaster 0xf200 + +/* This is an ArtFirmwareReply packet. It is returned by the node to acknowledge receipt of an + * ArtFirmwareMaster packet or ArtFileTnMaster packet. */ +#define OpFirmwareReply 0xf300 + +/* Uploads user file to node. */ +#define OpFileTnMaster 0xf400 + +/* Downloads user file from node. */ +#define OpFileFnMaster 0xf500 + +/* Node acknowledge for downloads. */ +#define OpFileFnReply 0xf600 + +/* This is an ArtIpProg packet. It is used to reprogramme the IP, Mask and Port address of the Node. + */ +#define OpIpProg 0xf800 + +/* This is an ArtIpProgReply packet. It is returned by the node to acknowledge receipt of an + * ArtIpProg packet. */ +#define OpIpProgReply 0xf900 + +/* This is an ArtMedia packet. It is Unicast by a Media Server and acted upon by a Controller. */ +#define OpMedia 0x9000 + +/* This is an ArtMediaPatch packet. It is Unicast by a Controller and acted upon by a Media Server. + */ +#define OpMediaPatch 0x9100 + +/* This is an ArtMediaControl packet. It is Unicast by a Controller and acted upon by a Media + * Server. */ +#define OpMediaControl 0x9200 + +/* This is an ArtMediaControlReply packet. It is Unicast by a Media Server and acted upon by a + * Controller. */ +#define OpMediaContrlReply 0x9300 + +/* This is an ArtTimeCode packet. It is used to transport time code over the network. */ +#define OpTimeCode 0x9700 + +/* Used to synchronise real time date and clock */ +#define OpTimeSync 0x9800 + +/* Used to send trigger macros */ +#define OpTrigger 0x9900 + +/* Requests a node's file list */ +#define OpDirectory 0x9a00 + +/* Replies to OpDirectory with file list */ +#define OpDirectoryReply 0x9b00 + +#endif // __RPI_ARTNET_OP_CODES_H__ \ No newline at end of file diff --git a/RpiLedBars/src/rpi_artnet_packets.h b/RpiLedBars/src/rpi_artnet_packets.h new file mode 100644 index 0000000..d4828f6 --- /dev/null +++ b/RpiLedBars/src/rpi_artnet_packets.h @@ -0,0 +1,178 @@ +#if !defined(__RPI_ARTNET_PACKETS_H__) +#define __RPI_ARTNET_PACKETS_H__ + +#include + +#include "rpi_artnet_op_codes.h" +#include "rpi_artnet_utils.h" + +typedef struct { + char id[8]; + uint16_t opCode; +} __attribute__((__packed__)) artnetHeader_t; + +typedef struct { + artnetHeader_t artnetHeader; + uint16_t protVer; + uint8_t sequence; + uint8_t physical; + uint8_t subUni; + uint8_t net; + uint8_t lengthHi; + uint8_t lengthLo; + uint8_t data[512]; +} __attribute__((__packed__)) artDmx_t; + +typedef struct { + char ID[8]; + uint16_t OpCode; + uint8_t IPAddr[4]; + uint16_t Port; + uint16_t VersionInfo; // The node's current FIRMWARE VERS + // Bits 14-8 of the 15 bit universe number are encoded into the bottom 7 bits of this field. + uint8_t NetSwitch; + // This is used in combination with SubSwitch and Swin[] or Swout[] to produce the full universe + // address. Bits 7-4 of the 15 bit universe number are encoded into the bottom 4 bits of this + // field. + uint8_t SubSwitch; + uint16_t Oem; // Manufacturer code, bit 15 set if + // extended features avail + uint8_t UbeaVersion; // Firmware version of UBEA + uint8_t Status; + // bit 0 = 0 UBEA not present + // bit 0 = 1 UBEA present + // bit 1 = 0 Not capable of RDM (Uni-directional DMX) + // bit 1 = 1 Capable of RDM (Bi-directional DMX) + // bit 2 = 0 Booted from flash (normal boot) + // bit 2 = 1 Booted from ROM (possible error condition) + // bit 3 = Not used + // bit 54 = 00 Universe programming authority unknown + // bit 54 = 01 Universe programming authority set by front panel controls + // bit 54 = 10 Universe programming authority set by network + // bit 76 = 00 Indicators Normal + // bit 76 = 01 Indicators Locate + // bit 76 = 10 Indicators Mute + uint8_t EstaMan[2]; // ESTA manufacturer id, lo byte + char ShortName[18]; // short name defaults to IP + char LongName[64]; + // Text feedback of Node status or errors also used for debug info + char NodeReport[64]; + + uint8_t NumPortsHi; // 0 + uint8_t NumPortsLo; // 4 If num i/p ports is dif to output ports, return biggest + + uint8_t PortTypes[4]; + // bit 7 is output + // bit 6 is input + // bits 0-5 are protocol number (0= DMX, 1=MIDI) + // for DMX-Hub ={0xc0,0xc0,0xc0,0xc0}; + + uint8_t GoodInput[4]; + // bit 7 is data received + // bit 6 is data includes test packets + // bit 5 is data includes SIP's + // bit 4 is data includes text + // bit 3 set is input is disabled + // bit 2 is receive errors + // bit 1-0 not used, transmitted as zero. + // Don't test for zero! + + uint8_t GoodOutput[4]; + // bit 7 is data is transmitting + // bit 6 is data includes test packets + // bit 5 is data includes SIP's + // bit 4 is data includes text + // bit 3 output is merging data. + // bit 2 set if DMX output short detected on power up + // bit 1 set if DMX output merge mode is LTP + // bit 0 not used, transmitted as zero. + + uint8_t SwIn[4]; + // Bits 3-0 of the 15 bit universe number are encoded into the low nibble + // This is used in combination with SubSwitch and NetSwitch to produce the full universe address. + // THIS IS FOR INPUT - ART-NET or DMX + // NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO. + + uint8_t SwOut[4]; + // Bits 3-0 of the 15 bit universe number are encoded into the low nibble + // This is used in combination with SubSwitch and NetSwitch to produce the full universe address. + // data belongs + // THIS IS FOR OUTPUT - ART-NET or DMX. + // NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO. + + uint8_t SwVideo; + // Low nibble is the value of the video + // output channel + + uint8_t SwMacro; + // Bit 0 is Macro input 1 + // Bit 7 is Macro input 8 + + uint8_t SwRemote; + // Bit 0 is Remote input 1 + // Bit 7 is Remote input 8 + + uint8_t Spare1; // Spare, currently zero + uint8_t Spare2; // Spare, currently zero + uint8_t Spare3; // Spare, currently zero + uint8_t Style; // Set to Style code to describe type of equipment + + uint8_t Mac[6]; // Mac Address, zero if info not available + + uint8_t BindIp[4]; // If this unit is part of a larger or modular product, this is the IP of the + // root device. + uint8_t BindIndex; // Set to zero if no binding, otherwise this number represents the order of + // bound devices. A lower number means closer to root device. + + uint8_t Status2; + // bit 0 = 0 Node does not support web browser + // bit 0 = 1 Node supports web browser configuration + + // bit 1 = 0 Node's IP address is manually configured + // bit 1 = 1 Node's IP address is DHCP configured + + // bit 2 = 0 Node is not DHCP capable + // bit 2 = 1 Node is DHCP capable + + // bit 2-7 not implemented, transmit as zero + + uint8_t Filler[26]; // Filler bytes, currently zero. +} artPollReply_t; + +static artPollReply_t artPollReply = { + .ID = "Art-Net", + .OpCode = OpPollReply, + .IPAddr = {0}, + .Port = ARTNET_PORT, + .VersionInfo = 1, + .NetSwitch = 0, + .SubSwitch = 0, + .Oem = 0x0190, + .UbeaVersion = 0, + .Status = 0, + .EstaMan = {0, 0}, + .ShortName = "Tropicananass", + .LongName = "Tropicananass Artnetnode", + .NodeReport = {0}, + .NumPortsHi = 0, + .NumPortsLo = 1, + .PortTypes = {0x80, 0, 0, 0}, + .GoodInput = {0}, + .GoodOutput = {0}, + .SwIn = {0}, + .SwOut = {0}, + .SwVideo = 0, + .SwMacro = 0, + .SwRemote = 0, + .Spare1 = 0, + .Spare2 = 0, + .Spare3 = 0, + .Style = 0, + .Mac = {0}, + .BindIp = {0}, + .BindIndex = 0, + .Status2 = 0b00000000, + .Filler = {0}, +}; + +#endif // __RPI_ARTNET_PACKETS_H__ diff --git a/RpiLedBars/src/rpi_artnet_utils.h b/RpiLedBars/src/rpi_artnet_utils.h new file mode 100644 index 0000000..e453be8 --- /dev/null +++ b/RpiLedBars/src/rpi_artnet_utils.h @@ -0,0 +1,25 @@ +#if !defined(__RPI_ARTNET_UTILS_H__) +#define __RPI_ARTNET_UTILS_H__ + +#define ARTNET_PORT 0x1936 + +// Buffers +#define ARTNET_MAX_BUFFER 530 +#define DMX_MAX_BUFFER 512 + +// Packet constants +#define ARTNET_ID "Art-Net" +#define ARTNET_DMX_START_LOC 18 + +// Packet confines +#define ARTNET_SHORT_NAME_MAX_LENGTH 17 +#define ARTNET_LONG_NAME_MAX_LENGTH 63 + +// DMX settings +#define DMX_MAX_OUTPUTS 4 +#define DMX_MS_BETWEEN_TICKS 25 + +// RDM +#define DMX_RDM_STARTCODE 0xCC + +#endif // __RPI_ARTNET_UTILS_H__ diff --git a/RpiLedBars/src/rpi_dma_utils.c b/RpiLedBars/src/rpi_dma_utils.c index d32dfe9..6b814ae 100644 --- a/RpiLedBars/src/rpi_dma_utils.c +++ b/RpiLedBars/src/rpi_dma_utils.c @@ -14,333 +14,294 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#include -#include -#include -#include #include #include +#include +#include #include #include #include +#include #include "rpi_dma_utils.h" // If non-zero, print debug information -#define DEBUG 0 +#define DEBUG 0 // If non-zero, enable PWM hardware output -#define PWM_OUT 0 +#define PWM_OUT 0 -char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD", - "TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""}; +char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD", + "TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""}; char *gpio_mode_strs[] = {GPIO_MODE_STRS}; // Virtual memory pointers to acceess GPIO, DMA and PWM from user space MEM_MAP pwm_regs, gpio_regs, dma_regs, clk_regs; // Use mmap to obtain virtual address, given physical -void *map_periph(MEM_MAP *mp, void *phys, int size) -{ - mp->phys = phys; - mp->size = PAGE_ROUNDUP(size); - mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE); - mp->virt = map_segment(phys, mp->size); - return(mp->virt); +void *map_periph(MEM_MAP *mp, void *phys, int size) { + mp->phys = phys; + mp->size = PAGE_ROUNDUP(size); + mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE); + mp->virt = map_segment(phys, mp->size); + return (mp->virt); } // Allocate uncached memory, get bus & phys addresses -void *map_uncached_mem(MEM_MAP *mp, int size) -{ - void *ret; - mp->size = PAGE_ROUNDUP(size); - mp->fd = open_mbox(); - ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 && - (mp->bus = lock_vc_mem(mp->fd, mp->h)) != 0 && - (mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0 - ? mp->virt : 0; - printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt); - return(ret); +void *map_uncached_mem(MEM_MAP *mp, int size) { + void *ret; + mp->size = PAGE_ROUNDUP(size); + mp->fd = open_mbox(); + ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 && + (mp->bus = lock_vc_mem(mp->fd, mp->h)) != 0 && + (mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0 + ? mp->virt + : 0; + printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt); + return (ret); } // Free mapped peripheral or memory -void unmap_periph_mem(MEM_MAP *mp) -{ - if (mp) - { - if (mp->fd) - { - unmap_segment(mp->virt, mp->size); - unlock_vc_mem(mp->fd, mp->h); - free_vc_mem(mp->fd, mp->h); - close_mbox(mp->fd); - } - else - unmap_segment(mp->virt, mp->size); - } +void unmap_periph_mem(MEM_MAP *mp) { + if (mp) { + if (mp->fd) { + unmap_segment(mp->virt, mp->size); + unlock_vc_mem(mp->fd, mp->h); + free_vc_mem(mp->fd, mp->h); + close_mbox(mp->fd); + } else + unmap_segment(mp->virt, mp->size); + } } // ----- GPIO ----- // Set input or output with pullups -void gpio_set(int pin, int mode, int pull) -{ - gpio_mode(pin, mode); - gpio_pull(pin, pull); +void gpio_set(int pin, int mode, int pull) { + gpio_mode(pin, mode); + gpio_pull(pin, pull); } // Set I/P pullup or pulldown -void gpio_pull(int pin, int pull) -{ - volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32; +void gpio_pull(int pin, int pull) { + volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32; - *REG32(gpio_regs, GPIO_GPPUD) = pull; - usleep(2); - *reg = pin << (pin % 32); - usleep(2); - *REG32(gpio_regs, GPIO_GPPUD) = 0; - *reg = 0; + *REG32(gpio_regs, GPIO_GPPUD) = pull; + usleep(2); + *reg = pin << (pin % 32); + usleep(2); + *REG32(gpio_regs, GPIO_GPPUD) = 0; + *reg = 0; } // Set input or output -void gpio_mode(int pin, int mode) -{ - volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3; - *reg = (*reg & ~(7 << shift)) | (mode << shift); +void gpio_mode(int pin, int mode) { + volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3; + *reg = (*reg & ~(7 << shift)) | (mode << shift); } // Set an O/P pin -void gpio_out(int pin, int val) -{ - volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin/32; - *reg = 1 << (pin % 32); +void gpio_out(int pin, int val) { + volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin / 32; + *reg = 1 << (pin % 32); } // Get an I/P pin value -uint8_t gpio_in(int pin) -{ - volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin/32; - return (((*reg) >> (pin % 32)) & 1); +uint8_t gpio_in(int pin) { + volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin / 32; + return (((*reg) >> (pin % 32)) & 1); } // Display the values in a GPIO mode register -void disp_mode_vals(uint32_t mode) -{ - int i; +void disp_mode_vals(uint32_t mode) { + int i; - for (i=0; i<10; i++) - printf("%u:%-4s ", i, gpio_mode_strs[(mode>>(i*3)) & 7]); - printf("\n"); + for (i = 0; i < 10; i++) + printf("%u:%-4s ", i, gpio_mode_strs[(mode >> (i * 3)) & 7]); + printf("\n"); } // ----- VIDEOCORE MAILBOX ----- // Open mailbox interface, return file descriptor -int open_mbox(void) -{ - int fd; +int open_mbox(void) { + int fd; - if ((fd = open("/dev/vcio", 0)) < 0) - fail("Error: can't open VC mailbox\n"); - return(fd); + if ((fd = open("/dev/vcio", 0)) < 0) + fail("Error: can't open VC mailbox\n"); + return (fd); } // Close mailbox interface -void close_mbox(int fd) -{ - if (fd >= 0) - close(fd); +void close_mbox(int fd) { + if (fd >= 0) + close(fd); } // Send message to mailbox, return first response int, 0 if error -uint32_t msg_mbox(int fd, VC_MSG *msgp) -{ - uint32_t ret=0, i; +uint32_t msg_mbox(int fd, VC_MSG *msgp) { + uint32_t ret = 0, i; - for (i=msgp->dlen/4; i<=msgp->blen/4; i+=4) - msgp->uints[i++] = 0; - msgp->len = (msgp->blen + 6) * 4; - msgp->req = 0; - if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0) - printf("VC IOCTL failed\n"); - else if ((msgp->req&0x80000000) == 0) - printf("VC IOCTL error\n"); - else if (msgp->req == 0x80000001) - printf("VC IOCTL partial error\n"); - else - ret = msgp->uints[0]; + for (i = msgp->dlen / 4; i <= msgp->blen / 4; i += 4) + msgp->uints[i++] = 0; + msgp->len = (msgp->blen + 6) * 4; + msgp->req = 0; + if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0) + printf("VC IOCTL failed\n"); + else if ((msgp->req & 0x80000000) == 0) + printf("VC IOCTL error\n"); + else if (msgp->req == 0x80000001) + printf("VC IOCTL partial error\n"); + else + ret = msgp->uints[0]; #if DEBUG - disp_vc_msg(msgp); + disp_vc_msg(msgp); #endif - return(ret); + return (ret); } // Allocate memory on PAGE_SIZE boundary, return handle -uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags) -{ - VC_MSG msg={.tag=0x3000c, .blen=12, .dlen=12, - .uints={PAGE_ROUNDUP(size), PAGE_SIZE, flags}}; - return(msg_mbox(fd, &msg)); +uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags) { + VC_MSG msg = { + .tag = 0x3000c, .blen = 12, .dlen = 12, .uints = {PAGE_ROUNDUP(size), PAGE_SIZE, flags}}; + return (msg_mbox(fd, &msg)); } // Lock allocated memory, return bus address -void *lock_vc_mem(int fd, int h) -{ - VC_MSG msg={.tag=0x3000d, .blen=4, .dlen=4, .uints={h}}; - return(h ? (void *)msg_mbox(fd, &msg) : 0); +void *lock_vc_mem(int fd, int h) { + VC_MSG msg = {.tag = 0x3000d, .blen = 4, .dlen = 4, .uints = {h}}; + return (h ? (void *)msg_mbox(fd, &msg) : 0); } // Unlock allocated memory -uint32_t unlock_vc_mem(int fd, int h) -{ - VC_MSG msg={.tag=0x3000e, .blen=4, .dlen=4, .uints={h}}; - return(h ? msg_mbox(fd, &msg) : 0); +uint32_t unlock_vc_mem(int fd, int h) { + VC_MSG msg = {.tag = 0x3000e, .blen = 4, .dlen = 4, .uints = {h}}; + return (h ? msg_mbox(fd, &msg) : 0); } // Free memory -uint32_t free_vc_mem(int fd, int h) -{ - VC_MSG msg={.tag=0x3000f, .blen=4, .dlen=4, .uints={h}}; - return(h ? msg_mbox(fd, &msg) : 0); +uint32_t free_vc_mem(int fd, int h) { + VC_MSG msg = {.tag = 0x3000f, .blen = 4, .dlen = 4, .uints = {h}}; + return (h ? msg_mbox(fd, &msg) : 0); } -uint32_t set_vc_clock(int fd, int id, uint32_t freq) -{ - VC_MSG msg1={.tag=0x38001, .blen=8, .dlen=8, .uints={id, 1}}; - VC_MSG msg2={.tag=0x38002, .blen=12, .dlen=12, .uints={id, freq, 0}}; - msg_mbox(fd, &msg1); - disp_vc_msg(&msg1); - msg_mbox(fd, &msg2); - disp_vc_msg(&msg2); - return(0); +uint32_t set_vc_clock(int fd, int id, uint32_t freq) { + VC_MSG msg1 = {.tag = 0x38001, .blen = 8, .dlen = 8, .uints = {id, 1}}; + VC_MSG msg2 = {.tag = 0x38002, .blen = 12, .dlen = 12, .uints = {id, freq, 0}}; + msg_mbox(fd, &msg1); + disp_vc_msg(&msg1); + msg_mbox(fd, &msg2); + disp_vc_msg(&msg2); + return (0); } // Display mailbox message -void disp_vc_msg(VC_MSG *msgp) -{ - int i; +void disp_vc_msg(VC_MSG *msgp) { + int i; - printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ", - msgp->len, msgp->req, msgp->tag, msgp->blen, msgp->dlen); - for (i=0; iblen/4; i++) - printf("%08X ", msgp->uints[i]); - printf("\n"); + printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ", msgp->len, msgp->req, msgp->tag, + msgp->blen, msgp->dlen); + for (i = 0; i < msgp->blen / 4; i++) + printf("%08X ", msgp->uints[i]); + printf("\n"); } // ----- VIRTUAL MEMORY ----- // Get virtual memory segment for peripheral regs or physical mem -void *map_segment(void *addr, int size) -{ - int fd; - void *mem; +void *map_segment(void *addr, int size) { + int fd; + void *mem; - size = PAGE_ROUNDUP(size); - if ((fd = open ("/dev/mem", O_RDWR|O_SYNC|O_CLOEXEC)) < 0) - fail("Error: can't open /dev/mem, run using sudo\n"); - mem = mmap(0, size, PROT_WRITE|PROT_READ, MAP_SHARED, fd, (uint32_t)addr); - close(fd); + size = PAGE_ROUNDUP(size); + if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) + fail("Error: can't open /dev/mem, run using sudo\n"); + mem = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr); + close(fd); #if DEBUG - printf("Map %p -> %p\n", (void *)addr, mem); + printf("Map %p -> %p\n", (void *)addr, mem); #endif - if (mem == MAP_FAILED) - fail("Error: can't map memory\n"); - return(mem); + if (mem == MAP_FAILED) + fail("Error: can't map memory\n"); + return (mem); } // Free mapped memory -void unmap_segment(void *mem, int size) -{ - if (mem) - munmap(mem, PAGE_ROUNDUP(size)); +void unmap_segment(void *mem, int size) { + if (mem) + munmap(mem, PAGE_ROUNDUP(size)); } // ----- DMA ----- // Enable and reset DMA -void enable_dma(int chan) -{ - *REG32(dma_regs, DMA_ENABLE) |= (1 << chan); - *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31; +void enable_dma(int chan) { + *REG32(dma_regs, DMA_ENABLE) |= (1 << chan); + *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31; } // Start DMA, given first control block -void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval) -{ - *REG32(dma_regs, DMA_REG(chan, DMA_CONBLK_AD)) = MEM_BUS_ADDR(mp, cbp); - *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 2; // Clear 'end' flag - *REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7; // Clear error bits - *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1|csval; // Start DMA +void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval) { + *REG32(dma_regs, DMA_REG(chan, DMA_CONBLK_AD)) = MEM_BUS_ADDR(mp, cbp); + *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 2; // Clear 'end' flag + *REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7; // Clear error bits + *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 | csval; // Start DMA } // Return remaining transfer length -uint32_t dma_transfer_len(int chan) -{ - return(*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN))); -} +uint32_t dma_transfer_len(int chan) { return (*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN))); } // Check if DMA is active -uint32_t dma_active(int chan) -{ - return((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1); -} +uint32_t dma_active(int chan) { return ((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1); } // Halt current DMA operation by resetting controller -void stop_dma(int chan) -{ - if (dma_regs.virt) - *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31; +void stop_dma(int chan) { + if (dma_regs.virt) + *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31; } // Display DMA registers -void disp_dma(int chan) -{ - volatile uint32_t *p=REG32(dma_regs, DMA_REG(chan, DMA_CS)); - int i=0; +void disp_dma(int chan) { + volatile uint32_t *p = REG32(dma_regs, DMA_REG(chan, DMA_CS)); + int i = 0; - while (dma_regstrs[i][0]) - { - printf("%-7s %08X ", dma_regstrs[i++], *p++); - if (i%5==0 || dma_regstrs[i][0]==0) - printf("\n"); - } + while (dma_regstrs[i][0]) { + printf("%-7s %08X ", dma_regstrs[i++], *p++); + if (i % 5 == 0 || dma_regstrs[i][0] == 0) + printf("\n"); + } } // ----- PWM ----- // Initialise PWM -void init_pwm(int freq, int range, int val) -{ - stop_pwm(); - if (*REG32(pwm_regs, PWM_STA) & 0x100) - { - printf("PWM bus error\n"); - *REG32(pwm_regs, PWM_STA) = 0x100; - } +void init_pwm(int freq, int range, int val) { + stop_pwm(); + if (*REG32(pwm_regs, PWM_STA) & 0x100) { + printf("PWM bus error\n"); + *REG32(pwm_regs, PWM_STA) = 0x100; + } #if USE_VC_CLOCK_SET - set_vc_clock(mbox_fd, PWM_CLOCK_ID, freq); + set_vc_clock(mbox_fd, PWM_CLOCK_ID, freq); #else - int divi=CLOCK_HZ / freq; - *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | (1 << 5); - while (*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) ; - *REG32(clk_regs, CLK_PWM_DIV) = CLK_PASSWD | (divi << 12); - *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4); - while ((*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) == 0) ; + int divi = CLOCK_HZ / freq; + *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | (1 << 5); + while (*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) + ; + *REG32(clk_regs, CLK_PWM_DIV) = CLK_PASSWD | (divi << 12); + *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4); + while ((*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) == 0) + ; #endif - usleep(100); - *REG32(pwm_regs, PWM_RNG1) = range; - *REG32(pwm_regs, PWM_FIF1) = val; + usleep(100); + *REG32(pwm_regs, PWM_RNG1) = range; + *REG32(pwm_regs, PWM_FIF1) = val; #if PWM_OUT - gpio_mode(PWM_PIN, PWM_PIN==12 ? GPIO_ALT0 : GPIO_ALT5); + gpio_mode(PWM_PIN, PWM_PIN == 12 ? GPIO_ALT0 : GPIO_ALT5); #endif } // Start PWM operation -void start_pwm(void) -{ - *REG32(pwm_regs, PWM_CTL) = PWM_CTL_USEF1 | PWM_ENAB; -} +void start_pwm(void) { *REG32(pwm_regs, PWM_CTL) = PWM_CTL_USEF1 | PWM_ENAB; } // Stop PWM operation -void stop_pwm(void) -{ - if (pwm_regs.virt) - { - *REG32(pwm_regs, PWM_CTL) = 0; - usleep(100); - } +void stop_pwm(void) { + if (pwm_regs.virt) { + *REG32(pwm_regs, PWM_CTL) = 0; + usleep(100); + } } // EOF diff --git a/RpiLedBars/src/rpi_dma_utils.h b/RpiLedBars/src/rpi_dma_utils.h index d618b63..078755b 100644 --- a/RpiLedBars/src/rpi_dma_utils.h +++ b/RpiLedBars/src/rpi_dma_utils.h @@ -15,158 +15,160 @@ // limitations under the License. // -// Location of peripheral registers in physical memory -#define PHYS_REG_BASE PI_23_REG_BASE -#define PI_01_REG_BASE 0x20000000 // Pi Zero or 1 -#define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3 -#define PI_4_REG_BASE 0xFE000000 // Pi 4 +#include -#define CLOCK_HZ 250000000 // Pi 2 - 4 +// Location of peripheral registers in physical memory +#define PHYS_REG_BASE PI_23_REG_BASE +#define PI_01_REG_BASE 0x20000000 // Pi Zero or 1 +#define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3 +#define PI_4_REG_BASE 0xFE000000 // Pi 4 + +#define CLOCK_HZ 250000000 // Pi 2 - 4 //#define CLOCK_HZ 400000000 // Pi Zero // Location of peripheral registers in bus memory -#define BUS_REG_BASE 0x7E000000 +#define BUS_REG_BASE 0x7E000000 // If non-zero, print debug information -#define DEBUG 0 +#define DEBUG 0 // If non-zero, set PWM clock using VideoCore mailbox #define USE_VC_CLOCK_SET 0 // Size of memory page -#define PAGE_SIZE 0x1000 +#define PAGE_SIZE 0x1000 // Round up to nearest page -#define PAGE_ROUNDUP(n) ((n)%PAGE_SIZE==0 ? (n) : ((n)+PAGE_SIZE)&~(PAGE_SIZE-1)) +#define PAGE_ROUNDUP(n) ((n) % PAGE_SIZE == 0 ? (n) : ((n) + PAGE_SIZE) & ~(PAGE_SIZE - 1)) // Structure for mapped peripheral or memory typedef struct { - int fd, // File descriptor - h, // Memory handle - size; // Memory size - void *bus, // Bus address - *virt, // Virtual address - *phys; // Physical address + int fd, // File descriptor + h, // Memory handle + size; // Memory size + void *bus, // Bus address + *virt, // Virtual address + *phys; // Physical address } MEM_MAP; // Get virtual 8 and 32-bit pointers to register -#define REG8(m, x) ((volatile uint8_t *) ((uint32_t)(m.virt)+(uint32_t)(x))) -#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt)+(uint32_t)(x))) +#define REG8(m, x) ((volatile uint8_t *)((uint32_t)(m.virt) + (uint32_t)(x))) +#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt) + (uint32_t)(x))) // Get bus address of register -#define REG_BUS_ADDR(m, x) ((uint32_t)(m.bus) + (uint32_t)(x)) +#define REG_BUS_ADDR(m, x) ((uint32_t)(m.bus) + (uint32_t)(x)) // Convert uncached memory virtual address to bus address -#define MEM_BUS_ADDR(mp, a) ((uint32_t)a-(uint32_t)mp->virt+(uint32_t)mp->bus) +#define MEM_BUS_ADDR(mp, a) ((uint32_t)a - (uint32_t)mp->virt + (uint32_t)mp->bus) // Convert bus address to physical address (for mmap) -#define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a)&~0xC0000000)) +#define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a) & ~0xC0000000)) // GPIO register definitions -#define GPIO_BASE (PHYS_REG_BASE + 0x200000) -#define GPIO_MODE0 0x00 -#define GPIO_SET0 0x1c -#define GPIO_CLR0 0x28 -#define GPIO_LEV0 0x34 -#define GPIO_GPPUD 0x94 -#define GPIO_GPPUDCLK0 0x98 +#define GPIO_BASE (PHYS_REG_BASE + 0x200000) +#define GPIO_MODE0 0x00 +#define GPIO_SET0 0x1c +#define GPIO_CLR0 0x28 +#define GPIO_LEV0 0x34 +#define GPIO_GPPUD 0x94 +#define GPIO_GPPUDCLK0 0x98 // GPIO I/O definitions -#define GPIO_IN 0 -#define GPIO_OUT 1 -#define GPIO_ALT0 4 -#define GPIO_ALT1 5 -#define GPIO_ALT2 6 -#define GPIO_ALT3 7 -#define GPIO_ALT4 3 -#define GPIO_ALT5 2 -#define GPIO_MODE_STRS "IN","OUT","ALT5","ALT4","ALT0","ALT1","ALT2","ALT3" -#define GPIO_NOPULL 0 -#define GPIO_PULLDN 1 -#define GPIO_PULLUP 2 +#define GPIO_IN 0 +#define GPIO_OUT 1 +#define GPIO_ALT0 4 +#define GPIO_ALT1 5 +#define GPIO_ALT2 6 +#define GPIO_ALT3 7 +#define GPIO_ALT4 3 +#define GPIO_ALT5 2 +#define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3" +#define GPIO_NOPULL 0 +#define GPIO_PULLDN 1 +#define GPIO_PULLUP 2 // Videocore mailbox memory allocation flags, see: // https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface typedef enum { - MEM_FLAG_DISCARDABLE = 1<<0, // can be resized to 0 at any time. Use for cached data - MEM_FLAG_NORMAL = 0<<2, // normal allocating alias. Don't use from ARM - MEM_FLAG_DIRECT = 1<<2, // 0xC alias uncached - MEM_FLAG_COHERENT = 2<<2, // 0x8 alias. Non-allocating in L2 but coherent - MEM_FLAG_ZERO = 1<<4, // initialise buffer to all zeros - MEM_FLAG_NO_INIT = 1<<5, // don't initialise (default is initialise to all ones) - MEM_FLAG_HINT_PERMALOCK = 1<<6, // Likely to be locked for long periods of time - MEM_FLAG_L1_NONALLOCATING=(MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2 + MEM_FLAG_DISCARDABLE = 1 << 0, // can be resized to 0 at any time. Use for cached data + MEM_FLAG_NORMAL = 0 << 2, // normal allocating alias. Don't use from ARM + MEM_FLAG_DIRECT = 1 << 2, // 0xC alias uncached + MEM_FLAG_COHERENT = 2 << 2, // 0x8 alias. Non-allocating in L2 but coherent + MEM_FLAG_ZERO = 1 << 4, // initialise buffer to all zeros + MEM_FLAG_NO_INIT = 1 << 5, // don't initialise (default is initialise to all ones) + MEM_FLAG_HINT_PERMALOCK = 1 << 6, // Likely to be locked for long periods of time + MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2 } VC_ALLOC_FLAGS; // VC flags for unchached DMA memory -#define DMA_MEM_FLAGS (MEM_FLAG_DIRECT|MEM_FLAG_ZERO) +#define DMA_MEM_FLAGS (MEM_FLAG_DIRECT | MEM_FLAG_ZERO) // Mailbox command/response structure typedef struct { - uint32_t len, // Overall length (bytes) - req, // Zero for request, 1<<31 for response - tag, // Command number - blen, // Buffer length (bytes) - dlen; // Data length (bytes) - uint32_t uints[32-5]; // Data (108 bytes maximum) -} VC_MSG __attribute__ ((aligned (16))); + uint32_t len, // Overall length (bytes) + req, // Zero for request, 1<<31 for response + tag, // Command number + blen, // Buffer length (bytes) + dlen; // Data length (bytes) + uint32_t uints[32 - 5]; // Data (108 bytes maximum) +} VC_MSG __attribute__((aligned(16))); // DMA channels and data requests -#define DMA_CHAN_A 10 -#define DMA_CHAN_B 11 -#define DMA_PWM_DREQ 5 +#define DMA_CHAN_A 10 +#define DMA_CHAN_B 11 +#define DMA_PWM_DREQ 5 #define DMA_SPI_TX_DREQ 6 #define DMA_SPI_RX_DREQ 7 -#define DMA_BASE (PHYS_REG_BASE + 0x007000) +#define DMA_BASE (PHYS_REG_BASE + 0x007000) // DMA register addresses offset by 0x100 * chan_num -#define DMA_CS 0x00 -#define DMA_CONBLK_AD 0x04 -#define DMA_TI 0x08 -#define DMA_SRCE_AD 0x0c -#define DMA_DEST_AD 0x10 -#define DMA_TXFR_LEN 0x14 -#define DMA_STRIDE 0x18 -#define DMA_NEXTCONBK 0x1c -#define DMA_DEBUG 0x20 -#define DMA_REG(ch, r) ((r)==DMA_ENABLE ? DMA_ENABLE : (ch)*0x100+(r)) -#define DMA_ENABLE 0xff0 +#define DMA_CS 0x00 +#define DMA_CONBLK_AD 0x04 +#define DMA_TI 0x08 +#define DMA_SRCE_AD 0x0c +#define DMA_DEST_AD 0x10 +#define DMA_TXFR_LEN 0x14 +#define DMA_STRIDE 0x18 +#define DMA_NEXTCONBK 0x1c +#define DMA_DEBUG 0x20 +#define DMA_REG(ch, r) ((r) == DMA_ENABLE ? DMA_ENABLE : (ch)*0x100 + (r)) +#define DMA_ENABLE 0xff0 // DMA register values -#define DMA_WAIT_RESP (1 << 3) +#define DMA_WAIT_RESP (1 << 3) #define DMA_CB_DEST_INC (1 << 4) -#define DMA_DEST_DREQ (1 << 6) +#define DMA_DEST_DREQ (1 << 6) #define DMA_CB_SRCE_INC (1 << 8) -#define DMA_SRCE_DREQ (1 << 10) +#define DMA_SRCE_DREQ (1 << 10) #define DMA_PRIORITY(n) ((n) << 16) // DMA control block (must be 32-byte aligned) typedef struct { - uint32_t ti, // Transfer info - srce_ad, // Source address - dest_ad, // Destination address - tfr_len, // Transfer length - stride, // Transfer stride - next_cb, // Next control block - debug, // Debug register, zero in control block - unused; -} DMA_CB __attribute__ ((aligned(32))); + uint32_t ti, // Transfer info + srce_ad, // Source address + dest_ad, // Destination address + tfr_len, // Transfer length + stride, // Transfer stride + next_cb, // Next control block + debug, // Debug register, zero in control block + unused; +} DMA_CB __attribute__((aligned(32))); // PWM controller registers -#define PWM_BASE (PHYS_REG_BASE + 0x20C000) -#define PWM_CTL 0x00 // Control -#define PWM_STA 0x04 // Status -#define PWM_DMAC 0x08 // DMA control -#define PWM_RNG1 0x10 // Channel 1 range -#define PWM_DAT1 0x14 // Channel 1 data -#define PWM_FIF1 0x18 // Channel 1 fifo -#define PWM_RNG2 0x20 // Channel 2 range -#define PWM_DAT2 0x24 // Channel 2 data +#define PWM_BASE (PHYS_REG_BASE + 0x20C000) +#define PWM_CTL 0x00 // Control +#define PWM_STA 0x04 // Status +#define PWM_DMAC 0x08 // DMA control +#define PWM_RNG1 0x10 // Channel 1 range +#define PWM_DAT1 0x14 // Channel 1 data +#define PWM_FIF1 0x18 // Channel 1 fifo +#define PWM_RNG2 0x20 // Channel 2 range +#define PWM_DAT2 0x24 // Channel 2 data // PWM register values -#define PWM_CTL_RPTL1 (1<<2) // Chan 1: repeat last data when FIFO empty -#define PWM_CTL_USEF1 (1<<5) // Chan 1: use FIFO -#define PWM_DMAC_ENAB (1<<31) // Start PWM DMA -#define PWM_ENAB 1 // Enable PWM -#define PWM_PIN 12 // GPIO pin for PWM output, 12 or 18 +#define PWM_CTL_RPTL1 (1 << 2) // Chan 1: repeat last data when FIFO empty +#define PWM_CTL_USEF1 (1 << 5) // Chan 1: use FIFO +#define PWM_DMAC_ENAB (1 << 31) // Start PWM DMA +#define PWM_ENAB 1 // Enable PWM +#define PWM_PIN 12 // GPIO pin for PWM output, 12 or 18 // Clock registers and values -#define CLK_BASE (PHYS_REG_BASE + 0x101000) -#define CLK_PWM_CTL 0xa0 -#define CLK_PWM_DIV 0xa4 -#define CLK_PASSWD 0x5a000000 -#define PWM_CLOCK_ID 0xa +#define CLK_BASE (PHYS_REG_BASE + 0x101000) +#define CLK_PWM_CTL 0xa0 +#define CLK_PWM_DIV 0xa4 +#define CLK_PASSWD 0x5a000000 +#define PWM_CLOCK_ID 0xa void fail(char *s); void *map_periph(MEM_MAP *mp, void *phys, int size); diff --git a/RpiLedBars/src/rpi_pixleds.c b/RpiLedBars/src/rpi_pixleds.c index 5f2a1d7..58f7b99 100644 --- a/RpiLedBars/src/rpi_pixleds.c +++ b/RpiLedBars/src/rpi_pixleds.c @@ -30,19 +30,20 @@ // v0.11 JPB 29/9/20 Added enable_dma before transfer (in case still active) // Corrected DMA nsamp value (was byte count) -#include "rpi_dma_utils.h" -#include "rpi_smi_defs.h" #include #include #include #include -#include #include #include #include #include #include +#include "rpi_artnet.h" +#include "rpi_dma_utils.h" +#include "rpi_smi_defs.h" + #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 #else // Timings for RPi v0-3 (1 GHz) @@ -95,8 +96,6 @@ volatile SMI_DCD_REG *smi_dcd; #define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T)) #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) int on_rgbs[16] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040, 0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040}; @@ -112,7 +111,6 @@ TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data int testmode, chan_ledcount = 1; // Command-line parameters int rgb_data[CHAN_MAXLEDS][LED_NCHANS]; // RGB data int chan_num; // Current channel for data I/P -int udpSocket; void rgb_txdata(int *rgbs, TXDATA_T *txd); int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan); @@ -165,29 +163,10 @@ int main(int argc, char *argv[]) { printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting", chan_ledcount, chan_ledcount == 1 ? "" : "s", LED_NCHANS); - int nBytes; - char buffer[1024]; - struct sockaddr_in serverAddr; - struct sockaddr_storage serverStorage; - socklen_t addr_size; - - /*Create UDP socket*/ - 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); + artnet_init(); + // loops if (testmode) { while (1) { @@ -208,42 +187,32 @@ int main(int argc, char *argv[]) { } } 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)]); - } + artDmx_t *artDmx; + if (artnet_read(&artDmx) == OpDmx) { + uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo; + unsigned int ledCountInFrame = dmxLength / 3; + uint16_t maxBound = ledCountInFrame < chan_ledcount ? ledCountInFrame : chan_ledcount; + unsigned int universe = artDmx->subUni & (LED_NCHANS - 1); + for (size_t i = 0; i < maxBound; ++i) { + uint8_t *rgb = artDmx->data + (i * 3); + rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; + rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]); + } #if LED_NCHANS <= 8 - swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount)); + 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); - } - } + 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); } } } terminate(0); - return (0); } // Convert RGB text string into integer data, for given channel @@ -317,8 +286,6 @@ void terminate(int sig) { int i; printf("Closing\n"); - close(udpSocket); - for (size_t i = 0; i < chan_ledcount; ++i) { rgb_txdata(off_rgbs, &tx_buffer[LED_TX_OSET(i)]); } diff --git a/RpiLedBars/src/rpi_smi_defs.h b/RpiLedBars/src/rpi_smi_defs.h index 49f734f..80c452b 100644 --- a/RpiLedBars/src/rpi_smi_defs.h +++ b/RpiLedBars/src/rpi_smi_defs.h @@ -3,98 +3,113 @@ // v0.01 JPB 12/7/20 Adapted from rpi_smi_test v0.19 // Register definitions -#define SMI_BASE (PHYS_REG_BASE + 0x600000) -#define SMI_CS 0x00 // Control & status -#define SMI_L 0x04 // Transfer length -#define SMI_A 0x08 // Address -#define SMI_D 0x0c // Data -#define SMI_DSR0 0x10 // Read settings device 0 -#define SMI_DSW0 0x14 // Write settings device 0 -#define SMI_DSR1 0x18 // Read settings device 1 -#define SMI_DSW1 0x1c // Write settings device 1 -#define SMI_DSR2 0x20 // Read settings device 2 -#define SMI_DSW2 0x24 // Write settings device 2 -#define SMI_DSR3 0x28 // Read settings device 3 -#define SMI_DSW3 0x2c // Write settings device 3 -#define SMI_DMC 0x30 // DMA control -#define SMI_DCS 0x34 // Direct control/status -#define SMI_DCA 0x38 // Direct address -#define SMI_DCD 0x3c // Direct data -#define SMI_FD 0x40 // FIFO debug -#define SMI_REGLEN (SMI_FD * 4) +#define SMI_BASE (PHYS_REG_BASE + 0x600000) +#define SMI_CS 0x00 // Control & status +#define SMI_L 0x04 // Transfer length +#define SMI_A 0x08 // Address +#define SMI_D 0x0c // Data +#define SMI_DSR0 0x10 // Read settings device 0 +#define SMI_DSW0 0x14 // Write settings device 0 +#define SMI_DSR1 0x18 // Read settings device 1 +#define SMI_DSW1 0x1c // Write settings device 1 +#define SMI_DSR2 0x20 // Read settings device 2 +#define SMI_DSW2 0x24 // Write settings device 2 +#define SMI_DSR3 0x28 // Read settings device 3 +#define SMI_DSW3 0x2c // Write settings device 3 +#define SMI_DMC 0x30 // DMA control +#define SMI_DCS 0x34 // Direct control/status +#define SMI_DCA 0x38 // Direct address +#define SMI_DCD 0x3c // Direct data +#define SMI_FD 0x40 // FIFO debug +#define SMI_REGLEN (SMI_FD * 4) // Data widths -#define SMI_8_BITS 0 +#define SMI_8_BITS 0 #define SMI_16_BITS 1 #define SMI_18_BITS 2 -#define SMI_9_BITS 3 +#define SMI_9_BITS 3 // DMA request #define DMA_SMI_DREQ 4 // Union of 32-bit value with register bitfields -#define REG_DEF(name, fields) typedef union {struct {volatile uint32_t fields;}; volatile uint32_t value;} name +#define REG_DEF(name, fields) \ + typedef union { \ + struct { \ + volatile uint32_t fields; \ + }; \ + volatile uint32_t value; \ + } name // Control and status register -#define SMI_CS_FIELDS \ - enable:1, done:1, active:1, start:1, clear:1, write:1, _x1:2,\ - teen:1, intd:1, intt:1, intr:1, pvmode:1, seterr:1, pxldat:1, edreq:1,\ - _x2:8, _x3:1, aferr:1, txw:1, rxr:1, txd:1, rxd:1, txe:1, rxf:1 +#define SMI_CS_FIELDS \ + enable: \ + 1, done : 1, active : 1, start : 1, clear : 1, write : 1, _x1 : 2, teen : 1, intd : 1, intt : 1, \ + intr : 1, pvmode : 1, seterr : 1, pxldat : 1, edreq : 1, _x2 : 8, _x3 : 1, aferr : 1, \ + txw : 1, rxr : 1, txd : 1, rxd : 1, txe : 1, rxf : 1 REG_DEF(SMI_CS_REG, SMI_CS_FIELDS); // Data length register -#define SMI_L_FIELDS \ - len:32 +#define SMI_L_FIELDS \ + len: \ + 32 REG_DEF(SMI_L_REG, SMI_L_FIELDS); // Address & device number -#define SMI_A_FIELDS \ - addr:6, _x1:2, dev:2 +#define SMI_A_FIELDS \ + addr: \ + 6, _x1 : 2, dev : 2 REG_DEF(SMI_A_REG, SMI_A_FIELDS); // Data FIFO -#define SMI_D_FIELDS \ - data:32 +#define SMI_D_FIELDS \ + data: \ + 32 REG_DEF(SMI_D_REG, SMI_D_FIELDS); // DMA control register -#define SMI_DMC_FIELDS \ - reqw:6, reqr:6, panicw:6, panicr:6, dmap:1, _x1:3, dmaen:1 +#define SMI_DMC_FIELDS \ + reqw: \ + 6, reqr : 6, panicw : 6, panicr : 6, dmap : 1, _x1 : 3, dmaen : 1 REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS); // Device settings: read (1 of 4) -#define SMI_DSR_FIELDS \ - rstrobe:7, rdreq:1, rpace:7, rpaceall:1, \ - rhold:6, fsetup:1, mode68:1, rsetup:6, rwidth:2 +#define SMI_DSR_FIELDS \ + rstrobe: \ + 7, rdreq : 1, rpace : 7, rpaceall : 1, rhold : 6, fsetup : 1, mode68 : 1, rsetup : 6, rwidth : 2 REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS); // Device settings: write (1 of 4) -#define SMI_DSW_FIELDS \ - wstrobe:7, wdreq:1, wpace:7, wpaceall:1, \ - whold:6, wswap:1, wformat:1, wsetup:6, wwidth:2 +#define SMI_DSW_FIELDS \ + wstrobe: \ + 7, wdreq : 1, wpace : 7, wpaceall : 1, whold : 6, wswap : 1, wformat : 1, wsetup : 6, wwidth : 2 REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS); // Direct control register -#define SMI_DCS_FIELDS \ - enable:1, start:1, done:1, write:1 +#define SMI_DCS_FIELDS \ + enable: \ + 1, start : 1, done : 1, write : 1 REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS); // Direct control address & device number -#define SMI_DCA_FIELDS \ - addr:6, _x1:2, dev:2 +#define SMI_DCA_FIELDS \ + addr: \ + 6, _x1 : 2, dev : 2 REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS); // Direct control data -#define SMI_DCD_FIELDS \ - data:32 +#define SMI_DCD_FIELDS \ + data: \ + 32 REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS); // Debug register -#define SMI_FLVL_FIELDS \ - fcnt:6, _x1:2, flvl:6 +#define SMI_FLVL_FIELDS \ + fcnt: \ + 6, _x1 : 2, flvl : 6 REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS); -#define CLK_SMI_CTL 0xb0 -#define CLK_SMI_DIV 0xb4 +#define CLK_SMI_CTL 0xb0 +#define CLK_SMI_DIV 0xb4 // EOF