beta with artnet 8 channels (8 universes)
This commit is contained in:
parent
e8b99665be
commit
eaf976d5a7
6
RpiLedBars/.vscode/launch.json
vendored
6
RpiLedBars/.vscode/launch.json
vendored
@ -10,7 +10,8 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/bin/pixled",
|
"program": "${workspaceFolder}/bin/pixled",
|
||||||
"args": [
|
"args": [
|
||||||
"-n", "5",
|
"-n",
|
||||||
|
"5",
|
||||||
// "-t"
|
// "-t"
|
||||||
],
|
],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
@ -25,7 +26,8 @@
|
|||||||
"ignoreFailures": true
|
"ignoreFailures": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"miDebuggerPath": "${workspaceFolder}/sgdb.sh"
|
"miDebuggerPath": "${workspaceFolder}/sgdb.sh",
|
||||||
|
"preLaunchTask": "${defaultBuildTask}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
2
RpiLedBars/.vscode/tasks.json
vendored
2
RpiLedBars/.vscode/tasks.json
vendored
@ -4,7 +4,7 @@
|
|||||||
{
|
{
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"label": "Build project",
|
"label": "Build project",
|
||||||
"command": "make",
|
"command": "make clean; make",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@ SRC=$(notdir $(wildcard $(SRCDIR)/*.c))
|
|||||||
OBJ=$(SRC:.c=.o)
|
OBJ=$(SRC:.c=.o)
|
||||||
BIN=pixled
|
BIN=pixled
|
||||||
|
|
||||||
all: clean $(addprefix $(BINDIR)/, $(BIN))
|
all: $(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
|
||||||
|
24
RpiLedBars/README.md
Normal file
24
RpiLedBars/README.md
Normal file
@ -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/
|
70
RpiLedBars/src/rpi_artnet.c
Normal file
70
RpiLedBars/src/rpi_artnet.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include "rpi_artnet.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#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));
|
||||||
|
}
|
10
RpiLedBars/src/rpi_artnet.h
Normal file
10
RpiLedBars/src/rpi_artnet.h
Normal file
@ -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__
|
128
RpiLedBars/src/rpi_artnet_op_codes.h
Normal file
128
RpiLedBars/src/rpi_artnet_op_codes.h
Normal file
@ -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__
|
178
RpiLedBars/src/rpi_artnet_packets.h
Normal file
178
RpiLedBars/src/rpi_artnet_packets.h
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
#if !defined(__RPI_ARTNET_PACKETS_H__)
|
||||||
|
#define __RPI_ARTNET_PACKETS_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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__
|
25
RpiLedBars/src/rpi_artnet_utils.h
Normal file
25
RpiLedBars/src/rpi_artnet_utils.h
Normal file
@ -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__
|
@ -14,15 +14,14 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "rpi_dma_utils.h"
|
#include "rpi_dma_utils.h"
|
||||||
|
|
||||||
@ -39,8 +38,7 @@ char *gpio_mode_strs[] = {GPIO_MODE_STRS};
|
|||||||
MEM_MAP pwm_regs, gpio_regs, dma_regs, clk_regs;
|
MEM_MAP pwm_regs, gpio_regs, dma_regs, clk_regs;
|
||||||
|
|
||||||
// Use mmap to obtain virtual address, given physical
|
// Use mmap to obtain virtual address, given physical
|
||||||
void *map_periph(MEM_MAP *mp, void *phys, int size)
|
void *map_periph(MEM_MAP *mp, void *phys, int size) {
|
||||||
{
|
|
||||||
mp->phys = phys;
|
mp->phys = phys;
|
||||||
mp->size = PAGE_ROUNDUP(size);
|
mp->size = PAGE_ROUNDUP(size);
|
||||||
mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
|
mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
|
||||||
@ -49,32 +47,28 @@ void *map_periph(MEM_MAP *mp, void *phys, int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate uncached memory, get bus & phys addresses
|
// Allocate uncached memory, get bus & phys addresses
|
||||||
void *map_uncached_mem(MEM_MAP *mp, int size)
|
void *map_uncached_mem(MEM_MAP *mp, int size) {
|
||||||
{
|
|
||||||
void *ret;
|
void *ret;
|
||||||
mp->size = PAGE_ROUNDUP(size);
|
mp->size = PAGE_ROUNDUP(size);
|
||||||
mp->fd = open_mbox();
|
mp->fd = open_mbox();
|
||||||
ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 &&
|
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->bus = lock_vc_mem(mp->fd, mp->h)) != 0 &&
|
||||||
(mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0
|
(mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0
|
||||||
? mp->virt : 0;
|
? mp->virt
|
||||||
|
: 0;
|
||||||
printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt);
|
printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt);
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free mapped peripheral or memory
|
// Free mapped peripheral or memory
|
||||||
void unmap_periph_mem(MEM_MAP *mp)
|
void unmap_periph_mem(MEM_MAP *mp) {
|
||||||
{
|
if (mp) {
|
||||||
if (mp)
|
if (mp->fd) {
|
||||||
{
|
|
||||||
if (mp->fd)
|
|
||||||
{
|
|
||||||
unmap_segment(mp->virt, mp->size);
|
unmap_segment(mp->virt, mp->size);
|
||||||
unlock_vc_mem(mp->fd, mp->h);
|
unlock_vc_mem(mp->fd, mp->h);
|
||||||
free_vc_mem(mp->fd, mp->h);
|
free_vc_mem(mp->fd, mp->h);
|
||||||
close_mbox(mp->fd);
|
close_mbox(mp->fd);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
unmap_segment(mp->virt, mp->size);
|
unmap_segment(mp->virt, mp->size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,14 +76,12 @@ void unmap_periph_mem(MEM_MAP *mp)
|
|||||||
// ----- GPIO -----
|
// ----- GPIO -----
|
||||||
|
|
||||||
// Set input or output with pullups
|
// Set input or output with pullups
|
||||||
void gpio_set(int pin, int mode, int pull)
|
void gpio_set(int pin, int mode, int pull) {
|
||||||
{
|
|
||||||
gpio_mode(pin, mode);
|
gpio_mode(pin, mode);
|
||||||
gpio_pull(pin, pull);
|
gpio_pull(pin, pull);
|
||||||
}
|
}
|
||||||
// Set I/P pullup or pulldown
|
// Set I/P pullup or pulldown
|
||||||
void gpio_pull(int pin, int pull)
|
void gpio_pull(int pin, int pull) {
|
||||||
{
|
|
||||||
volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
|
||||||
|
|
||||||
*REG32(gpio_regs, GPIO_GPPUD) = pull;
|
*REG32(gpio_regs, GPIO_GPPUD) = pull;
|
||||||
@ -101,29 +93,25 @@ void gpio_pull(int pin, int pull)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set input or output
|
// Set input or output
|
||||||
void gpio_mode(int pin, int mode)
|
void gpio_mode(int pin, int mode) {
|
||||||
{
|
|
||||||
volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3;
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3;
|
||||||
*reg = (*reg & ~(7 << shift)) | (mode << shift);
|
*reg = (*reg & ~(7 << shift)) | (mode << shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set an O/P pin
|
// Set an O/P pin
|
||||||
void gpio_out(int pin, int val)
|
void gpio_out(int pin, int val) {
|
||||||
{
|
|
||||||
volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin / 32;
|
volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin / 32;
|
||||||
*reg = 1 << (pin % 32);
|
*reg = 1 << (pin % 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an I/P pin value
|
// Get an I/P pin value
|
||||||
uint8_t gpio_in(int pin)
|
uint8_t gpio_in(int pin) {
|
||||||
{
|
|
||||||
volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin / 32;
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin / 32;
|
||||||
return (((*reg) >> (pin % 32)) & 1);
|
return (((*reg) >> (pin % 32)) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the values in a GPIO mode register
|
// Display the values in a GPIO mode register
|
||||||
void disp_mode_vals(uint32_t mode)
|
void disp_mode_vals(uint32_t mode) {
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < 10; i++)
|
for (i = 0; i < 10; i++)
|
||||||
@ -134,8 +122,7 @@ void disp_mode_vals(uint32_t mode)
|
|||||||
// ----- VIDEOCORE MAILBOX -----
|
// ----- VIDEOCORE MAILBOX -----
|
||||||
|
|
||||||
// Open mailbox interface, return file descriptor
|
// Open mailbox interface, return file descriptor
|
||||||
int open_mbox(void)
|
int open_mbox(void) {
|
||||||
{
|
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
if ((fd = open("/dev/vcio", 0)) < 0)
|
if ((fd = open("/dev/vcio", 0)) < 0)
|
||||||
@ -143,15 +130,13 @@ int open_mbox(void)
|
|||||||
return (fd);
|
return (fd);
|
||||||
}
|
}
|
||||||
// Close mailbox interface
|
// Close mailbox interface
|
||||||
void close_mbox(int fd)
|
void close_mbox(int fd) {
|
||||||
{
|
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send message to mailbox, return first response int, 0 if error
|
// Send message to mailbox, return first response int, 0 if error
|
||||||
uint32_t msg_mbox(int fd, VC_MSG *msgp)
|
uint32_t msg_mbox(int fd, VC_MSG *msgp) {
|
||||||
{
|
|
||||||
uint32_t ret = 0, i;
|
uint32_t ret = 0, i;
|
||||||
|
|
||||||
for (i = msgp->dlen / 4; i <= msgp->blen / 4; i += 4)
|
for (i = msgp->dlen / 4; i <= msgp->blen / 4; i += 4)
|
||||||
@ -173,32 +158,27 @@ uint32_t msg_mbox(int fd, VC_MSG *msgp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate memory on PAGE_SIZE boundary, return handle
|
// Allocate memory on PAGE_SIZE boundary, return handle
|
||||||
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags)
|
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags) {
|
||||||
{
|
VC_MSG msg = {
|
||||||
VC_MSG msg={.tag=0x3000c, .blen=12, .dlen=12,
|
.tag = 0x3000c, .blen = 12, .dlen = 12, .uints = {PAGE_ROUNDUP(size), PAGE_SIZE, flags}};
|
||||||
.uints={PAGE_ROUNDUP(size), PAGE_SIZE, flags}};
|
|
||||||
return (msg_mbox(fd, &msg));
|
return (msg_mbox(fd, &msg));
|
||||||
}
|
}
|
||||||
// Lock allocated memory, return bus address
|
// Lock allocated memory, return bus address
|
||||||
void *lock_vc_mem(int fd, int h)
|
void *lock_vc_mem(int fd, int h) {
|
||||||
{
|
|
||||||
VC_MSG msg = {.tag = 0x3000d, .blen = 4, .dlen = 4, .uints = {h}};
|
VC_MSG msg = {.tag = 0x3000d, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
return (h ? (void *)msg_mbox(fd, &msg) : 0);
|
return (h ? (void *)msg_mbox(fd, &msg) : 0);
|
||||||
}
|
}
|
||||||
// Unlock allocated memory
|
// Unlock allocated memory
|
||||||
uint32_t unlock_vc_mem(int fd, int h)
|
uint32_t unlock_vc_mem(int fd, int h) {
|
||||||
{
|
|
||||||
VC_MSG msg = {.tag = 0x3000e, .blen = 4, .dlen = 4, .uints = {h}};
|
VC_MSG msg = {.tag = 0x3000e, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
return (h ? msg_mbox(fd, &msg) : 0);
|
return (h ? msg_mbox(fd, &msg) : 0);
|
||||||
}
|
}
|
||||||
// Free memory
|
// Free memory
|
||||||
uint32_t free_vc_mem(int fd, int h)
|
uint32_t free_vc_mem(int fd, int h) {
|
||||||
{
|
|
||||||
VC_MSG msg = {.tag = 0x3000f, .blen = 4, .dlen = 4, .uints = {h}};
|
VC_MSG msg = {.tag = 0x3000f, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
return (h ? msg_mbox(fd, &msg) : 0);
|
return (h ? msg_mbox(fd, &msg) : 0);
|
||||||
}
|
}
|
||||||
uint32_t set_vc_clock(int fd, int id, uint32_t freq)
|
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 msg1 = {.tag = 0x38001, .blen = 8, .dlen = 8, .uints = {id, 1}};
|
||||||
VC_MSG msg2 = {.tag = 0x38002, .blen = 12, .dlen = 12, .uints = {id, freq, 0}};
|
VC_MSG msg2 = {.tag = 0x38002, .blen = 12, .dlen = 12, .uints = {id, freq, 0}};
|
||||||
msg_mbox(fd, &msg1);
|
msg_mbox(fd, &msg1);
|
||||||
@ -209,12 +189,11 @@ uint32_t set_vc_clock(int fd, int id, uint32_t freq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display mailbox message
|
// Display mailbox message
|
||||||
void disp_vc_msg(VC_MSG *msgp)
|
void disp_vc_msg(VC_MSG *msgp) {
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ",
|
printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ", msgp->len, msgp->req, msgp->tag,
|
||||||
msgp->len, msgp->req, msgp->tag, msgp->blen, msgp->dlen);
|
msgp->blen, msgp->dlen);
|
||||||
for (i = 0; i < msgp->blen / 4; i++)
|
for (i = 0; i < msgp->blen / 4; i++)
|
||||||
printf("%08X ", msgp->uints[i]);
|
printf("%08X ", msgp->uints[i]);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -223,8 +202,7 @@ void disp_vc_msg(VC_MSG *msgp)
|
|||||||
// ----- VIRTUAL MEMORY -----
|
// ----- VIRTUAL MEMORY -----
|
||||||
|
|
||||||
// Get virtual memory segment for peripheral regs or physical mem
|
// Get virtual memory segment for peripheral regs or physical mem
|
||||||
void *map_segment(void *addr, int size)
|
void *map_segment(void *addr, int size) {
|
||||||
{
|
|
||||||
int fd;
|
int fd;
|
||||||
void *mem;
|
void *mem;
|
||||||
|
|
||||||
@ -241,8 +219,7 @@ void *map_segment(void *addr, int size)
|
|||||||
return (mem);
|
return (mem);
|
||||||
}
|
}
|
||||||
// Free mapped memory
|
// Free mapped memory
|
||||||
void unmap_segment(void *mem, int size)
|
void unmap_segment(void *mem, int size) {
|
||||||
{
|
|
||||||
if (mem)
|
if (mem)
|
||||||
munmap(mem, PAGE_ROUNDUP(size));
|
munmap(mem, PAGE_ROUNDUP(size));
|
||||||
}
|
}
|
||||||
@ -250,15 +227,13 @@ void unmap_segment(void *mem, int size)
|
|||||||
// ----- DMA -----
|
// ----- DMA -----
|
||||||
|
|
||||||
// Enable and reset DMA
|
// Enable and reset DMA
|
||||||
void enable_dma(int chan)
|
void enable_dma(int chan) {
|
||||||
{
|
|
||||||
*REG32(dma_regs, DMA_ENABLE) |= (1 << chan);
|
*REG32(dma_regs, DMA_ENABLE) |= (1 << chan);
|
||||||
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start DMA, given first control block
|
// Start DMA, given first control block
|
||||||
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval)
|
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_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_CS)) = 2; // Clear 'end' flag
|
||||||
*REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7; // Clear error bits
|
*REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7; // Clear error bits
|
||||||
@ -266,32 +241,23 @@ void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return remaining transfer length
|
// Return remaining transfer length
|
||||||
uint32_t dma_transfer_len(int chan)
|
uint32_t dma_transfer_len(int chan) { return (*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN))); }
|
||||||
{
|
|
||||||
return(*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if DMA is active
|
// Check if DMA is active
|
||||||
uint32_t dma_active(int chan)
|
uint32_t dma_active(int chan) { return ((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1); }
|
||||||
{
|
|
||||||
return((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Halt current DMA operation by resetting controller
|
// Halt current DMA operation by resetting controller
|
||||||
void stop_dma(int chan)
|
void stop_dma(int chan) {
|
||||||
{
|
|
||||||
if (dma_regs.virt)
|
if (dma_regs.virt)
|
||||||
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display DMA registers
|
// Display DMA registers
|
||||||
void disp_dma(int chan)
|
void disp_dma(int chan) {
|
||||||
{
|
|
||||||
volatile uint32_t *p = REG32(dma_regs, DMA_REG(chan, DMA_CS));
|
volatile uint32_t *p = REG32(dma_regs, DMA_REG(chan, DMA_CS));
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (dma_regstrs[i][0])
|
while (dma_regstrs[i][0]) {
|
||||||
{
|
|
||||||
printf("%-7s %08X ", dma_regstrs[i++], *p++);
|
printf("%-7s %08X ", dma_regstrs[i++], *p++);
|
||||||
if (i % 5 == 0 || dma_regstrs[i][0] == 0)
|
if (i % 5 == 0 || dma_regstrs[i][0] == 0)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -301,11 +267,9 @@ void disp_dma(int chan)
|
|||||||
// ----- PWM -----
|
// ----- PWM -----
|
||||||
|
|
||||||
// Initialise PWM
|
// Initialise PWM
|
||||||
void init_pwm(int freq, int range, int val)
|
void init_pwm(int freq, int range, int val) {
|
||||||
{
|
|
||||||
stop_pwm();
|
stop_pwm();
|
||||||
if (*REG32(pwm_regs, PWM_STA) & 0x100)
|
if (*REG32(pwm_regs, PWM_STA) & 0x100) {
|
||||||
{
|
|
||||||
printf("PWM bus error\n");
|
printf("PWM bus error\n");
|
||||||
*REG32(pwm_regs, PWM_STA) = 0x100;
|
*REG32(pwm_regs, PWM_STA) = 0x100;
|
||||||
}
|
}
|
||||||
@ -314,10 +278,12 @@ void init_pwm(int freq, int range, int val)
|
|||||||
#else
|
#else
|
||||||
int divi = CLOCK_HZ / freq;
|
int divi = CLOCK_HZ / freq;
|
||||||
*REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | (1 << 5);
|
*REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | (1 << 5);
|
||||||
while (*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) ;
|
while (*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7))
|
||||||
|
;
|
||||||
*REG32(clk_regs, CLK_PWM_DIV) = CLK_PASSWD | (divi << 12);
|
*REG32(clk_regs, CLK_PWM_DIV) = CLK_PASSWD | (divi << 12);
|
||||||
*REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4);
|
*REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4);
|
||||||
while ((*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) == 0) ;
|
while ((*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) == 0)
|
||||||
|
;
|
||||||
#endif
|
#endif
|
||||||
usleep(100);
|
usleep(100);
|
||||||
*REG32(pwm_regs, PWM_RNG1) = range;
|
*REG32(pwm_regs, PWM_RNG1) = range;
|
||||||
@ -328,16 +294,11 @@ void init_pwm(int freq, int range, int val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start PWM operation
|
// Start PWM operation
|
||||||
void start_pwm(void)
|
void start_pwm(void) { *REG32(pwm_regs, PWM_CTL) = PWM_CTL_USEF1 | PWM_ENAB; }
|
||||||
{
|
|
||||||
*REG32(pwm_regs, PWM_CTL) = PWM_CTL_USEF1 | PWM_ENAB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop PWM operation
|
// Stop PWM operation
|
||||||
void stop_pwm(void)
|
void stop_pwm(void) {
|
||||||
{
|
if (pwm_regs.virt) {
|
||||||
if (pwm_regs.virt)
|
|
||||||
{
|
|
||||||
*REG32(pwm_regs, PWM_CTL) = 0;
|
*REG32(pwm_regs, PWM_CTL) = 0;
|
||||||
usleep(100);
|
usleep(100);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// Location of peripheral registers in physical memory
|
// Location of peripheral registers in physical memory
|
||||||
#define PHYS_REG_BASE PI_23_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
|
||||||
|
@ -30,19 +30,20 @@
|
|||||||
// 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 "rpi_dma_utils.h"
|
|
||||||
#include "rpi_smi_defs.h"
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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)
|
#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)
|
||||||
@ -95,8 +96,6 @@ volatile SMI_DCD_REG *smi_dcd;
|
|||||||
#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, 0xff4040, 0x40ff40, 0x4040ff, 0x404040,
|
int on_rgbs[16] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040,
|
||||||
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 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);
|
||||||
@ -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,
|
printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting", chan_ledcount,
|
||||||
chan_ledcount == 1 ? "" : "s", LED_NCHANS);
|
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));
|
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
|
||||||
start_smi(&vc_mem);
|
start_smi(&vc_mem);
|
||||||
|
artnet_init();
|
||||||
|
|
||||||
// loops
|
// loops
|
||||||
if (testmode) {
|
if (testmode) {
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -208,22 +187,15 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (1) {
|
while (1) {
|
||||||
nBytes = recvfrom(udpSocket, buffer, 1024, 0, (struct sockaddr *)&serverStorage, &addr_size);
|
artDmx_t *artDmx;
|
||||||
if (nBytes <= 530 && nBytes > 0) {
|
if (artnet_read(&artDmx) == OpDmx) {
|
||||||
if (memcmp(buffer, artnetId, sizeof(artnetId)) == 0) {
|
uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
|
||||||
uint16_t opcode = buffer[8] | (buffer[9] << 8);
|
unsigned int ledCountInFrame = dmxLength / 3;
|
||||||
if (opcode == 0x5000) {
|
uint16_t maxBound = ledCountInFrame < chan_ledcount ? ledCountInFrame : chan_ledcount;
|
||||||
// uint8_t sequence = buffer[12];
|
unsigned int universe = artDmx->subUni & (LED_NCHANS - 1);
|
||||||
// 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 i = 0; i < maxBound; ++i) {
|
||||||
for (size_t j = 0; j < LED_NCHANS; ++j) {
|
uint8_t *rgb = artDmx->data + (i * 3);
|
||||||
char *rgb = dmxData + (i * 3);
|
rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
||||||
rgb_data[i][j] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
|
||||||
}
|
|
||||||
rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
|
rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,10 +212,7 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
terminate(0);
|
terminate(0);
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert RGB text string into integer data, for given channel
|
// Convert RGB text string into integer data, for given channel
|
||||||
@ -317,8 +286,6 @@ void terminate(int sig) {
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
printf("Closing\n");
|
printf("Closing\n");
|
||||||
close(udpSocket);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < chan_ledcount; ++i) {
|
for (size_t i = 0; i < chan_ledcount; ++i) {
|
||||||
rgb_txdata(off_rgbs, &tx_buffer[LED_TX_OSET(i)]);
|
rgb_txdata(off_rgbs, &tx_buffer[LED_TX_OSET(i)]);
|
||||||
}
|
}
|
||||||
|
@ -33,65 +33,80 @@
|
|||||||
#define DMA_SMI_DREQ 4
|
#define DMA_SMI_DREQ 4
|
||||||
|
|
||||||
// Union of 32-bit value with register bitfields
|
// 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
|
// Control and status register
|
||||||
#define SMI_CS_FIELDS \
|
#define SMI_CS_FIELDS \
|
||||||
enable:1, done:1, active:1, start:1, clear:1, write:1, _x1:2,\
|
enable: \
|
||||||
teen:1, intd:1, intt:1, intr:1, pvmode:1, seterr:1, pxldat:1, edreq:1,\
|
1, done : 1, active : 1, start : 1, clear : 1, write : 1, _x1 : 2, teen : 1, intd : 1, intt : 1, \
|
||||||
_x2:8, _x3:1, aferr:1, txw:1, rxr:1, txd:1, rxd:1, txe:1, rxf: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);
|
REG_DEF(SMI_CS_REG, SMI_CS_FIELDS);
|
||||||
|
|
||||||
// Data length register
|
// Data length register
|
||||||
#define SMI_L_FIELDS \
|
#define SMI_L_FIELDS \
|
||||||
len:32
|
len: \
|
||||||
|
32
|
||||||
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
|
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
|
||||||
|
|
||||||
// Address & device number
|
// Address & device number
|
||||||
#define SMI_A_FIELDS \
|
#define SMI_A_FIELDS \
|
||||||
addr:6, _x1:2, dev:2
|
addr: \
|
||||||
|
6, _x1 : 2, dev : 2
|
||||||
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
|
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
|
||||||
|
|
||||||
// Data FIFO
|
// Data FIFO
|
||||||
#define SMI_D_FIELDS \
|
#define SMI_D_FIELDS \
|
||||||
data:32
|
data: \
|
||||||
|
32
|
||||||
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
|
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
|
||||||
|
|
||||||
// DMA control register
|
// DMA control register
|
||||||
#define SMI_DMC_FIELDS \
|
#define SMI_DMC_FIELDS \
|
||||||
reqw:6, reqr:6, panicw:6, panicr:6, dmap:1, _x1:3, dmaen:1
|
reqw: \
|
||||||
|
6, reqr : 6, panicw : 6, panicr : 6, dmap : 1, _x1 : 3, dmaen : 1
|
||||||
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
|
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
|
||||||
|
|
||||||
// Device settings: read (1 of 4)
|
// Device settings: read (1 of 4)
|
||||||
#define SMI_DSR_FIELDS \
|
#define SMI_DSR_FIELDS \
|
||||||
rstrobe:7, rdreq:1, rpace:7, rpaceall:1, \
|
rstrobe: \
|
||||||
rhold:6, fsetup:1, mode68:1, rsetup:6, rwidth:2
|
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);
|
REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS);
|
||||||
|
|
||||||
// Device settings: write (1 of 4)
|
// Device settings: write (1 of 4)
|
||||||
#define SMI_DSW_FIELDS \
|
#define SMI_DSW_FIELDS \
|
||||||
wstrobe:7, wdreq:1, wpace:7, wpaceall:1, \
|
wstrobe: \
|
||||||
whold:6, wswap:1, wformat:1, wsetup:6, wwidth:2
|
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);
|
REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS);
|
||||||
|
|
||||||
// Direct control register
|
// Direct control register
|
||||||
#define SMI_DCS_FIELDS \
|
#define SMI_DCS_FIELDS \
|
||||||
enable:1, start:1, done:1, write:1
|
enable: \
|
||||||
|
1, start : 1, done : 1, write : 1
|
||||||
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
|
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
|
||||||
|
|
||||||
// Direct control address & device number
|
// Direct control address & device number
|
||||||
#define SMI_DCA_FIELDS \
|
#define SMI_DCA_FIELDS \
|
||||||
addr:6, _x1:2, dev:2
|
addr: \
|
||||||
|
6, _x1 : 2, dev : 2
|
||||||
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
|
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
|
||||||
|
|
||||||
// Direct control data
|
// Direct control data
|
||||||
#define SMI_DCD_FIELDS \
|
#define SMI_DCD_FIELDS \
|
||||||
data:32
|
data: \
|
||||||
|
32
|
||||||
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
|
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
|
||||||
|
|
||||||
// Debug register
|
// Debug register
|
||||||
#define SMI_FLVL_FIELDS \
|
#define SMI_FLVL_FIELDS \
|
||||||
fcnt:6, _x1:2, flvl:6
|
fcnt: \
|
||||||
|
6, _x1 : 2, flvl : 6
|
||||||
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
|
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
|
||||||
|
|
||||||
#define CLK_SMI_CTL 0xb0
|
#define CLK_SMI_CTL 0xb0
|
||||||
|
Loading…
Reference in New Issue
Block a user