beta with artnet 8 channels (8 universes)

This commit is contained in:
Tropicananass 2021-05-01 16:48:48 +01:00
parent e8b99665be
commit eaf976d5a7
13 changed files with 806 additions and 424 deletions

View File

@ -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}"
} }
] ]
} }

View File

@ -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}"
}, },

View File

@ -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
View 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/

View 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));
}

View 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__

View 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 Nodes 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__

View 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__

View 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__

View File

@ -14,333 +14,294 @@
// 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"
// If non-zero, print debug information // If non-zero, print debug information
#define DEBUG 0 #define DEBUG 0
// If non-zero, enable PWM hardware output // 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", char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD",
"TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""}; "TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""};
char *gpio_mode_strs[] = {GPIO_MODE_STRS}; char *gpio_mode_strs[] = {GPIO_MODE_STRS};
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space // Virtual memory pointers to acceess GPIO, DMA and PWM from user space
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); mp->virt = map_segment(phys, mp->size);
mp->virt = map_segment(phys, mp->size); return (mp->virt);
return(mp->virt);
} }
// 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
? mp->virt : 0; : 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) {
{ unmap_segment(mp->virt, mp->size);
if (mp->fd) unlock_vc_mem(mp->fd, mp->h);
{ free_vc_mem(mp->fd, mp->h);
unmap_segment(mp->virt, mp->size); close_mbox(mp->fd);
unlock_vc_mem(mp->fd, mp->h); } else
free_vc_mem(mp->fd, mp->h); unmap_segment(mp->virt, mp->size);
close_mbox(mp->fd); }
}
else
unmap_segment(mp->virt, mp->size);
}
} }
// ----- 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;
usleep(2); usleep(2);
*reg = pin << (pin % 32); *reg = pin << (pin % 32);
usleep(2); usleep(2);
*REG32(gpio_regs, GPIO_GPPUD) = 0; *REG32(gpio_regs, GPIO_GPPUD) = 0;
*reg = 0; *reg = 0;
} }
// 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++)
printf("%u:%-4s ", i, gpio_mode_strs[(mode>>(i*3)) & 7]); printf("%u:%-4s ", i, gpio_mode_strs[(mode >> (i * 3)) & 7]);
printf("\n"); printf("\n");
} }
// ----- 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)
fail("Error: can't open VC mailbox\n"); fail("Error: can't open VC mailbox\n");
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)
msgp->uints[i++] = 0; msgp->uints[i++] = 0;
msgp->len = (msgp->blen + 6) * 4; msgp->len = (msgp->blen + 6) * 4;
msgp->req = 0; msgp->req = 0;
if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0) if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0)
printf("VC IOCTL failed\n"); printf("VC IOCTL failed\n");
else if ((msgp->req&0x80000000) == 0) else if ((msgp->req & 0x80000000) == 0)
printf("VC IOCTL error\n"); printf("VC IOCTL error\n");
else if (msgp->req == 0x80000001) else if (msgp->req == 0x80000001)
printf("VC IOCTL partial error\n"); printf("VC IOCTL partial error\n");
else else
ret = msgp->uints[0]; ret = msgp->uints[0];
#if DEBUG #if DEBUG
disp_vc_msg(msgp); disp_vc_msg(msgp);
#endif #endif
return(ret); return (ret);
} }
// 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); disp_vc_msg(&msg1);
disp_vc_msg(&msg1); msg_mbox(fd, &msg2);
msg_mbox(fd, &msg2); disp_vc_msg(&msg2);
disp_vc_msg(&msg2); return (0);
return(0);
} }
// 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");
} }
// ----- 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;
size = PAGE_ROUNDUP(size); size = PAGE_ROUNDUP(size);
if ((fd = open ("/dev/mem", O_RDWR|O_SYNC|O_CLOEXEC)) < 0) if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0)
fail("Error: can't open /dev/mem, run using sudo\n"); 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); mem = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr);
close(fd); close(fd);
#if DEBUG #if DEBUG
printf("Map %p -> %p\n", (void *)addr, mem); printf("Map %p -> %p\n", (void *)addr, mem);
#endif #endif
if (mem == MAP_FAILED) if (mem == MAP_FAILED)
fail("Error: can't map memory\n"); fail("Error: can't map memory\n");
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));
} }
// ----- 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 *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 | csval; // Start DMA
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1|csval; // Start DMA
} }
// 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"); }
}
} }
// ----- 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");
{ *REG32(pwm_regs, PWM_STA) = 0x100;
printf("PWM bus error\n"); }
*REG32(pwm_regs, PWM_STA) = 0x100;
}
#if USE_VC_CLOCK_SET #if USE_VC_CLOCK_SET
set_vc_clock(mbox_fd, PWM_CLOCK_ID, freq); set_vc_clock(mbox_fd, PWM_CLOCK_ID, freq);
#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_CTL) = CLK_PASSWD | 6 | (1 << 4); *REG32(clk_regs, CLK_PWM_DIV) = CLK_PASSWD | (divi << 12);
while ((*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) == 0) ; *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4);
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;
*REG32(pwm_regs, PWM_FIF1) = val; *REG32(pwm_regs, PWM_FIF1) = val;
#if PWM_OUT #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 #endif
} }
// 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;
{ usleep(100);
*REG32(pwm_regs, PWM_CTL) = 0; }
usleep(100);
}
} }
// EOF // EOF

View File

@ -15,158 +15,160 @@
// limitations under the License. // limitations under the License.
// //
// Location of peripheral registers in physical memory #include <stdint.h>
#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 // 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 //#define CLOCK_HZ 400000000 // Pi Zero
// Location of peripheral registers in bus memory // Location of peripheral registers in bus memory
#define BUS_REG_BASE 0x7E000000 #define BUS_REG_BASE 0x7E000000
// If non-zero, print debug information // If non-zero, print debug information
#define DEBUG 0 #define DEBUG 0
// If non-zero, set PWM clock using VideoCore mailbox // If non-zero, set PWM clock using VideoCore mailbox
#define USE_VC_CLOCK_SET 0 #define USE_VC_CLOCK_SET 0
// Size of memory page // Size of memory page
#define PAGE_SIZE 0x1000 #define PAGE_SIZE 0x1000
// Round up to nearest page // 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 // Structure for mapped peripheral or memory
typedef struct { typedef struct {
int fd, // File descriptor int fd, // File descriptor
h, // Memory handle h, // Memory handle
size; // Memory size size; // Memory size
void *bus, // Bus address void *bus, // Bus address
*virt, // Virtual address *virt, // Virtual address
*phys; // Physical address *phys; // Physical address
} MEM_MAP; } MEM_MAP;
// Get virtual 8 and 32-bit pointers to register // Get virtual 8 and 32-bit pointers to register
#define REG8(m, x) ((volatile uint8_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))) #define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
// Get bus address of register // 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 // 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) // 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 // GPIO register definitions
#define GPIO_BASE (PHYS_REG_BASE + 0x200000) #define GPIO_BASE (PHYS_REG_BASE + 0x200000)
#define GPIO_MODE0 0x00 #define GPIO_MODE0 0x00
#define GPIO_SET0 0x1c #define GPIO_SET0 0x1c
#define GPIO_CLR0 0x28 #define GPIO_CLR0 0x28
#define GPIO_LEV0 0x34 #define GPIO_LEV0 0x34
#define GPIO_GPPUD 0x94 #define GPIO_GPPUD 0x94
#define GPIO_GPPUDCLK0 0x98 #define GPIO_GPPUDCLK0 0x98
// GPIO I/O definitions // GPIO I/O definitions
#define GPIO_IN 0 #define GPIO_IN 0
#define GPIO_OUT 1 #define GPIO_OUT 1
#define GPIO_ALT0 4 #define GPIO_ALT0 4
#define GPIO_ALT1 5 #define GPIO_ALT1 5
#define GPIO_ALT2 6 #define GPIO_ALT2 6
#define GPIO_ALT3 7 #define GPIO_ALT3 7
#define GPIO_ALT4 3 #define GPIO_ALT4 3
#define GPIO_ALT5 2 #define GPIO_ALT5 2
#define GPIO_MODE_STRS "IN","OUT","ALT5","ALT4","ALT0","ALT1","ALT2","ALT3" #define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
#define GPIO_NOPULL 0 #define GPIO_NOPULL 0
#define GPIO_PULLDN 1 #define GPIO_PULLDN 1
#define GPIO_PULLUP 2 #define GPIO_PULLUP 2
// Videocore mailbox memory allocation flags, see: // Videocore mailbox memory allocation flags, see:
// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface // https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
typedef enum { typedef enum {
MEM_FLAG_DISCARDABLE = 1<<0, // can be resized to 0 at any time. Use for cached data 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_NORMAL = 0 << 2, // normal allocating alias. Don't use from ARM
MEM_FLAG_DIRECT = 1<<2, // 0xC alias uncached MEM_FLAG_DIRECT = 1 << 2, // 0xC alias uncached
MEM_FLAG_COHERENT = 2<<2, // 0x8 alias. Non-allocating in L2 but coherent 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_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_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_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_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2
} VC_ALLOC_FLAGS; } VC_ALLOC_FLAGS;
// VC flags for unchached DMA memory // 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 // Mailbox command/response structure
typedef struct { typedef struct {
uint32_t len, // Overall length (bytes) uint32_t len, // Overall length (bytes)
req, // Zero for request, 1<<31 for response req, // Zero for request, 1<<31 for response
tag, // Command number tag, // Command number
blen, // Buffer length (bytes) blen, // Buffer length (bytes)
dlen; // Data length (bytes) dlen; // Data length (bytes)
uint32_t uints[32-5]; // Data (108 bytes maximum) uint32_t uints[32 - 5]; // Data (108 bytes maximum)
} VC_MSG __attribute__ ((aligned (16))); } VC_MSG __attribute__((aligned(16)));
// DMA channels and data requests // DMA channels and data requests
#define DMA_CHAN_A 10 #define DMA_CHAN_A 10
#define DMA_CHAN_B 11 #define DMA_CHAN_B 11
#define DMA_PWM_DREQ 5 #define DMA_PWM_DREQ 5
#define DMA_SPI_TX_DREQ 6 #define DMA_SPI_TX_DREQ 6
#define DMA_SPI_RX_DREQ 7 #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 // DMA register addresses offset by 0x100 * chan_num
#define DMA_CS 0x00 #define DMA_CS 0x00
#define DMA_CONBLK_AD 0x04 #define DMA_CONBLK_AD 0x04
#define DMA_TI 0x08 #define DMA_TI 0x08
#define DMA_SRCE_AD 0x0c #define DMA_SRCE_AD 0x0c
#define DMA_DEST_AD 0x10 #define DMA_DEST_AD 0x10
#define DMA_TXFR_LEN 0x14 #define DMA_TXFR_LEN 0x14
#define DMA_STRIDE 0x18 #define DMA_STRIDE 0x18
#define DMA_NEXTCONBK 0x1c #define DMA_NEXTCONBK 0x1c
#define DMA_DEBUG 0x20 #define DMA_DEBUG 0x20
#define DMA_REG(ch, r) ((r)==DMA_ENABLE ? DMA_ENABLE : (ch)*0x100+(r)) #define DMA_REG(ch, r) ((r) == DMA_ENABLE ? DMA_ENABLE : (ch)*0x100 + (r))
#define DMA_ENABLE 0xff0 #define DMA_ENABLE 0xff0
// DMA register values // DMA register values
#define DMA_WAIT_RESP (1 << 3) #define DMA_WAIT_RESP (1 << 3)
#define DMA_CB_DEST_INC (1 << 4) #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_CB_SRCE_INC (1 << 8)
#define DMA_SRCE_DREQ (1 << 10) #define DMA_SRCE_DREQ (1 << 10)
#define DMA_PRIORITY(n) ((n) << 16) #define DMA_PRIORITY(n) ((n) << 16)
// DMA control block (must be 32-byte aligned) // DMA control block (must be 32-byte aligned)
typedef struct { typedef struct {
uint32_t ti, // Transfer info uint32_t ti, // Transfer info
srce_ad, // Source address srce_ad, // Source address
dest_ad, // Destination address dest_ad, // Destination address
tfr_len, // Transfer length tfr_len, // Transfer length
stride, // Transfer stride stride, // Transfer stride
next_cb, // Next control block next_cb, // Next control block
debug, // Debug register, zero in control block debug, // Debug register, zero in control block
unused; unused;
} DMA_CB __attribute__ ((aligned(32))); } DMA_CB __attribute__((aligned(32)));
// PWM controller registers // PWM controller registers
#define PWM_BASE (PHYS_REG_BASE + 0x20C000) #define PWM_BASE (PHYS_REG_BASE + 0x20C000)
#define PWM_CTL 0x00 // Control #define PWM_CTL 0x00 // Control
#define PWM_STA 0x04 // Status #define PWM_STA 0x04 // Status
#define PWM_DMAC 0x08 // DMA control #define PWM_DMAC 0x08 // DMA control
#define PWM_RNG1 0x10 // Channel 1 range #define PWM_RNG1 0x10 // Channel 1 range
#define PWM_DAT1 0x14 // Channel 1 data #define PWM_DAT1 0x14 // Channel 1 data
#define PWM_FIF1 0x18 // Channel 1 fifo #define PWM_FIF1 0x18 // Channel 1 fifo
#define PWM_RNG2 0x20 // Channel 2 range #define PWM_RNG2 0x20 // Channel 2 range
#define PWM_DAT2 0x24 // Channel 2 data #define PWM_DAT2 0x24 // Channel 2 data
// PWM register values // PWM register values
#define PWM_CTL_RPTL1 (1<<2) // Chan 1: repeat last data when FIFO empty #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_CTL_USEF1 (1 << 5) // Chan 1: use FIFO
#define PWM_DMAC_ENAB (1<<31) // Start PWM DMA #define PWM_DMAC_ENAB (1 << 31) // Start PWM DMA
#define PWM_ENAB 1 // Enable PWM #define PWM_ENAB 1 // Enable PWM
#define PWM_PIN 12 // GPIO pin for PWM output, 12 or 18 #define PWM_PIN 12 // GPIO pin for PWM output, 12 or 18
// Clock registers and values // Clock registers and values
#define CLK_BASE (PHYS_REG_BASE + 0x101000) #define CLK_BASE (PHYS_REG_BASE + 0x101000)
#define CLK_PWM_CTL 0xa0 #define CLK_PWM_CTL 0xa0
#define CLK_PWM_DIV 0xa4 #define CLK_PWM_DIV 0xa4
#define CLK_PASSWD 0x5a000000 #define CLK_PASSWD 0x5a000000
#define PWM_CLOCK_ID 0xa #define PWM_CLOCK_ID 0xa
void fail(char *s); void fail(char *s);
void *map_periph(MEM_MAP *mp, void *phys, int size); void *map_periph(MEM_MAP *mp, void *phys, int size);

View File

@ -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,42 +187,32 @@ 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); for (size_t i = 0; i < maxBound; ++i) {
uint16_t dmxDataLength = buffer[17] | (buffer[16] << 8); uint8_t *rgb = artDmx->data + (i * 3);
char *dmxData = buffer + 18; rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
uint16_t maxBound = rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
(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)]);
}
#if LED_NCHANS <= 8 #if LED_NCHANS <= 8
swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount)); swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
#endif #endif
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount)); memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
// enable_dma(DMA_CHAN); // enable_dma(DMA_CHAN);
start_smi(&vc_mem); start_smi(&vc_mem);
// usleep(10); // usleep(10);
// while (dma_active(DMA_CHAN)) // while (dma_active(DMA_CHAN))
// usleep(10); // usleep(10);
}
}
} }
} }
} }
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)]);
} }

View File

@ -3,98 +3,113 @@
// v0.01 JPB 12/7/20 Adapted from rpi_smi_test v0.19 // v0.01 JPB 12/7/20 Adapted from rpi_smi_test v0.19
// Register definitions // Register definitions
#define SMI_BASE (PHYS_REG_BASE + 0x600000) #define SMI_BASE (PHYS_REG_BASE + 0x600000)
#define SMI_CS 0x00 // Control & status #define SMI_CS 0x00 // Control & status
#define SMI_L 0x04 // Transfer length #define SMI_L 0x04 // Transfer length
#define SMI_A 0x08 // Address #define SMI_A 0x08 // Address
#define SMI_D 0x0c // Data #define SMI_D 0x0c // Data
#define SMI_DSR0 0x10 // Read settings device 0 #define SMI_DSR0 0x10 // Read settings device 0
#define SMI_DSW0 0x14 // Write settings device 0 #define SMI_DSW0 0x14 // Write settings device 0
#define SMI_DSR1 0x18 // Read settings device 1 #define SMI_DSR1 0x18 // Read settings device 1
#define SMI_DSW1 0x1c // Write settings device 1 #define SMI_DSW1 0x1c // Write settings device 1
#define SMI_DSR2 0x20 // Read settings device 2 #define SMI_DSR2 0x20 // Read settings device 2
#define SMI_DSW2 0x24 // Write settings device 2 #define SMI_DSW2 0x24 // Write settings device 2
#define SMI_DSR3 0x28 // Read settings device 3 #define SMI_DSR3 0x28 // Read settings device 3
#define SMI_DSW3 0x2c // Write settings device 3 #define SMI_DSW3 0x2c // Write settings device 3
#define SMI_DMC 0x30 // DMA control #define SMI_DMC 0x30 // DMA control
#define SMI_DCS 0x34 // Direct control/status #define SMI_DCS 0x34 // Direct control/status
#define SMI_DCA 0x38 // Direct address #define SMI_DCA 0x38 // Direct address
#define SMI_DCD 0x3c // Direct data #define SMI_DCD 0x3c // Direct data
#define SMI_FD 0x40 // FIFO debug #define SMI_FD 0x40 // FIFO debug
#define SMI_REGLEN (SMI_FD * 4) #define SMI_REGLEN (SMI_FD * 4)
// Data widths // Data widths
#define SMI_8_BITS 0 #define SMI_8_BITS 0
#define SMI_16_BITS 1 #define SMI_16_BITS 1
#define SMI_18_BITS 2 #define SMI_18_BITS 2
#define SMI_9_BITS 3 #define SMI_9_BITS 3
// DMA request // DMA request
#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
#define CLK_SMI_DIV 0xb4 #define CLK_SMI_DIV 0xb4
// EOF // EOF