init main app code
This commit is contained in:
		
							
								
								
									
										315
									
								
								RpiLedBars/src/artnet/ArtnetnodeWifi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								RpiLedBars/src/artnet/ArtnetnodeWifi.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,315 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Copyright (c) Charles Yarnold charlesyarnold@gmail.com 2015
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2016-2020 Stephan Ruloff
 | 
			
		||||
https://github.com/rstephan/ArtnetnodeWifi
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, under version 2 of the License.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <ArtnetnodeWifi.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char ArtnetnodeWifi::artnetId[] = ARTNET_ID;
 | 
			
		||||
 | 
			
		||||
ArtnetnodeWifi::ArtnetnodeWifi()
 | 
			
		||||
{
 | 
			
		||||
  // Initalise DMXOutput array
 | 
			
		||||
  for (int i = 0; i < DMX_MAX_OUTPUTS; i++) {
 | 
			
		||||
    DMXOutputs[i][0] = 0xFF;
 | 
			
		||||
    DMXOutputs[i][1] = 0xFF;
 | 
			
		||||
    DMXOutputs[i][2] = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Start DMX tick clock
 | 
			
		||||
  msSinceDMXSend = 0;
 | 
			
		||||
 | 
			
		||||
  // Init DMX buffers
 | 
			
		||||
  for (int i = 0; i < DMX_MAX_OUTPUTS; i++) {
 | 
			
		||||
    memset(DMXBuffer[i], 0, sizeof(DMXBuffer[i]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sequence = 1;
 | 
			
		||||
  physical = 0;
 | 
			
		||||
  outgoingUniverse = 0;
 | 
			
		||||
  dmxDataLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@retval 0 Ok
 | 
			
		||||
*/
 | 
			
		||||
uint8_t ArtnetnodeWifi::begin(String hostname)
 | 
			
		||||
{
 | 
			
		||||
  byte mac[6];
 | 
			
		||||
 | 
			
		||||
  Udp.begin(ARTNET_PORT);
 | 
			
		||||
  localIP = WiFi.localIP();
 | 
			
		||||
  localMask = WiFi.subnetMask();
 | 
			
		||||
  localBroadcast = IPAddress((uint32_t)localIP | ~(uint32_t)localMask);
 | 
			
		||||
 | 
			
		||||
  WiFi.macAddress(mac);
 | 
			
		||||
  PollReplyPacket.setMac(mac);
 | 
			
		||||
  PollReplyPacket.setIP(localIP);
 | 
			
		||||
  PollReplyPacket.canDHCP(true);
 | 
			
		||||
  PollReplyPacket.isDHCP(true);
 | 
			
		||||
 | 
			
		||||
  host = hostname;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::setShortName(const char name[])
 | 
			
		||||
{
 | 
			
		||||
  PollReplyPacket.setShortName(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::setLongName(const char name[])
 | 
			
		||||
{
 | 
			
		||||
  PollReplyPacket.setLongName(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::setName(const char name[])
 | 
			
		||||
{
 | 
			
		||||
  PollReplyPacket.setShortName(name);
 | 
			
		||||
  PollReplyPacket.setLongName(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::setNumPorts(uint8_t num)
 | 
			
		||||
{
 | 
			
		||||
  PollReplyPacket.setNumPorts(num);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::setStartingUniverse(uint16_t startingUniverse)
 | 
			
		||||
{
 | 
			
		||||
  PollReplyPacket.setStartingUniverse(startingUniverse);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t ArtnetnodeWifi::read()
 | 
			
		||||
{
 | 
			
		||||
  uint8_t startcode;
 | 
			
		||||
 | 
			
		||||
  packetSize = Udp.parsePacket();
 | 
			
		||||
 | 
			
		||||
  if (packetSize <= ARTNET_MAX_BUFFER && packetSize > 0) {
 | 
			
		||||
    Udp.read(artnetPacket, ARTNET_MAX_BUFFER);
 | 
			
		||||
 | 
			
		||||
    // Check that packetID is "Art-Net" else ignore
 | 
			
		||||
    if (memcmp(artnetPacket, artnetId, sizeof(artnetId)) != 0) {
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opcode = artnetPacket[8] | artnetPacket[9] << 8;
 | 
			
		||||
 | 
			
		||||
    switch (opcode) {
 | 
			
		||||
    case OpDmx:
 | 
			
		||||
      return handleDMX(0);
 | 
			
		||||
    case OpPoll:
 | 
			
		||||
      return handlePollRequest();
 | 
			
		||||
    case OpNzs:
 | 
			
		||||
      startcode = artnetPacket[13];
 | 
			
		||||
      if (startcode != 0 && startcode != DMX_RDM_STARTCODE) {
 | 
			
		||||
        return handleDMX(startcode);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return opcode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t ArtnetnodeWifi::makePacket(void)
 | 
			
		||||
{
 | 
			
		||||
  uint16_t len;
 | 
			
		||||
  uint16_t version;
 | 
			
		||||
 | 
			
		||||
  memcpy(artnetPacket, artnetId, sizeof(artnetId));
 | 
			
		||||
  opcode = OpDmx;
 | 
			
		||||
  artnetPacket[8] = opcode;
 | 
			
		||||
  artnetPacket[9] = opcode >> 8;
 | 
			
		||||
  version = 14;
 | 
			
		||||
  artnetPacket[10] = version >> 8;
 | 
			
		||||
  artnetPacket[11] = version;
 | 
			
		||||
  artnetPacket[12] = sequence;
 | 
			
		||||
  sequence++;
 | 
			
		||||
  if (!sequence) {
 | 
			
		||||
    sequence = 1;
 | 
			
		||||
  }
 | 
			
		||||
  artnetPacket[13] = physical;
 | 
			
		||||
  artnetPacket[14] = outgoingUniverse;
 | 
			
		||||
  artnetPacket[15] = outgoingUniverse >> 8;
 | 
			
		||||
  len = dmxDataLength + (dmxDataLength % 2); // make an even number
 | 
			
		||||
  artnetPacket[16] = len >> 8;
 | 
			
		||||
  artnetPacket[17] = len;
 | 
			
		||||
 | 
			
		||||
  return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ArtnetnodeWifi::write(void)
 | 
			
		||||
{
 | 
			
		||||
  uint16_t len;
 | 
			
		||||
 | 
			
		||||
  len = makePacket();
 | 
			
		||||
  Udp.beginPacket(host.c_str(), ARTNET_PORT);
 | 
			
		||||
  Udp.write(artnetPacket, ARTNET_DMX_START_LOC + len);
 | 
			
		||||
 | 
			
		||||
  return Udp.endPacket();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ArtnetnodeWifi::write(IPAddress ip)
 | 
			
		||||
{
 | 
			
		||||
  uint16_t len;
 | 
			
		||||
 | 
			
		||||
  len = makePacket();
 | 
			
		||||
  Udp.beginPacket(ip, ARTNET_PORT);
 | 
			
		||||
  Udp.write(artnetPacket, ARTNET_DMX_START_LOC + len);
 | 
			
		||||
 | 
			
		||||
  return Udp.endPacket();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::setByte(uint16_t pos, uint8_t value)
 | 
			
		||||
{
 | 
			
		||||
  if (pos > 512) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  artnetPacket[ARTNET_DMX_START_LOC + pos] = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool ArtnetnodeWifi::isBroadcast()
 | 
			
		||||
{
 | 
			
		||||
  if (Udp.remoteIP() == localBroadcast){
 | 
			
		||||
    return true;
 | 
			
		||||
  } else {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t ArtnetnodeWifi::handleDMX(uint8_t nzs)
 | 
			
		||||
{
 | 
			
		||||
  if (isBroadcast()) {
 | 
			
		||||
    return 0;
 | 
			
		||||
  } else {
 | 
			
		||||
    // Get universe
 | 
			
		||||
    uint16_t universe = artnetPacket[14] | artnetPacket[15] << 8;
 | 
			
		||||
 | 
			
		||||
    // Get DMX frame length
 | 
			
		||||
    uint16_t dmxDataLength = artnetPacket[17] | artnetPacket[16] << 8;
 | 
			
		||||
    
 | 
			
		||||
    // Sequence
 | 
			
		||||
    uint8_t sequence = artnetPacket[12];
 | 
			
		||||
 | 
			
		||||
    if (artDmxCallback) {
 | 
			
		||||
      (*artDmxCallback)(universe, dmxDataLength, sequence, artnetPacket + ARTNET_DMX_START_LOC);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for(int a = 0; a < DMX_MAX_OUTPUTS; a++){
 | 
			
		||||
      if(DMXOutputs[a][1] == universe){
 | 
			
		||||
        for (int i = 0 ; i < DMX_MAX_BUFFER ; i++){
 | 
			
		||||
          if(i < dmxDataLength){
 | 
			
		||||
            DMXBuffer[a][i] = artnetPacket[i+ARTNET_DMX_START_LOC];
 | 
			
		||||
          }
 | 
			
		||||
          else{
 | 
			
		||||
            DMXBuffer[a][i] = 0;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (nzs) {
 | 
			
		||||
      return OpNzs;
 | 
			
		||||
    } else {
 | 
			
		||||
      return OpDmx;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t ArtnetnodeWifi::handlePollRequest()
 | 
			
		||||
{
 | 
			
		||||
  if (1 || isBroadcast()) {
 | 
			
		||||
    Udp.beginPacket(localBroadcast, ARTNET_PORT);
 | 
			
		||||
    Udp.write(PollReplyPacket.printPacket(), sizeof(PollReplyPacket.packet));
 | 
			
		||||
    Udp.endPacket();
 | 
			
		||||
 | 
			
		||||
    return OpPoll;
 | 
			
		||||
  } else{
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::enableDMX()
 | 
			
		||||
{
 | 
			
		||||
  DMXOutputStatus = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::disableDMX()
 | 
			
		||||
{
 | 
			
		||||
  DMXOutputStatus = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::enableDMXOutput(uint8_t outputID)
 | 
			
		||||
{
 | 
			
		||||
  DMXOutputs[outputID][2] = 1;
 | 
			
		||||
 | 
			
		||||
  int numEnabled = 0;
 | 
			
		||||
  for(int i = 0; i < DMX_MAX_OUTPUTS; i++){
 | 
			
		||||
    if(DMXOutputs[i][2]==1){
 | 
			
		||||
      if(numEnabled<4){
 | 
			
		||||
        numEnabled++;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  PollReplyPacket.setNumPorts(numEnabled);
 | 
			
		||||
  PollReplyPacket.setOutputEnabled(outputID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::disableDMXOutput(uint8_t outputID)
 | 
			
		||||
{
 | 
			
		||||
  DMXOutputs[outputID][2] = 0;
 | 
			
		||||
 | 
			
		||||
  int numEnabled = 0;
 | 
			
		||||
  for(int i = 0; i < DMX_MAX_OUTPUTS; i++){
 | 
			
		||||
    if(DMXOutputs[i][2]==1){
 | 
			
		||||
      if(numEnabled<4){
 | 
			
		||||
        numEnabled++;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  PollReplyPacket.setNumPorts(numEnabled);
 | 
			
		||||
  PollReplyPacket.setOutputDisabled(outputID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t ArtnetnodeWifi::setDMXOutput(uint8_t outputID, uint8_t uartNum, uint16_t attachedUniverse)
 | 
			
		||||
{
 | 
			
		||||
  // Validate input
 | 
			
		||||
  if(outputID < DMX_MAX_OUTPUTS && uartNum != 0xFF && attachedUniverse != 0xFF){
 | 
			
		||||
    DMXOutputs[outputID][0] = uartNum;
 | 
			
		||||
    DMXOutputs[outputID][1] = attachedUniverse;
 | 
			
		||||
    DMXOutputs[outputID][2] = 0;
 | 
			
		||||
    PollReplyPacket.setSwOut(outputID, attachedUniverse);
 | 
			
		||||
    return 1;
 | 
			
		||||
  } else {
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArtnetnodeWifi::tickDMX(uint32_t time)
 | 
			
		||||
{
 | 
			
		||||
  msSinceDMXSend += time;
 | 
			
		||||
  if(msSinceDMXSend > DMX_MS_BETWEEN_TICKS){
 | 
			
		||||
    sendDMX();
 | 
			
		||||
    msSinceDMXSend = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										151
									
								
								RpiLedBars/src/artnet/ArtnetnodeWifi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								RpiLedBars/src/artnet/ArtnetnodeWifi.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
#ifndef ARTNETNODEWIFI_H
 | 
			
		||||
#define ARTNETNODEWIFI_H
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Copyright (c) Charles Yarnold charlesyarnold@gmail.com 2015
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2016 Stephan Ruloff
 | 
			
		||||
https://github.com/rstephan
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, under version 2 of the License.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP32)
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#elif defined(ARDUINO_ARCH_ESP8266)
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#elif defined(ARDUINO_ARCH_SAMD)
 | 
			
		||||
#if defined(ARDUINO_SAMD_MKR1000)
 | 
			
		||||
#include <WiFi101.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <WiFiNINA.h>
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#error "Architecture not supported!"
 | 
			
		||||
#endif
 | 
			
		||||
#include <WiFiUdp.h>
 | 
			
		||||
#include "OpCodes.h"
 | 
			
		||||
#include "NodeReportCodes.h"
 | 
			
		||||
#include "StyleCodes.h"
 | 
			
		||||
#include "PriorityCodes.h"
 | 
			
		||||
#include "ProtocolSettings.h"
 | 
			
		||||
#include "PollReply.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArtnetnodeWifi
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  ArtnetnodeWifi();
 | 
			
		||||
 | 
			
		||||
  uint8_t begin(String hostname = "");
 | 
			
		||||
  uint16_t read();
 | 
			
		||||
 | 
			
		||||
  // Node identity
 | 
			
		||||
  void setShortName(const char name[]);
 | 
			
		||||
  void setLongName(const char name[]);
 | 
			
		||||
  void setName(const char name[]);
 | 
			
		||||
  void setNumPorts(uint8_t num);
 | 
			
		||||
 | 
			
		||||
  void setStartingUniverse(uint16_t startingUniverse);
 | 
			
		||||
 | 
			
		||||
  // Transmit
 | 
			
		||||
  int write(void);
 | 
			
		||||
  int write(IPAddress ip);
 | 
			
		||||
  void setByte(uint16_t pos, uint8_t value);
 | 
			
		||||
 | 
			
		||||
  inline void setUniverse(uint16_t universe)
 | 
			
		||||
  {
 | 
			
		||||
    outgoingUniverse = universe;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline void setPhysical(uint8_t port)
 | 
			
		||||
  {
 | 
			
		||||
    physical = port;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline void setLength(uint16_t len)
 | 
			
		||||
  {
 | 
			
		||||
    dmxDataLength = len;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline void setPortType(uint8_t port, uint8_t type)
 | 
			
		||||
  {
 | 
			
		||||
    PollReplyPacket.setPortType(port, type);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // DMX controls
 | 
			
		||||
  void enableDMX();
 | 
			
		||||
  void disableDMX();
 | 
			
		||||
  void enableDMXOutput(uint8_t outputID);
 | 
			
		||||
  void disableDMXOutput(uint8_t outputID);
 | 
			
		||||
 | 
			
		||||
  uint8_t setDMXOutput(uint8_t outputID, uint8_t uartNum, uint16_t attachedUniverse);
 | 
			
		||||
 | 
			
		||||
  // DMX tick
 | 
			
		||||
  void tickDMX(uint32_t time);
 | 
			
		||||
  
 | 
			
		||||
  // Return a pointer to the start of the DMX data
 | 
			
		||||
  inline uint8_t* getDmxFrame(void)
 | 
			
		||||
  {
 | 
			
		||||
    return artnetPacket + ARTNET_DMX_START_LOC;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline void setArtDmxCallback(void (*fptr)(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data)) 
 | 
			
		||||
  {
 | 
			
		||||
    artDmxCallback = fptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  WiFiUDP Udp;
 | 
			
		||||
  PollReply PollReplyPacket;
 | 
			
		||||
  String host;
 | 
			
		||||
 | 
			
		||||
  // Packet handlers
 | 
			
		||||
  uint16_t handleDMX(uint8_t nzs);
 | 
			
		||||
  uint16_t handlePollRequest();
 | 
			
		||||
  
 | 
			
		||||
  // Packet vars
 | 
			
		||||
  uint8_t artnetPacket[ARTNET_MAX_BUFFER];
 | 
			
		||||
  uint16_t packetSize;
 | 
			
		||||
  uint16_t opcode;
 | 
			
		||||
  uint8_t sequence;
 | 
			
		||||
  uint8_t physical;
 | 
			
		||||
  uint16_t outgoingUniverse;
 | 
			
		||||
  uint16_t dmxDataLength;
 | 
			
		||||
  IPAddress localIP;
 | 
			
		||||
  IPAddress localMask;
 | 
			
		||||
  IPAddress localBroadcast;
 | 
			
		||||
 | 
			
		||||
  // Packet functions
 | 
			
		||||
  bool isBroadcast();
 | 
			
		||||
  uint16_t makePacket(void);
 | 
			
		||||
 | 
			
		||||
  // DMX settings
 | 
			
		||||
  bool DMXOutputStatus;
 | 
			
		||||
  uint16_t DMXOutputs[DMX_MAX_OUTPUTS][3];
 | 
			
		||||
  uint8_t DMXBuffer[DMX_MAX_OUTPUTS][DMX_MAX_BUFFER];
 | 
			
		||||
 | 
			
		||||
  uint16_t startingUniverse;
 | 
			
		||||
 | 
			
		||||
  // DMX tick
 | 
			
		||||
  void sendDMX();
 | 
			
		||||
  uint8_t* getDmxFrame(uint8_t outputID);
 | 
			
		||||
  uint8_t msSinceDMXSend;
 | 
			
		||||
 | 
			
		||||
  void (*artDmxCallback)(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data);
 | 
			
		||||
  static const char artnetId[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										22
									
								
								RpiLedBars/src/artnet/NodeReportCodes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								RpiLedBars/src/artnet/NodeReportCodes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
#ifndef NODEREPORTCODES_H
 | 
			
		||||
#define NODEREPORTCODES_H
 | 
			
		||||
// List of hex values and discriptions of Node Report Codes
 | 
			
		||||
 | 
			
		||||
#define RcDebug        0x0000  // Booted in debug mode (Only used in development)
 | 
			
		||||
#define RcPowerOk      0x0001  // Power On Tests successful
 | 
			
		||||
#define RcPowerFail    0x0002  // Hardware tests failed at Power On
 | 
			
		||||
#define RcSocketWr1    0x0003  // Last UDP from Node failed due to truncated length, Most likely caused by a collision.
 | 
			
		||||
#define RcParseFail    0x0004  // Unable to identify last UDP transmission. Check OpCode and \packet length.
 | 
			
		||||
#define RcUdpFail      0x0005  // Unable to open Udp Socket in last transmission attempt
 | 
			
		||||
#define RcShNameOk     0x0006  // Confirms that Short Name programming via ArtAddress, was successful.
 | 
			
		||||
#define RcLoNameOk     0x0007  // Confirms that Long Name programming via ArtAddress, was successful.
 | 
			
		||||
#define RcDmxError     0x0008  // DMX512 receive errors detected.
 | 
			
		||||
#define RcDmxUdpFull   0x0009  // Ran out of internal DMX transmit buffers.
 | 
			
		||||
#define RcDmxRxFull    0x000a  // Ran out of internal DMX Rx buffers.
 | 
			
		||||
#define RcSwitchErr    0x000b  // Rx Universe switches conflict.
 | 
			
		||||
#define RcConfigErr    0x000c  // Product configuration does not match firmware.
 | 
			
		||||
#define RcDmxShort     0x000d  // DMX output short detected. See GoodOutput field.
 | 
			
		||||
#define RcFirmwareFail 0x000e  // Last attempt to upload new firmware failed.
 | 
			
		||||
#define RcUserFail     0x000f  // User changed switch settings when address locked by remote programming. User changes ignored.
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										136
									
								
								RpiLedBars/src/artnet/PollReply.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								RpiLedBars/src/artnet/PollReply.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Copyright (c) Charles Yarnold charlesyarnold@gmail.com 2015
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2016 Stephan Ruloff
 | 
			
		||||
https://github.com/rstephan
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, under version 2 of the License.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Class for saving details to and for constructing pollreply packets
 | 
			
		||||
 | 
			
		||||
#include <PollReply.h>
 | 
			
		||||
 | 
			
		||||
PollReply::PollReply(){
 | 
			
		||||
  // Zero out vars
 | 
			
		||||
  memset(packet.IPAddr, 0, sizeof(packet.IPAddr));
 | 
			
		||||
  memset(packet.NodeReport, 0, sizeof(packet.NodeReport));
 | 
			
		||||
  memset(packet.PortTypes, 0, sizeof(packet.PortTypes));
 | 
			
		||||
  memset(packet.GoodInput, 0, sizeof(packet.GoodInput));
 | 
			
		||||
  memset(packet.GoodOutput, 0, sizeof(packet.GoodOutput));
 | 
			
		||||
  memset(packet.SwIn, 0, sizeof(packet.SwIn));
 | 
			
		||||
  memset(packet.SwOut, 0, sizeof(packet.SwOut));
 | 
			
		||||
  memset(packet.Filler, 0, sizeof(packet.Filler));
 | 
			
		||||
  setStartingUniverse(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setMac(byte mac[]){
 | 
			
		||||
  packet.Mac[0] = mac[0];
 | 
			
		||||
  packet.Mac[1] = mac[1];
 | 
			
		||||
  packet.Mac[2] = mac[2];
 | 
			
		||||
  packet.Mac[3] = mac[3];
 | 
			
		||||
  packet.Mac[4] = mac[4];
 | 
			
		||||
  packet.Mac[5] = mac[5];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setIP(IPAddress IP){
 | 
			
		||||
  packet.IPAddr[0] = IP[0];
 | 
			
		||||
  packet.IPAddr[1] = IP[1];
 | 
			
		||||
  packet.IPAddr[2] = IP[2];
 | 
			
		||||
  packet.IPAddr[3] = IP[3];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setShortName(const char name[]){
 | 
			
		||||
  int shortNameLen = sizeof(packet.ShortName);
 | 
			
		||||
 | 
			
		||||
  memset(packet.ShortName, 0, shortNameLen);
 | 
			
		||||
 | 
			
		||||
  shortNameLen--;
 | 
			
		||||
  for(int i = 0; i < shortNameLen && name[i] != 0; i++){
 | 
			
		||||
    packet.ShortName[i] = name[i];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setLongName(const char name[]){
 | 
			
		||||
  int longNameLen = sizeof(packet.LongName);
 | 
			
		||||
 | 
			
		||||
  memset(packet.LongName, 0, longNameLen);
 | 
			
		||||
 | 
			
		||||
  longNameLen--;
 | 
			
		||||
  for(int i = 0; i < longNameLen && name[i] != 0; i++){
 | 
			
		||||
    packet.LongName[i] = name[i];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::canDHCP(bool can){
 | 
			
		||||
  if(can){
 | 
			
		||||
    packet.Status2 = packet.Status2 | 0b00100000;
 | 
			
		||||
  }
 | 
			
		||||
  else{
 | 
			
		||||
    packet.Status2 = packet.Status2 & (~0b00100000);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::isDHCP(bool is){
 | 
			
		||||
  if(is){
 | 
			
		||||
    packet.Status2 = packet.Status2 | 0b01000000;
 | 
			
		||||
  }
 | 
			
		||||
  else{
 | 
			
		||||
    packet.Status2 = packet.Status2 & (~0b01000000);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setNumPorts(uint8_t num){
 | 
			
		||||
  packet.NumPortsLo = num;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setPortType(uint8_t port, uint8_t type)
 | 
			
		||||
{
 | 
			
		||||
  if (port < 4) {
 | 
			
		||||
    packet.PortTypes[port] = type;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setSwOut(uint8_t port, uint16_t universe){
 | 
			
		||||
  if(port < 4){
 | 
			
		||||
    packet.SwOut[port] = universe & 0b0000000000001111;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setOutputEnabled(uint8_t port){
 | 
			
		||||
  if(port < 4){
 | 
			
		||||
    packet.PortTypes[port] = packet.PortTypes[port] | 0b10000000;
 | 
			
		||||
    packet.GoodOutput[port] = packet.GoodOutput[port] | 0b10000000;
 | 
			
		||||
    setSwOut(port, startingUniverse + port);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setOutputDisabled(uint8_t port){
 | 
			
		||||
  if(port < 4){
 | 
			
		||||
    packet.PortTypes[port] = packet.PortTypes[port] & (~0b10000000);
 | 
			
		||||
    packet.GoodOutput[port] = packet.GoodOutput[port] & (~0b10000000);
 | 
			
		||||
    setSwOut(port, 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollReply::setStartingUniverse(uint16_t startUniverse){
 | 
			
		||||
  startingUniverse = startUniverse;
 | 
			
		||||
  packet.NetSwitch = startUniverse >> 8;
 | 
			
		||||
  packet.SubSwitch = (startUniverse & 0b000000011111111) >> 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t* PollReply::printPacket(){
 | 
			
		||||
  return (uint8_t *)&packet;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										188
									
								
								RpiLedBars/src/artnet/PollReply.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								RpiLedBars/src/artnet/PollReply.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,188 @@
 | 
			
		||||
#ifndef POLLREPLY_H
 | 
			
		||||
#define POLLREPLY_H
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Copyright (c) Charles Yarnold charlesyarnold@gmail.com 2015
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, under version 2 of the License.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Class for saving details to and for constructing pollreply packets
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <WiFiUdp.h>
 | 
			
		||||
#include "OpCodes.h"
 | 
			
		||||
#include "NodeReportCodes.h"
 | 
			
		||||
#include "StyleCodes.h"
 | 
			
		||||
#include "PriorityCodes.h"
 | 
			
		||||
#include "ProtocolSettings.h"
 | 
			
		||||
 | 
			
		||||
struct replyPollPacket{
 | 
			
		||||
  char ID[8] = "Art-Net"; // protocol ID = "Art-Net"
 | 
			
		||||
  //char ID[8]; // protocol ID = "Art-Net"
 | 
			
		||||
  uint16_t OpCode = OpPollReply; // == OpPollReply
 | 
			
		||||
  uint8_t IPAddr[4]; // 0 if not yet configured
 | 
			
		||||
  uint16_t Port = 0x1936;
 | 
			
		||||
 | 
			
		||||
  uint8_t VersionInfoHi = 0; // The node's current FIRMWARE VERS hi
 | 
			
		||||
  uint8_t VersionInfoLo = 1; // The node's current FIRMWARE VERS lo
 | 
			
		||||
 | 
			
		||||
  uint8_t NetSwitch = 0; // Bits 14-8 of the 15 bit universe number are encoded into the bottom 7 bits of this field.
 | 
			
		||||
  // This is used in combination with SubSwitch and Swin[] or Swout[] to produce the full universe address.
 | 
			
		||||
  uint8_t SubSwitch = 0; // Bits 7-4 of the 15 bit universe number are encoded into the bottom 4 bits of this field.
 | 
			
		||||
  // This is used in combination with NetSwitch and Swin[] or Swout[] to produce the full universe address.
 | 
			
		||||
 | 
			
		||||
  uint16_t Oem = 0x0190; // Manufacturer code, bit 15 set if
 | 
			
		||||
  // extended features avail
 | 
			
		||||
 | 
			
		||||
  uint8_t UbeaVersion = 0; // Firmware version of UBEA
 | 
			
		||||
 | 
			
		||||
  uint8_t Status = 0;
 | 
			
		||||
  // 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] = {0, 0};      // ESTA manufacturer id, lo byte
 | 
			
		||||
 | 
			
		||||
  char ShortName[18] = "ElLab Artnetnode\0"; // short name defaults to IP
 | 
			
		||||
  char LongName[64] = "Electric Laboratory Artnetnode\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";   // long name
 | 
			
		||||
  char NodeReport[64]; // Text feedback of Node status or errors
 | 
			
		||||
  //  also used for debug info
 | 
			
		||||
 | 
			
		||||
  uint8_t NumPortsHi = 0; // 0
 | 
			
		||||
  uint8_t NumPortsLo = 0; // 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 = 0;
 | 
			
		||||
  // Low nibble is the value of the video
 | 
			
		||||
  // output channel
 | 
			
		||||
 | 
			
		||||
  uint8_t SwMacro = 0;
 | 
			
		||||
  // Bit 0 is Macro input 1
 | 
			
		||||
  // Bit 7 is Macro input 8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  uint8_t SwRemote = 0;
 | 
			
		||||
  // Bit 0 is Remote input 1
 | 
			
		||||
  // Bit 7 is Remote input 8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  uint8_t Spare1 = 0; // Spare, currently zero
 | 
			
		||||
  uint8_t Spare2 = 0; // Spare, currently zero
 | 
			
		||||
  uint8_t Spare3 = 0; // Spare, currently zero
 | 
			
		||||
  uint8_t Style = 0; // 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 = 0; // 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 = 0b00000000;
 | 
			
		||||
  // 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.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PollReply{
 | 
			
		||||
public:
 | 
			
		||||
  PollReply();
 | 
			
		||||
 | 
			
		||||
  void setMac(byte mac[]);
 | 
			
		||||
  void setIP(IPAddress IP);
 | 
			
		||||
  void setShortName(const char name[]);
 | 
			
		||||
  void setLongName(const char name[]);
 | 
			
		||||
 | 
			
		||||
  void setNumPorts(uint8_t num);
 | 
			
		||||
 | 
			
		||||
  void setSwOut(uint8_t id, uint16_t universe);
 | 
			
		||||
  void setPortType(uint8_t port, uint8_t type);
 | 
			
		||||
 | 
			
		||||
  void setOutputEnabled(uint8_t port);
 | 
			
		||||
  void setOutputDisabled(uint8_t port);
 | 
			
		||||
 | 
			
		||||
  void canDHCP(bool can);
 | 
			
		||||
  void isDHCP(bool is);
 | 
			
		||||
 | 
			
		||||
  void setStartingUniverse(uint16_t startUniverse);
 | 
			
		||||
 | 
			
		||||
  uint8_t* printPacket();
 | 
			
		||||
 | 
			
		||||
  struct replyPollPacket packet;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  uint16_t startingUniverse;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1
									
								
								RpiLedBars/src/artnet/artnet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								RpiLedBars/src/artnet/artnet.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
#include "artnet.h"
 | 
			
		||||
							
								
								
									
										4
									
								
								RpiLedBars/src/artnet/artnet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								RpiLedBars/src/artnet/artnet.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
#if !defined(__ARTNET_H__)
 | 
			
		||||
#define __ARTNET_H__
 | 
			
		||||
 | 
			
		||||
#endif // __ARTNET_H__
 | 
			
		||||
							
								
								
									
										128
									
								
								RpiLedBars/src/artnet/artnet_op_codes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								RpiLedBars/src/artnet/artnet_op_codes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
#if !defined(__ARTNET_OP_CODES_H__)
 | 
			
		||||
#define __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 // __ARTNET_OP_CODES_H__
 | 
			
		||||
							
								
								
									
										26
									
								
								RpiLedBars/src/artnet/artnet_protocol_settings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								RpiLedBars/src/artnet/artnet_protocol_settings.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#if !defined(__PROTOCOL_SETTINGS_H__)
 | 
			
		||||
#define __PROTOCOL_SETTINGS_H__
 | 
			
		||||
 | 
			
		||||
// UDP port
 | 
			
		||||
#define ARTNET_PORT 6454
 | 
			
		||||
 | 
			
		||||
// 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 // __PROTOCOL_SETTINGS_H__
 | 
			
		||||
							
								
								
									
										242
									
								
								RpiLedBars/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								RpiLedBars/src/main.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,242 @@
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "rpi_artnet.h"
 | 
			
		||||
#include "rpi_pixleds.h"
 | 
			
		||||
#include "rpi_smi_defs.h"
 | 
			
		||||
 | 
			
		||||
#include "rpi_selector.h"
 | 
			
		||||
 | 
			
		||||
/*  Command-line parameters */
 | 
			
		||||
bool IsTestMode = false;
 | 
			
		||||
int chanLedCount = 0;
 | 
			
		||||
 | 
			
		||||
/* Global */
 | 
			
		||||
MEM_MAP vc_mem;
 | 
			
		||||
TXDATA_T *txdata;
 | 
			
		||||
 | 
			
		||||
void parseCommandLineArgs(int argc, char const *argv[]);
 | 
			
		||||
 | 
			
		||||
void execute_test_mode();
 | 
			
		||||
 | 
			
		||||
void execute_artnet_mode();
 | 
			
		||||
 | 
			
		||||
void execute_autonomous_mode();
 | 
			
		||||
 | 
			
		||||
void execute_autonomous2_mode();
 | 
			
		||||
 | 
			
		||||
int main(int argc, char const *argv[]) {
 | 
			
		||||
  int previousMode = 0;
 | 
			
		||||
 | 
			
		||||
  // setup
 | 
			
		||||
  parseCommandLineArgs(argc, argv);
 | 
			
		||||
 | 
			
		||||
  signal(SIGINT, terminate);
 | 
			
		||||
 | 
			
		||||
  setup_selector();
 | 
			
		||||
 | 
			
		||||
  // setup led
 | 
			
		||||
  map_devices();
 | 
			
		||||
  init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
 | 
			
		||||
  map_uncached_mem(&vc_mem, VC_MEM_SIZE);
 | 
			
		||||
 | 
			
		||||
  setup_smi_dma(&vc_mem, TX_BUFF_LEN(chanLedCount), &txdata);
 | 
			
		||||
 | 
			
		||||
  artnet_init();
 | 
			
		||||
 | 
			
		||||
  // loop
 | 
			
		||||
  while (1) {
 | 
			
		||||
    int mode = get_selector_position();
 | 
			
		||||
 | 
			
		||||
    if (mode != previousMode) {
 | 
			
		||||
#if defined(_DEBUG)
 | 
			
		||||
      print_selector();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (mode) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      // mode test
 | 
			
		||||
      execute_test_mode();
 | 
			
		||||
      break;
 | 
			
		||||
    case 1:
 | 
			
		||||
      // artnet mode
 | 
			
		||||
      execute_artnet_mode();
 | 
			
		||||
      break;
 | 
			
		||||
    case 2:
 | 
			
		||||
      execute_autonomous_mode();
 | 
			
		||||
      break;
 | 
			
		||||
    case 3:
 | 
			
		||||
      // autonomous mode 2
 | 
			
		||||
      execute_autonomous2_mode();
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      printf("error in selector \n");
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    previousMode = mode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //   printf("%s %u LED%s per channel, %u channels\n", IsTestMode ? "Testing" : "Setting",
 | 
			
		||||
  //   chanLedCount,
 | 
			
		||||
  //          chanLedCount == 1 ? "" : "s", LED_NCHANS);
 | 
			
		||||
 | 
			
		||||
  //   for (size_t colorIndex = 0; colorIndex < 3; ++colorIndex) {
 | 
			
		||||
  //     for (size_t i = 0; i < chanLedCount; ++i) {
 | 
			
		||||
  //       set_color(on_rgbs[colorIndex], &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
  //     }
 | 
			
		||||
 | 
			
		||||
  // #if LED_NCHANS <= 8
 | 
			
		||||
  //     swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
  // #endif
 | 
			
		||||
 | 
			
		||||
  //     memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
  //     start_smi(&vc_mem);
 | 
			
		||||
  //     usleep(CHASE_MSEC * 1000);
 | 
			
		||||
  //     sleep(1);
 | 
			
		||||
  //   }
 | 
			
		||||
 | 
			
		||||
  //   artnet_init();
 | 
			
		||||
 | 
			
		||||
  //   // loops
 | 
			
		||||
  //   if (IsTestMode) {
 | 
			
		||||
  //     while (1) {
 | 
			
		||||
  //       test_leds();
 | 
			
		||||
  //       sleep(3);
 | 
			
		||||
  //     }
 | 
			
		||||
  //   } else {
 | 
			
		||||
  //     while (1) {
 | 
			
		||||
  //       artDmx_t *artDmx;
 | 
			
		||||
  //       if (artnet_read(&artDmx) == OpDmx) {
 | 
			
		||||
  //         uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
 | 
			
		||||
  //         unsigned int ledCountInFrame = dmxLength / 3;
 | 
			
		||||
  //         uint16_t maxBound = ledCountInFrame < chanLedCount ? ledCountInFrame : chanLedCount;
 | 
			
		||||
  //         unsigned int universe = artDmx->subUni & (LED_NCHANS - 1);
 | 
			
		||||
  //         for (size_t i = 0; i < maxBound; ++i) {
 | 
			
		||||
  //           uint8_t *rgb = artDmx->data + (i * 3);
 | 
			
		||||
  //           rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
 | 
			
		||||
  //           rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
  //         }
 | 
			
		||||
 | 
			
		||||
  // #if LED_NCHANS <= 8
 | 
			
		||||
  //         swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
  // #endif
 | 
			
		||||
 | 
			
		||||
  //         memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
  //         // enable_dma(DMA_CHAN);
 | 
			
		||||
  //         start_smi(&vc_mem);
 | 
			
		||||
  //         // usleep(10);
 | 
			
		||||
  //         // while (dma_active(DMA_CHAN))
 | 
			
		||||
  //         //     usleep(10);
 | 
			
		||||
  //       }
 | 
			
		||||
  //     }
 | 
			
		||||
  //   }
 | 
			
		||||
  //   terminate(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void parseCommandLineArgs(int argc, char const *argv[]) {
 | 
			
		||||
  int args = 0;
 | 
			
		||||
 | 
			
		||||
  while (argc > ++args) // Process command-line args
 | 
			
		||||
  {
 | 
			
		||||
    if (argv[args][0] == '-') {
 | 
			
		||||
      switch (toupper(argv[args][1])) {
 | 
			
		||||
      case 'N': // -N: number of LEDs per channel
 | 
			
		||||
        if (args >= argc - 1)
 | 
			
		||||
          fprintf(stderr, "Error: no numeric value\n");
 | 
			
		||||
        else
 | 
			
		||||
          chanLedCount = atoi(argv[++args]);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'T': // -T: test mode
 | 
			
		||||
        IsTestMode = true;
 | 
			
		||||
        break;
 | 
			
		||||
      default: // Otherwise error
 | 
			
		||||
        printf("Unrecognised option '%c'\n", argv[args][1]);
 | 
			
		||||
        printf("Options:\n"
 | 
			
		||||
               "  -n num    number of LEDs per channel\n"
 | 
			
		||||
               "  -t        Test mode (flash LEDs)\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// Pointer to uncached Tx data buffer
 | 
			
		||||
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
 | 
			
		||||
 | 
			
		||||
void execute_test_mode() {
 | 
			
		||||
  // RGB values for test mode (1 value for each of 16 channels)
 | 
			
		||||
  uint32_t on_rgbs[] = {0xef0000, 0x00ef00, 0x0000ef, 0xefef00, 0xef00ef, 0x00efef, 0xefefef};
 | 
			
		||||
  uint32_t off_rgbs = 0x000000;
 | 
			
		||||
 | 
			
		||||
  static int i = 0, offset = 0, ledIndex = 0;
 | 
			
		||||
 | 
			
		||||
  if (ledIndex < chanLedCount) {
 | 
			
		||||
    set_color(ledIndex <= offset % chanLedCount ? on_rgbs[i] : off_rgbs,
 | 
			
		||||
              &tx_buffer[LED_TX_OSET(ledIndex)]);
 | 
			
		||||
    ++ledIndex;
 | 
			
		||||
 | 
			
		||||
  } else {
 | 
			
		||||
    ledIndex = 0;
 | 
			
		||||
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
    swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
    start_smi(&vc_mem);
 | 
			
		||||
    usleep(CHASE_MSEC * 1000);
 | 
			
		||||
    if (offset < chanLedCount) {
 | 
			
		||||
      ++offset;
 | 
			
		||||
    } else {
 | 
			
		||||
      offset = 0;
 | 
			
		||||
      if (i < 7) {
 | 
			
		||||
        ++i;
 | 
			
		||||
      } else {
 | 
			
		||||
        i = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void execute_artnet_mode() {
 | 
			
		||||
  artDmx_t *artDmx;
 | 
			
		||||
  // RGB data
 | 
			
		||||
  int rgb_data[CHAN_MAXLEDS][LED_NCHANS];
 | 
			
		||||
 | 
			
		||||
  if (artnet_read(&artDmx) == OpDmx) {
 | 
			
		||||
    uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
 | 
			
		||||
    unsigned int ledCountInFrame = dmxLength / 3;
 | 
			
		||||
    uint16_t maxBound = ledCountInFrame < chanLedCount ? ledCountInFrame : chanLedCount;
 | 
			
		||||
    unsigned int universe = artDmx->subUni & (LED_NCHANS - 1);
 | 
			
		||||
    for (size_t i = 0; i < maxBound; ++i) {
 | 
			
		||||
      uint8_t *rgb = artDmx->data + (i * 3);
 | 
			
		||||
      rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
 | 
			
		||||
      rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
    swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
 | 
			
		||||
    enable_dma(DMA_CHAN);
 | 
			
		||||
    start_smi(&vc_mem);
 | 
			
		||||
    usleep(10);
 | 
			
		||||
    while (dma_active(DMA_CHAN))
 | 
			
		||||
      usleep(10);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void execute_autonomous_mode() {}
 | 
			
		||||
 | 
			
		||||
void execute_autonomous2_mode() {}
 | 
			
		||||
@@ -30,6 +30,8 @@
 | 
			
		||||
// v0.11 JPB 29/9/20 Added enable_dma before transfer (in case still active)
 | 
			
		||||
//                   Corrected DMA nsamp value (was byte count)
 | 
			
		||||
 | 
			
		||||
#include "rpi_pixleds.h"
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
@@ -41,37 +43,8 @@
 | 
			
		||||
#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)
 | 
			
		||||
#define SMI_TIMING 10, 15, 30, 15  // 400 ns cycle time
 | 
			
		||||
#else                              // Timings for RPi v0-3 (1 GHz)
 | 
			
		||||
#define SMI_TIMING 10, 10, 20, 10  // 400 ns cycle time
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TX_TEST 0        // If non-zero, use dummy Tx data
 | 
			
		||||
#define LED_D0_PIN 8     // GPIO pin for D0 output
 | 
			
		||||
#define LED_NCHANS 8     // Number of LED channels (8 or 16)
 | 
			
		||||
#define LED_NBITS 24     // Number of data bits per LED
 | 
			
		||||
#define LED_PREBITS 4    // Number of zero bits before LED data
 | 
			
		||||
#define LED_POSTBITS 4   // Number of zero bits after LED data
 | 
			
		||||
#define BIT_NPULSES 3    // Number of O/P pulses per LED bit
 | 
			
		||||
#define CHAN_MAXLEDS 50  // Maximum number of LEDs per channel
 | 
			
		||||
#define CHASE_MSEC 100   // Delay time for chaser light test
 | 
			
		||||
#define REQUEST_THRESH 2 // DMA request threshold
 | 
			
		||||
#define DMA_CHAN 10      // DMA channel to use
 | 
			
		||||
 | 
			
		||||
// Length of data for 1 row (1 LED on each channel)
 | 
			
		||||
#define LED_DLEN (LED_NBITS * BIT_NPULSES)
 | 
			
		||||
 | 
			
		||||
// Transmit data type, 8 or 16 bits
 | 
			
		||||
#if LED_NCHANS > 8
 | 
			
		||||
#define TXDATA_T uint16_t
 | 
			
		||||
#else
 | 
			
		||||
#define TXDATA_T uint8_t
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Structures for mapped I/O devices, and non-volatile memory
 | 
			
		||||
extern MEM_MAP gpio_regs, dma_regs;
 | 
			
		||||
MEM_MAP vc_mem, clk_regs, smi_regs;
 | 
			
		||||
@@ -88,132 +61,110 @@ volatile SMI_DCS_REG *smi_dcs;
 | 
			
		||||
volatile SMI_DCA_REG *smi_dca;
 | 
			
		||||
volatile SMI_DCD_REG *smi_dcd;
 | 
			
		||||
 | 
			
		||||
// Ofset into Tx data buffer, given LED number in chan
 | 
			
		||||
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
 | 
			
		||||
 | 
			
		||||
// Size of data buffers & NV memory, given number of LEDs per chan
 | 
			
		||||
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
 | 
			
		||||
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
 | 
			
		||||
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
 | 
			
		||||
 | 
			
		||||
// RGB values for test mode (1 value for each of 16 channels)
 | 
			
		||||
int on_rgbs[16] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040,
 | 
			
		||||
                   0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0xff4040, 0x40ff40, 0x4040ff, 0x404040};
 | 
			
		||||
int off_rgbs[16];
 | 
			
		||||
// uint32_t on_rgbs[] = {0xef0000, 0x00ef00, 0x0000ef, 0xefef00, 0xef00ef, 0x00efef, 0xefefef};
 | 
			
		||||
// uint32_t off_rgbs = 0x000000;
 | 
			
		||||
 | 
			
		||||
#if TX_TEST
 | 
			
		||||
// Data for simple transmission test
 | 
			
		||||
TXDATA_T tx_test_data[] = {1, 2, 3, 4, 5, 6, 7, 0};
 | 
			
		||||
#endif
 | 
			
		||||
// TXDATA_T *txdata;                              // Pointer to uncached Tx data buffer
 | 
			
		||||
// TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
 | 
			
		||||
// int testmode, chan_ledcount = 1;               // Command-line parameters
 | 
			
		||||
int rgb_data[CHAN_MAXLEDS][LED_NCHANS]; // RGB data
 | 
			
		||||
int chan_num;
 | 
			
		||||
 | 
			
		||||
TXDATA_T *txdata;                              // Pointer to uncached Tx data buffer
 | 
			
		||||
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
 | 
			
		||||
int testmode, chan_ledcount = 1;               // Command-line parameters
 | 
			
		||||
int rgb_data[CHAN_MAXLEDS][LED_NCHANS];        // RGB data
 | 
			
		||||
int chan_num;                                  // Current channel for data I/P
 | 
			
		||||
// Current channel for data I/P
 | 
			
		||||
 | 
			
		||||
void rgb_txdata(int *rgbs, TXDATA_T *txd);
 | 
			
		||||
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
 | 
			
		||||
void swap_bytes(void *data, int len);
 | 
			
		||||
int hexdig(char c);
 | 
			
		||||
void map_devices(void);
 | 
			
		||||
void fail(char *s);
 | 
			
		||||
void terminate(int sig);
 | 
			
		||||
void init_smi(int width, int ns, int setup, int hold, int strobe);
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp);
 | 
			
		||||
void start_smi(MEM_MAP *mp);
 | 
			
		||||
// int main(int argc, char *argv[]) {
 | 
			
		||||
//   // setup
 | 
			
		||||
//   int args = 0, n;
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[]) {
 | 
			
		||||
  // setup
 | 
			
		||||
  int args = 0, n, oset = 0;
 | 
			
		||||
  memset(off_rgbs, 0, sizeof(int) * 16);
 | 
			
		||||
//   while (argc > ++args) // Process command-line args
 | 
			
		||||
//   {
 | 
			
		||||
//     if (argv[args][0] == '-') {
 | 
			
		||||
//       switch (toupper(argv[args][1])) {
 | 
			
		||||
//       case 'N': // -N: number of LEDs per channel
 | 
			
		||||
//         if (args >= argc - 1)
 | 
			
		||||
//           fprintf(stderr, "Error: no numeric value\n");
 | 
			
		||||
//         else
 | 
			
		||||
//           chan_ledcount = atoi(argv[++args]);
 | 
			
		||||
//         break;
 | 
			
		||||
//       case 'T': // -T: test mode
 | 
			
		||||
//         testmode = 1;
 | 
			
		||||
//         break;
 | 
			
		||||
//       default: // Otherwise error
 | 
			
		||||
//         printf("Unrecognised option '%c'\n", argv[args][1]);
 | 
			
		||||
//         printf("Options:\n"
 | 
			
		||||
//                "  -n num    number of LEDs per channel\n"
 | 
			
		||||
//                "  -t        Test mode (flash LEDs)\n");
 | 
			
		||||
//         return (1);
 | 
			
		||||
//       }
 | 
			
		||||
//     } else if (chan_num < LED_NCHANS && hexdig(argv[args][0]) >= 0 &&
 | 
			
		||||
//                (n = str_rgb(argv[args], rgb_data, chan_num)) > 0) {
 | 
			
		||||
//       chan_ledcount = n > chan_ledcount ? n : chan_ledcount;
 | 
			
		||||
//       chan_num++;
 | 
			
		||||
//     }
 | 
			
		||||
//   }
 | 
			
		||||
//   signal(SIGINT, terminate);
 | 
			
		||||
//   map_devices();
 | 
			
		||||
//   init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
 | 
			
		||||
//   map_uncached_mem(&vc_mem, VC_MEM_SIZE);
 | 
			
		||||
 | 
			
		||||
  while (argc > ++args) // Process command-line args
 | 
			
		||||
  {
 | 
			
		||||
    if (argv[args][0] == '-') {
 | 
			
		||||
      switch (toupper(argv[args][1])) {
 | 
			
		||||
      case 'N': // -N: number of LEDs per channel
 | 
			
		||||
        if (args >= argc - 1)
 | 
			
		||||
          fprintf(stderr, "Error: no numeric value\n");
 | 
			
		||||
        else
 | 
			
		||||
          chan_ledcount = atoi(argv[++args]);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'T': // -T: test mode
 | 
			
		||||
        testmode = 1;
 | 
			
		||||
        break;
 | 
			
		||||
      default: // Otherwise error
 | 
			
		||||
        printf("Unrecognised option '%c'\n", argv[args][1]);
 | 
			
		||||
        printf("Options:\n"
 | 
			
		||||
               "  -n num    number of LEDs per channel\n"
 | 
			
		||||
               "  -t        Test mode (flash LEDs)\n");
 | 
			
		||||
        return (1);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (chan_num < LED_NCHANS && hexdig(argv[args][0]) >= 0 &&
 | 
			
		||||
               (n = str_rgb(argv[args], rgb_data, chan_num)) > 0) {
 | 
			
		||||
      chan_ledcount = n > chan_ledcount ? n : chan_ledcount;
 | 
			
		||||
      chan_num++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  signal(SIGINT, terminate);
 | 
			
		||||
  map_devices();
 | 
			
		||||
  init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
 | 
			
		||||
  map_uncached_mem(&vc_mem, VC_MEM_SIZE);
 | 
			
		||||
//   setup_smi_dma(&vc_mem, TX_BUFF_LEN(chan_ledcount));
 | 
			
		||||
//   printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting",
 | 
			
		||||
//   chan_ledcount,
 | 
			
		||||
//          chan_ledcount == 1 ? "" : "s", LED_NCHANS);
 | 
			
		||||
 | 
			
		||||
  setup_smi_dma(&vc_mem, TX_BUFF_LEN(chan_ledcount));
 | 
			
		||||
  printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting", chan_ledcount,
 | 
			
		||||
         chan_ledcount == 1 ? "" : "s", LED_NCHANS);
 | 
			
		||||
//   for (size_t colorIndex = 0; colorIndex < 3; ++colorIndex) {
 | 
			
		||||
//     for (size_t i = 0; i < chan_ledcount; ++i) {
 | 
			
		||||
//       set_color(on_rgbs[colorIndex], &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
//     }
 | 
			
		||||
 | 
			
		||||
  memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
  start_smi(&vc_mem);
 | 
			
		||||
  artnet_init();
 | 
			
		||||
// #if LED_NCHANS <= 8
 | 
			
		||||
//     swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
// #endif
 | 
			
		||||
 | 
			
		||||
  // loops
 | 
			
		||||
  if (testmode) {
 | 
			
		||||
    while (1) {
 | 
			
		||||
      if (chan_ledcount < 2)
 | 
			
		||||
        rgb_txdata(oset & 1 ? off_rgbs : on_rgbs, tx_buffer);
 | 
			
		||||
      else {
 | 
			
		||||
        for (n = 0; n < chan_ledcount; n++) {
 | 
			
		||||
          rgb_txdata(n == oset % chan_ledcount ? on_rgbs : off_rgbs, &tx_buffer[LED_TX_OSET(n)]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      oset++;
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
      swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
#endif
 | 
			
		||||
      memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
      start_smi(&vc_mem);
 | 
			
		||||
      usleep(CHASE_MSEC * 1000);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    while (1) {
 | 
			
		||||
      artDmx_t *artDmx;
 | 
			
		||||
      if (artnet_read(&artDmx) == OpDmx) {
 | 
			
		||||
        uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
 | 
			
		||||
        unsigned int ledCountInFrame = dmxLength / 3;
 | 
			
		||||
        uint16_t maxBound = ledCountInFrame < chan_ledcount ? ledCountInFrame : chan_ledcount;
 | 
			
		||||
        unsigned int universe = artDmx->subUni & (LED_NCHANS - 1);
 | 
			
		||||
        for (size_t i = 0; i < maxBound; ++i) {
 | 
			
		||||
          uint8_t *rgb = artDmx->data + (i * 3);
 | 
			
		||||
          rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
 | 
			
		||||
          rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
        }
 | 
			
		||||
//     memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
//     start_smi(&vc_mem);
 | 
			
		||||
//     usleep(CHASE_MSEC * 1000);
 | 
			
		||||
//     sleep(1);
 | 
			
		||||
//   }
 | 
			
		||||
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
        swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
#endif
 | 
			
		||||
//   artnet_init();
 | 
			
		||||
 | 
			
		||||
        memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
        // enable_dma(DMA_CHAN);
 | 
			
		||||
        start_smi(&vc_mem);
 | 
			
		||||
        // usleep(10);
 | 
			
		||||
        // while (dma_active(DMA_CHAN))
 | 
			
		||||
        //     usleep(10);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  terminate(0);
 | 
			
		||||
}
 | 
			
		||||
//   // loops
 | 
			
		||||
//   if (testmode) {
 | 
			
		||||
//     while (1) {
 | 
			
		||||
//       test_leds();
 | 
			
		||||
//       sleep(3);
 | 
			
		||||
//     }
 | 
			
		||||
//   } else {
 | 
			
		||||
//     while (1) {
 | 
			
		||||
//       artDmx_t *artDmx;
 | 
			
		||||
//       if (artnet_read(&artDmx) == OpDmx) {
 | 
			
		||||
//         uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
 | 
			
		||||
//         unsigned int ledCountInFrame = dmxLength / 3;
 | 
			
		||||
//         uint16_t maxBound = ledCountInFrame < chan_ledcount ? ledCountInFrame :
 | 
			
		||||
//         chan_ledcount; unsigned int universe = artDmx->subUni & (LED_NCHANS - 1); for (size_t
 | 
			
		||||
//         i = 0; i < maxBound; ++i) {
 | 
			
		||||
//           uint8_t *rgb = artDmx->data + (i * 3);
 | 
			
		||||
//           rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
 | 
			
		||||
//           rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
//         }
 | 
			
		||||
 | 
			
		||||
// #if LED_NCHANS <= 8
 | 
			
		||||
//         swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
// #endif
 | 
			
		||||
 | 
			
		||||
//         memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
//         // enable_dma(DMA_CHAN);
 | 
			
		||||
//         start_smi(&vc_mem);
 | 
			
		||||
//         // usleep(10);
 | 
			
		||||
//         // while (dma_active(DMA_CHAN))
 | 
			
		||||
//         //     usleep(10);
 | 
			
		||||
//       }
 | 
			
		||||
//     }
 | 
			
		||||
//   }
 | 
			
		||||
//   terminate(0);
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// Convert RGB text string into integer data, for given channel
 | 
			
		||||
// Return number of data points for this channel
 | 
			
		||||
@@ -227,6 +178,28 @@ int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan) {
 | 
			
		||||
  }
 | 
			
		||||
  return (i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
 | 
			
		||||
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
 | 
			
		||||
void set_color(uint32_t rgb, TXDATA_T *txd) {
 | 
			
		||||
  int msk;
 | 
			
		||||
 | 
			
		||||
  // For each bit of the 24-bit RGB values..
 | 
			
		||||
  for (size_t n = 0; n < LED_NBITS; n++) {
 | 
			
		||||
    // Mask to convert RGB to GRB, M.S bit first
 | 
			
		||||
    msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
 | 
			
		||||
    // 1st byte or word is a high pulse on all lines
 | 
			
		||||
    txd[0] = (TXDATA_T)0xffff;
 | 
			
		||||
    // 2nd has high or low bits from data
 | 
			
		||||
    // 3rd is a low pulse
 | 
			
		||||
    txd[1] = txd[2] = 0;
 | 
			
		||||
    if (rgb & msk) {
 | 
			
		||||
      txd[1] = (TXDATA_T)0xffff;
 | 
			
		||||
    }
 | 
			
		||||
    txd += BIT_NPULSES;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
 | 
			
		||||
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
 | 
			
		||||
void rgb_txdata(int *rgbs, TXDATA_T *txd) {
 | 
			
		||||
@@ -235,7 +208,7 @@ void rgb_txdata(int *rgbs, TXDATA_T *txd) {
 | 
			
		||||
  // For each bit of the 24-bit RGB values..
 | 
			
		||||
  for (n = 0; n < LED_NBITS; n++) {
 | 
			
		||||
    // Mask to convert RGB to GRB, M.S bit first
 | 
			
		||||
    msk = n == 0 ? 0x8000 : n == 8 ? 0x800000 : n == 16 ? 0x80 : msk >> 1;
 | 
			
		||||
    msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
 | 
			
		||||
    // 1st byte or word is a high pulse on all lines
 | 
			
		||||
    txd[0] = (TXDATA_T)0xffff;
 | 
			
		||||
    // 2nd has high or low bits from data
 | 
			
		||||
@@ -284,16 +257,7 @@ void fail(char *s) {
 | 
			
		||||
// Free memory segments and exit
 | 
			
		||||
void terminate(int sig) {
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  printf("Closing\n");
 | 
			
		||||
  for (size_t i = 0; i < chan_ledcount; ++i) {
 | 
			
		||||
    rgb_txdata(off_rgbs, &tx_buffer[LED_TX_OSET(i)]);
 | 
			
		||||
  }
 | 
			
		||||
  swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
  memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
  start_smi(&vc_mem);
 | 
			
		||||
  sleep(1);
 | 
			
		||||
 | 
			
		||||
  if (gpio_regs.virt) {
 | 
			
		||||
    for (i = 0; i < LED_NCHANS; i++)
 | 
			
		||||
      gpio_mode(LED_D0_PIN + i, GPIO_IN);
 | 
			
		||||
@@ -352,10 +316,10 @@ void init_smi(int width, int ns, int setup, int strobe, int hold) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set up SMI transfers using DMA
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp) {
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp, TXDATA_T **txdata) {
 | 
			
		||||
  DMA_CB *cbs = mp->virt;
 | 
			
		||||
 | 
			
		||||
  txdata = (TXDATA_T *)(cbs + 1);
 | 
			
		||||
  *txdata = (TXDATA_T *)(cbs + 1);
 | 
			
		||||
  smi_dmc->dmaen = 1;
 | 
			
		||||
  smi_cs->enable = 1;
 | 
			
		||||
  smi_cs->clear = 1;
 | 
			
		||||
@@ -365,7 +329,7 @@ void setup_smi_dma(MEM_MAP *mp, int nsamp) {
 | 
			
		||||
  enable_dma(DMA_CHAN);
 | 
			
		||||
  cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP;
 | 
			
		||||
  cbs[0].tfr_len = nsamp;
 | 
			
		||||
  cbs[0].srce_ad = MEM_BUS_ADDR(mp, txdata);
 | 
			
		||||
  cbs[0].srce_ad = MEM_BUS_ADDR(mp, *txdata);
 | 
			
		||||
  cbs[0].dest_ad = REG_BUS_ADDR(smi_regs, SMI_D);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								RpiLedBars/src/rpi_pixleds.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								RpiLedBars/src/rpi_pixleds.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
#if !defined(__RPI_PIXLEDS_H__)
 | 
			
		||||
#define __RPI_PIXLEDS_H__
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "rpi_dma_utils.h"
 | 
			
		||||
 | 
			
		||||
#if PHYS_REG_BASE == PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz)
 | 
			
		||||
#define SMI_TIMING 10, 15, 30, 15  // 400 ns cycle time
 | 
			
		||||
#else                              // Timings for RPi v0-3 (1 GHz)
 | 
			
		||||
#define SMI_TIMING 10, 10, 20, 10  // 400 ns cycle time
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TX_TEST 0           // If non-zero, use dummy Tx data
 | 
			
		||||
#define LED_D0_PIN 8        // GPIO pin for D0 output
 | 
			
		||||
#define LED_NCHANS 8        // Number of LED channels (8 or 16)
 | 
			
		||||
#define LED_NBITS 24        // Number of data bits per LED
 | 
			
		||||
#define LED_PREBITS 4       // Number of zero bits before LED data
 | 
			
		||||
#define LED_POSTBITS 4      // Number of zero bits after LED data
 | 
			
		||||
#define BIT_NPULSES 3       // Number of O/P pulses per LED bit
 | 
			
		||||
#define CHAN_MAXLEDS 6 * 60 // Maximum number of LEDs per channel
 | 
			
		||||
#define CHASE_MSEC 20       // Delay time for chaser light test
 | 
			
		||||
#define REQUEST_THRESH 2    // DMA request threshold
 | 
			
		||||
#define DMA_CHAN 10         // DMA channel to use
 | 
			
		||||
 | 
			
		||||
// Length of data for 1 row (1 LED on each channel)
 | 
			
		||||
#define LED_DLEN (LED_NBITS * BIT_NPULSES)
 | 
			
		||||
 | 
			
		||||
// Transmit data type, 8 or 16 bits
 | 
			
		||||
#if LED_NCHANS > 8
 | 
			
		||||
#define TXDATA_T uint16_t
 | 
			
		||||
#else
 | 
			
		||||
#define TXDATA_T uint8_t
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Ofset into Tx data buffer, given LED number in chan
 | 
			
		||||
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
 | 
			
		||||
 | 
			
		||||
// Size of data buffers & NV memory, given number of LEDs per chan
 | 
			
		||||
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
 | 
			
		||||
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
 | 
			
		||||
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
 | 
			
		||||
 | 
			
		||||
#if TX_TEST
 | 
			
		||||
// Data for simple transmission test
 | 
			
		||||
TXDATA_T tx_test_data[] = {1, 2, 3, 4, 5, 6, 7, 0};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void test_leds();
 | 
			
		||||
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
 | 
			
		||||
void set_color(uint32_t rgb, TXDATA_T *txd);
 | 
			
		||||
void rgb_txdata(int *rgbs, TXDATA_T *txd);
 | 
			
		||||
void swap_bytes(void *data, int len);
 | 
			
		||||
int hexdig(char c);
 | 
			
		||||
void map_devices(void);
 | 
			
		||||
void fail(char *s);
 | 
			
		||||
void terminate(int sig);
 | 
			
		||||
void init_smi(int width, int ns, int setup, int hold, int strobe);
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp, TXDATA_T **txdata);
 | 
			
		||||
void start_smi(MEM_MAP *mp);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_PIXLEDS_H__
 | 
			
		||||
							
								
								
									
										32
									
								
								RpiLedBars/src/rpi_selector.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								RpiLedBars/src/rpi_selector.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <wiringPi.h>
 | 
			
		||||
 | 
			
		||||
unsigned const selectorPinNumber = 4;
 | 
			
		||||
int selectorPins[4] = {5, 6, 26, 27};
 | 
			
		||||
 | 
			
		||||
void setup_selector() {
 | 
			
		||||
  wiringPiSetupGpio();
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < selectorPinNumber; ++i) {
 | 
			
		||||
    pinMode(selectorPins[i], INPUT);
 | 
			
		||||
    pullUpDnControl(selectorPins[i], PUD_DOWN);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int get_selector_position() {
 | 
			
		||||
  for (size_t i = 0; i < selectorPinNumber; ++i) {
 | 
			
		||||
    if (digitalRead(selectorPins[i])) {
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void print_selector() {
 | 
			
		||||
  char modeStr[] = "0 | 0 | 0 | 0 \n";
 | 
			
		||||
  for (size_t i = 0; i < selectorPinNumber; ++i) {
 | 
			
		||||
    modeStr[i * 4] = digitalRead(selectorPins[i]) ? '1' : '0';
 | 
			
		||||
  }
 | 
			
		||||
  printf(modeStr);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								RpiLedBars/src/rpi_selector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								RpiLedBars/src/rpi_selector.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#if !defined(__RPI_SELECTOR_H__)
 | 
			
		||||
#define __RPI_SELECTOR_H__
 | 
			
		||||
 | 
			
		||||
void setup_selector();
 | 
			
		||||
 | 
			
		||||
int get_selector_position();
 | 
			
		||||
 | 
			
		||||
void print_selector();
 | 
			
		||||
 | 
			
		||||
#endif /* __RPI_SELECTOR_H__ */
 | 
			
		||||
							
								
								
									
										37
									
								
								RpiLedBars/src/smi/VitualMemory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								RpiLedBars/src/smi/VitualMemory.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
#include "VitualMemory.hpp"
 | 
			
		||||
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
// #include <sys/ioctl.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
VitualMemory::VitualMemory(void *addr, int size) {
 | 
			
		||||
  int fd;
 | 
			
		||||
 | 
			
		||||
  roundSize = PAGE_ROUNDUP(size);
 | 
			
		||||
 | 
			
		||||
  if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) {
 | 
			
		||||
    throw std::runtime_error("Error: can't open /dev/mem, run using sudo");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mem = mmap(0, roundSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr);
 | 
			
		||||
  close(fd);
 | 
			
		||||
 | 
			
		||||
#if _DEBUG
 | 
			
		||||
  printf("Map %p -> %p\n", (void *)addr, mem);
 | 
			
		||||
#endif // _DEBUG
 | 
			
		||||
 | 
			
		||||
  if (mem == MAP_FAILED) {
 | 
			
		||||
    throw std::runtime_error("Error: can't map memory");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VitualMemory::~VitualMemory() {
 | 
			
		||||
  if (mem) {
 | 
			
		||||
    munmap(mem, roundSize);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								RpiLedBars/src/smi/VitualMemory.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								RpiLedBars/src/smi/VitualMemory.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#if !defined(__VIRTUAL_MEMORY_H__)
 | 
			
		||||
#define __VIRTUAL_MEMORY_H__
 | 
			
		||||
 | 
			
		||||
class VitualMemory {
 | 
			
		||||
public:
 | 
			
		||||
  VitualMemory(void *addr, int size);
 | 
			
		||||
  ~VitualMemory();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  int roundSize;
 | 
			
		||||
  void *mem;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // __VIRTUAL_MEMORY_H__
 | 
			
		||||
							
								
								
									
										4
									
								
								RpiLedBars/src/smi/utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								RpiLedBars/src/smi/utils.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
// Size of memory page
 | 
			
		||||
#define PAGE_SIZE 0x1000
 | 
			
		||||
// Round up to nearest page
 | 
			
		||||
#define PAGE_ROUNDUP(n) ((n) % PAGE_SIZE == 0 ? (n) : ((n) + PAGE_SIZE) & ~(PAGE_SIZE - 1))
 | 
			
		||||
							
								
								
									
										8
									
								
								RpiLedBars/src/tmp/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								RpiLedBars/src/tmp/main.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int main(int argc, char const *argv[])
 | 
			
		||||
{
 | 
			
		||||
  
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								RpiLedBars/src/tmp/rpi_pixleds.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								RpiLedBars/src/tmp/rpi_pixleds.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#if !defined(__RPI_PIXLEDS_H__)
 | 
			
		||||
#define __RPI_PIXLEDS_H__
 | 
			
		||||
 | 
			
		||||
void rgb_txdata(int *rgbs, TXDATA_T *txd);
 | 
			
		||||
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
 | 
			
		||||
void swap_bytes(void *data, int len);
 | 
			
		||||
int hexdig(char c);
 | 
			
		||||
void map_devices(void);
 | 
			
		||||
void fail(char *s);
 | 
			
		||||
void terminate(int sig);
 | 
			
		||||
void init_smi(int width, int ns, int setup, int hold, int strobe);
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp, TXDATA_T *txdata);
 | 
			
		||||
void start_smi(MEM_MAP *mp);
 | 
			
		||||
 | 
			
		||||
#endif // __RPI_PIXLEDS_H__
 | 
			
		||||
		Reference in New Issue
	
	Block a user