├── .github └── workflows │ └── arduino-checks.yml ├── LICENSE ├── README.md ├── examples ├── DMXSerialFlow │ └── DMXSerialFlow.ino ├── DmxSerialNeoPixels │ ├── DmxSerialNeoPixels.ino │ └── ws2812.h ├── DmxSerialRecv │ └── DmxSerialRecv.ino ├── DmxSerialSend │ └── DmxSerialSend.ino └── DmxSniff │ └── DmxSniff.ino ├── keywords.txt ├── library.properties └── src ├── DMXSerial.cpp ├── DMXSerial.h ├── DMXSerial_avr.h └── DMXSerial_megaavr.h /.github/workflows/arduino-checks.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions for Arduino library projects 2 | 3 | name: Arduino Library Checks 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the develop branch 8 | push: 9 | branches: [develop,master] 10 | pull_request: 11 | branches: [develop,master] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | jobs: 17 | 18 | # This defines a job for checking the Arduino library format specifications 19 | # see 20 | lint: 21 | name: check library format 22 | runs-on: ubuntu-latest 23 | continue-on-error: true 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | 28 | # Arduino - lint 29 | - name: Arduino-lint 30 | uses: arduino/arduino-lint-action@v1 31 | with: 32 | library-manager: update 33 | verbose: false 34 | 35 | # These jobs are used to compile the examples fot the specific processor/board. 36 | # see 37 | compile-uno: 38 | name: compile uno examples 39 | runs-on: ubuntu-latest 40 | continue-on-error: true 41 | 42 | steps: 43 | - uses: actions/checkout@v2 44 | 45 | # Compile Examples for UNO 46 | - name: Compile examples on uno 47 | uses: arduino/compile-sketches@v1 48 | with: 49 | verbose: true 50 | fqbn: arduino:avr:uno 51 | sketch-paths: | 52 | - 'examples/DMXSerialFlow' 53 | - 'examples/DmxSerialRecv' 54 | - 'examples/DmxSerialSend' 55 | 56 | compile-leonardo: 57 | needs: compile-uno 58 | name: compile leonardo examples 59 | runs-on: ubuntu-latest 60 | continue-on-error: true 61 | 62 | steps: 63 | - uses: actions/checkout@v2 64 | 65 | - name: Compile for leonardo 66 | uses: arduino/compile-sketches@v1 67 | with: 68 | verbose: true 69 | fqbn: arduino:avr:leonardo 70 | sketch-paths: | 71 | - 'examples/DMXSerialFlow' 72 | - 'examples/DmxSerialNeoPixels' 73 | - 'examples/DmxSerialRecv' 74 | - 'examples/DmxSerialSend' 75 | - 'examples/DmxSniff' 76 | 77 | compile-mega: 78 | needs: compile-uno 79 | name: compile examples for mega 80 | runs-on: ubuntu-latest 81 | continue-on-error: true 82 | 83 | steps: 84 | - uses: actions/checkout@v2 85 | 86 | - name: Compile for mega 87 | uses: arduino/compile-sketches@v1 88 | with: 89 | verbose: true 90 | fqbn: arduino:avr:mega 91 | sketch-paths: | 92 | - 'examples/DMXSerialFlow' 93 | - 'examples/DmxSerialNeoPixels' 94 | - 'examples/DmxSerialRecv' 95 | - 'examples/DmxSerialSend' 96 | 97 | compile-atmega4809: 98 | needs: compile-uno 99 | name: compile examples for atmega4809 100 | runs-on: ubuntu-latest 101 | continue-on-error: true 102 | 103 | steps: 104 | - uses: actions/checkout@v2 105 | 106 | - name: Compile for atmega4809 107 | uses: arduino/compile-sketches@v1 108 | with: 109 | verbose: true 110 | fqbn: arduino:megaavr:nona4809:mode=off 111 | sketch-paths: | 112 | - 'examples/DMXSerialFlow' 113 | - 'examples/DmxSerialRecv' 114 | - 'examples/DmxSerialSend' 115 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2005-2020, Matthias Hertel, http://www.mathertel.de/ 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | 3. Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DMXSerial 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/DMXSerial.svg?)](https://www.ardu-badge.com/DMXSerial) 4 | [![Arduino Library Checks](https://github.com/mathertel/DMXSerial/actions/workflows/arduino-checks.yml/badge.svg)](https://github.com/mathertel/DMXSerial/actions/workflows/arduino-checks.yml) 5 | 6 | 7 | This is a library for sending and receiving DMX codes using the Arduino platform. 8 | You can implement DMX devices and DMX controllers with this library. 9 | 10 | The DMX communication implemented by the DMXSerial library relies completely on the hardware support of a builtin USART / Serial port interface 11 | by using interrupts that handle all I/O transfer in the background. 12 | 13 | There is a full 512 byte buffer allocated to support all possible DMX values of a line at the same time. 14 | 15 | Since Version 1.5.0 the original ATmega168 based implementation was refactored and enhanced 16 | to support also other processor architectures like the ATMEGA4809. 17 | 18 | 19 | ## Supported Boards and processors 20 | 21 | The supported processors and Arduino Boards are: 22 | * Arduino UNO, Ethernet, Fio, Nano and Arduino Pro, Arduino Pro Mini (ATmega328 or ATmega168) 23 | * Arduino Mega2560 (ATmega2560) 24 | * Arduino Leonardo (Atmega32U4) 25 | * Arduino nano Every (ATMEGA4809) 26 | * ATmega8 boards - experimental 27 | 28 | Other compatible boards my work as well. 29 | 30 | You can find more detail on this library at http://www.mathertel.de/Arduino/DMXSerial.aspx. 31 | 32 | 33 | ## Compile for other Serial ports 34 | 35 | The original library was written for Arduino 2009 and Arduino UNO boards that use the ATmega328 or ATmega168 chip. 36 | These chips only have a single Universal Synchronous and Asynchronous serial Receiver and Transmitter (USART) a.k.a. the Serial port in the Arduino environment. 37 | The DMXSerial library uses this port for sending or receiving DMX signals by default. 38 | 39 | This conflicts a little bit with the usage of the programming mode the Arduino offers through then USB ports or serial interfaces but it is possible to build compatible DMX shields that don’t interfere with this usage if done right. The DMXShield is an example of this. 40 | 41 | By using the Arduino Leonardo, MEGA or Every board you can write debugging information to the Serial port in your sketch for debugging or data exchange purpose as the processor supports multiple ports. 42 | So maybe a DMX diagnostic sketch and debugging output in your code can profit from that. 43 | 44 | 45 | **Arduino Leonardo, Arduino Esplora** 46 | 47 | When compiling for a Arduino Leonardo board the DMXSerial library will choose the **Serial1** port by default for DMX communication. 48 | 49 | The Arduino Leonardo board uses a chip that has a USB 2.0 port available in addition to the USART. Therefore the “Serial” port is routed through the USB port that behaves like a serial port device and not the built-in USART. If you like to address the real Serial port you have to go with the Serial1. 50 | 51 | When you look at the hardware and the registers definitions and manual for the processor the USART0 still exists but the definitions for addressing the registers have changed 52 | (for example USART_RX_vect is now USART0_RX_vect). Therefore some adjustments had to be done. 53 | 54 | 55 | **Arduino MEGA 2560** 56 | 57 | When using the chip on the Arduino MEGA 2560 board you have more than one USART available and maybe you wish to use port 1 instead of port 0. 58 | This can be done by enabling the definitions for port1 in the library in file `src\DMXSerial_avr.h` just uncomment the line 59 | 60 | ```CPP 61 | #define DMX_USE_PORT1 62 | ``` 63 | 64 | **MEGAAVR processors like 4809 in Arduino nano Every** 65 | 66 | For this processor the USART1 is used for DMX. 67 | 68 | 69 | **ATmega8 boards** 70 | 71 | I added the definitions of boards based on the ATmega8 too for experimental purpose. If you find problems with these boards, leave me a note please. 72 | 73 | 74 | 75 | ## Supported Shields 76 | 77 | A suitable hardware is the Arduino platform plus a shield for the DMX physical protocol implementation. 78 | You can find such a shield at: http://www.mathertel.de/Arduino/DMXShield.aspx. 79 | 80 | Without or some modification this library can also used with other DMX shields 81 | that use the Serial port for connecting to the DMX bus. 82 | 83 | 84 | ## License 85 | 86 | Copyright (c) 2005-2020 by Matthias Hertel, http://www.mathertel.de/ 87 | 88 | The detailed Software License Agreement can be found at: http://www.mathertel.de/License.aspx 89 | 90 | -------------------------------------------------------------------------------- /examples/DMXSerialFlow/DMXSerialFlow.ino: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DmxSerial - A hardware supported interface to DMX. 3 | // DMXSerialFlow.ino: Sample DMX application for sending 60 DMX values. 4 | // Copyright (c) 2014-2015 by Matthias Hertel, http://www.mathertel.de 5 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 6 | // 7 | // Documentation and samples are available at http://www.mathertel.de/Arduino 8 | // 25.07.2011 creation of the DmxSerial library. 9 | // 01.07.2013 published version of the example applikation 10 | // 11 | // This is an example of how to send a more complex RGB based pattern of colors 12 | // over a DMX line. 13 | // The values for one of the channels can be watched at the PWM outputs of the Arduino Board. 14 | // - - - - - 15 | 16 | #include 17 | 18 | // Constants for demo program 19 | 20 | const int RedPin = 9; // PWM output pin for Red Light. 21 | const int GreenPin = 6; // PWM output pin for Green Light. 22 | const int BluePin = 5; // PWM output pin for Blue Light. 23 | 24 | void setup(void) 25 | { 26 | // Serial.begin(57600); // only on Leonardo 27 | // Serial.println("DMXSerialFlow DMX Example"); 28 | 29 | DMXSerial.init(DMXController); 30 | 31 | // Set the number of channels the controller will send 32 | // this call is not needed, because the DMXController extends the DMX packet length automatically when data is added. 33 | // DMXSerial.maxChannel(60); 34 | 35 | pinMode(RedPin, OUTPUT); // sets the digital pin as output 36 | pinMode(GreenPin, OUTPUT); 37 | pinMode(BluePin, OUTPUT); 38 | 39 | analogWrite(RedPin, 80); 40 | analogWrite(GreenPin, 80); 41 | analogWrite(BluePin, 80); 42 | } // setup 43 | 44 | 45 | // set 3 channels to a RGB value with the specified hue (0...764) 46 | void setChannelRGB(int channel, int hue) { 47 | hue = hue % 765; 48 | if (hue < 256) { 49 | /// blue to red 50 | DMXSerial.write(channel , hue); 51 | DMXSerial.write(channel+1, 0); 52 | DMXSerial.write(channel+2, 255-hue); 53 | 54 | } else if (hue < 511) { 55 | /// red to green 56 | DMXSerial.write(channel , 255 - (hue-255)); 57 | DMXSerial.write(channel+1, hue-255); 58 | DMXSerial.write(channel+2, 0); 59 | 60 | } else { 61 | /// green to blue 62 | DMXSerial.write(channel , 0); 63 | DMXSerial.write(channel+1, 255 - (hue-510)); 64 | DMXSerial.write(channel+2, hue-510); 65 | } // if 66 | } // setChannelRGB() 67 | 68 | 69 | void loop(void) 70 | { 71 | unsigned long now = millis(); 72 | int testchannel; 73 | 74 | // create some DMX test values: 5 RGB channels 75 | // adjust the "12" for changing the speed 76 | int alpha = (now / 12) % 765; 77 | 78 | // uncomment this line to have a scenario where DMX values are changed ~ 2 times the second 79 | // alpha = (alpha / 64) * 64; 80 | // uncomment this line to have a scenario where DMX values are changed every 5 seconds 81 | // alpha &= 0xFF00; 82 | 83 | // setup for 60 devices with RGB (3) channels; len = 180 84 | testchannel = 1; 85 | for (int n = 0; n < 60; n++) { 86 | setChannelRGB (n*3+1, alpha + n*64); 87 | } // for 88 | 89 | // setup for 30 devices with aRGBW--- (8) channels; len = 480 90 | // testchannel = 2; 91 | // for (int n = 0; n < 60; n++) { 92 | // DMXSerial.write(n*8+1, 250); 93 | // setChannelRGB (n*8+2, alpha + n*64); 94 | // DMXSerial.write(n*8+5, 0); 95 | // DMXSerial.write(n*8+6, 0); 96 | // DMXSerial.write(n*8+7, 0); 97 | // DMXSerial.write(n*8+8, 0); 98 | // } // for 99 | 100 | // send the DMX values fo the testchannel to the PWM pins 101 | analogWrite(RedPin, DMXSerial.read(testchannel+0)); 102 | analogWrite(GreenPin, DMXSerial.read(testchannel+1)); 103 | analogWrite(BluePin, DMXSerial.read(testchannel+2)); 104 | } 105 | 106 | // End 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /examples/DmxSerialNeoPixels/DmxSerialNeoPixels.ino: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DmxSerialNeoPixels.ino: Sample DMX application for retrieving 3 DMX values: 3 | // 4 | // Copyright (c) 2016 by Matthias Hertel, http://www.mathertel.de 5 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 6 | // 7 | // Documentation and samples are available at http://www.mathertel.de/Arduino 8 | // 06.09.2016 Creation of DmxSerialNeoPixels sample. 9 | // 27.08.2017 working with new DMXSerial DMXProbe mode. 10 | // cleanup. 11 | 12 | // - - - - - 13 | 14 | #include 15 | #include 16 | 17 | #include "ws2812.h" 18 | 19 | // Constants for demo program 20 | 21 | const int RedPin = 9; // PWM output pin for Red Light. 22 | const int GreenPin = 6; // PWM output pin for Green Light. 23 | const int BluePin = 5; // PWM output pin for Blue Light. 24 | 25 | #define RedDefaultLevel 5 // 100 26 | #define GreenDefaultLevel 0 // 200 27 | #define BlueDefaultLevel 0 // 255 28 | 29 | // number of RGB neopixels, RGB channels are transfered 30 | // warning: try with 12 first and scale up carefully. 31 | #define PIXELS 60 32 | 33 | // first DMX start address 34 | #define DMXSTART 1 35 | 36 | // number of DMX channels used 37 | #define DMXLENGTH (PIXELS*3) 38 | 39 | // Initialize DMXSerial and neo pixel output 40 | void setup () { 41 | int n; 42 | DMXSerial.init(DMXProbe); 43 | 44 | // enable pwm outputs 45 | pinMode(RedPin, OUTPUT); // sets the digital pin as output 46 | pinMode(GreenPin, OUTPUT); 47 | pinMode(BluePin, OUTPUT); 48 | 49 | DMXSerial.maxChannel(DMXLENGTH); // after 3 * pixel channels, the onUpdate will be called when new data arrived. 50 | 51 | // setup the neopixel output 52 | setupNeopixel(); 53 | 54 | // give them a decent color... 55 | n = 1; 56 | for (int p = 0; p < PIXELS; p++) { 57 | DMXSerial.write(n++, 5); 58 | DMXSerial.write(n++, 10); 59 | DMXSerial.write(n++, 20); 60 | } 61 | updateNeopixel(DMXSerial.getBuffer() + DMXSTART, PIXELS); 62 | 63 | } // setup () 64 | 65 | 66 | // do constantly fetch DMX data and update the neopixels. 67 | void loop() { 68 | // wait for an incomming DMX packet. 69 | if (DMXSerial.receive()) { 70 | analogWrite(RedPin, DMXSerial.read(1)); 71 | analogWrite(GreenPin, DMXSerial.read(2)); 72 | analogWrite(BluePin, DMXSerial.read(3)); 73 | updateNeopixel(DMXSerial.getBuffer() + DMXSTART, PIXELS); 74 | 75 | } else { 76 | // don't update the Neopixels but signal a red. 77 | analogWrite(RedPin, 100); 78 | analogWrite(GreenPin, 0); 79 | analogWrite(BluePin, 0); 80 | } // if 81 | 82 | } // loop() 83 | 84 | 85 | // The End. 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /examples/DmxSerialNeoPixels/ws2812.h: -------------------------------------------------------------------------------- 1 | // neopixel.h 2 | 3 | 4 | /* 5 | The Neopixel driving routines are taken from the article and sketch from bigjosh 6 | http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ 7 | where the interrupt cli() and sei() are included in the sendBit function. 8 | At the sources from his github this is not the case but it's important for the usage with DMXSerial library. 9 | (see https://github.com/bigjosh/SimpleNeoPixelDemo ) 10 | 11 | These routines fit very good to the DMXSerial implementation because they switch on and off the 12 | Interrupt 13 | 14 | On DMX usual channels are used in the red then green then blue order. 15 | Neopixel wants colors in green then red then blue order so the 2 channels are switched. 16 | */ 17 | 18 | // ----- global defines from josh: ----- 19 | 20 | // These values are for the pin that connects to the Data Input pin on the LED strip. They correspond to... 21 | 22 | #define PIXEL_PORT PORTB // Port of the pin the pixels are connected to 23 | #define PIXEL_DDR DDRB // Port of the pin the pixels are connected to 24 | #define PIXEL_BIT 4 // Bit of the pin the pixels are connected to 25 | 26 | // This re3sults in the following Arduino Pins: 27 | // Arduino Yun: Digital Pin 8 28 | // DueMilinove/UNO: Digital Pin 12 29 | // Arduino Mega PWM Pin 4 30 | 31 | // You'll need to look up the port/bit combination for other boards. 32 | // Note that you could also include the DigitalWriteFast header file to not need to to this lookup. 33 | 34 | // These are the timing constraints taken mostly from the WS2812 datasheets 35 | // These are chosen to be conservative and avoid problems rather than for maximum throughput 36 | 37 | #define T1H 900 // Width of a 1 bit in ns 38 | #define T1L 600 // Width of a 1 bit in ns 39 | 40 | #define T0H 400 // Width of a 0 bit in ns 41 | #define T0L 900 // Width of a 0 bit in ns 42 | 43 | #define RES 6000 // Width of the low gap between bits to cause a frame to latch 44 | 45 | // Here are some convience defines for using nanoseconds specs to generate actual CPU delays 46 | 47 | #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 48 | 49 | #define CYCLES_PER_SEC (F_CPU) 50 | 51 | #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) 52 | 53 | #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) 54 | 55 | #define DELAY_CYCLES(n) ( ((n)>0) ? __builtin_avr_delay_cycles( n ) : __builtin_avr_delay_cycles( 0 ) ) // Make sure we never have a delay less than zero 56 | 57 | // Low level function with mixed in assembler code. 58 | 59 | // Actually send a bit to the string. We turn off optimizations to make sure the compile does 60 | // not reorder things and make it so the delay happens in the wrong place. 61 | inline void sendBit( bool bitVal ) 62 | { 63 | if (bitVal) { // 0 bit 64 | asm volatile ( 65 | "sbi %[port], %[bit] \n\t" // Set the output bit 66 | ".rept %[onCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 67 | "nop \n\t" 68 | ".endr \n\t" 69 | "cbi %[port], %[bit] \n\t" // Clear the output bit 70 | ".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 71 | "nop \n\t" 72 | ".endr \n\t" 73 | :: 74 | [port] "I" (_SFR_IO_ADDR(PIXEL_PORT)), 75 | [bit] "I" (PIXEL_BIT), 76 | [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), // 1-bit width less overhead for the actual bit setting, note that this delay could be longer and everything would still work 77 | [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) // Minimum interbit delay. Note that we probably don't need this at all since the loop overhead will be enough, but here for correctness 78 | ); 79 | 80 | } else { // 1 bit 81 | // ************************************************************************** 82 | // This line is really the only tight goldilocks timing in the whole program! 83 | // ************************************************************************** 84 | asm volatile ( 85 | "sbi %[port], %[bit] \n\t" // Set the output bit 86 | ".rept %[onCycles] \n\t" // Now timing actually matters. The 0-bit must be long enough to be detected but not too long or it will be a 1-bit 87 | "nop \n\t" // Execute NOPs to delay exactly the specified number of cycles 88 | ".endr \n\t" 89 | "cbi %[port], %[bit] \n\t" // Clear the output bit 90 | ".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 91 | "nop \n\t" 92 | ".endr \n\t" 93 | :: 94 | [port] "I" (_SFR_IO_ADDR(PIXEL_PORT)), 95 | [bit] "I" (PIXEL_BIT), 96 | [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), 97 | [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) 98 | ); 99 | } // if 100 | 101 | // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the 5us reset timeout (which is A long time) 102 | // Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time. 103 | // This has thenice side effect of avoid glitches on very long strings becuase 104 | } // sendBit() 105 | 106 | // Neopixel wants bit in highest-to-lowest order 107 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 108 | inline void sendByte(uint8_t byte) 109 | { 110 | for (uint8_t bit = 0; bit < 8; bit++) { 111 | sendBit(byte & 0x80); 112 | byte <<= 113 | 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 114 | } // for 115 | } // sendByte() 116 | 117 | /* 118 | The following three functions are the public API: 119 | ledSetup() - set up the pin that is connected to the string. Call once at the begining of the program. 120 | sendPixel( r g , b ) - send a single pixel to the string. Call this once for each pixel in a frame. 121 | show() - show the recently sent pixel on the LEDs . Call once per frame. 122 | */ 123 | 124 | // Set the specified pin up as digital out 125 | 126 | void sendPixel(uint8_t r, uint8_t g, uint8_t b) { 127 | sendByte(g); // Neopixel wants colors in green then red then blue order 128 | sendByte(r); 129 | sendByte(b); 130 | } // sendPixel 131 | 132 | 133 | // ----- defines and routines from josh - End ----- 134 | 135 | void setupNeopixel() { 136 | bitSet( PIXEL_DDR , PIXEL_BIT ); 137 | } // setupNeopixel() 138 | 139 | 140 | // read data from the DMX buffer (RGB) and send it to the neopixels... 141 | void updateNeopixel(uint8_t *ptr, uint8_t pixels) { 142 | uint8_t r, g, b; 143 | 144 | // no interrupt is welcome. 145 | cli(); 146 | 147 | for (int p = 0; p < pixels; p++ ) { 148 | r = *ptr++; 149 | g = *ptr++; 150 | b = *ptr++; 151 | // send to Neopixels 152 | // sendPixel(r, g , b); 153 | sendPixel(r >> 2, g >> 2, b >> 2); 154 | } // for 155 | 156 | // interrupt may come. 157 | sei(); 158 | 159 | // Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame 160 | _delay_us((RES / 1000UL) + 1); 161 | } // updateNeopixel() 162 | 163 | // End 164 | -------------------------------------------------------------------------------- /examples/DmxSerialRecv/DmxSerialRecv.ino: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DmxSerial - A hardware supported interface to DMX. 3 | // DmxSerialRecv.ino: Sample DMX application for retrieving 3 DMX values: 4 | // address 1 (red) -> PWM Port 9 5 | // address 2 (green) -> PWM Port 6 6 | // address 3 (blue) -> PWM Port 5 7 | // 8 | // Copyright (c) 2011-2015 by Matthias Hertel, http://www.mathertel.de 9 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 10 | // 11 | // Documentation and samples are available at http://www.mathertel.de/Arduino 12 | // 25.07.2011 creation of the DmxSerial library. 13 | // 10.09.2011 fully control the serial hardware register 14 | // without using the Arduino Serial (HardwareSerial) class to avoid ISR implementation conflicts. 15 | // 01.12.2011 include file and extension changed to work with the Arduino 1.0 environment 16 | // 28.12.2011 changed to channels 1..3 (RGB) for compatibility with the DmxSerialSend sample. 17 | // 10.05.2012 added some lines to loop to show how to fall back to a default color when no data was received since some time. 18 | // - - - - - 19 | 20 | #include 21 | 22 | // Constants for demo program 23 | 24 | const int RedPin = 9; // PWM output pin for Red Light. 25 | const int GreenPin = 6; // PWM output pin for Green Light. 26 | const int BluePin = 5; // PWM output pin for Blue Light. 27 | 28 | // This Example receives the 3 values starting with this channel: 29 | const int startChannel = 0 * 3 + 1; 30 | 31 | #define RedDefaultLevel 100 32 | #define GreenDefaultLevel 200 33 | #define BlueDefaultLevel 255 34 | 35 | void setup() { 36 | DMXSerial.init(DMXReceiver); 37 | 38 | // set some default values 39 | DMXSerial.write(1, 80); 40 | DMXSerial.write(2, 0); 41 | DMXSerial.write(3, 0); 42 | 43 | // enable pwm outputs 44 | pinMode(RedPin, OUTPUT); // sets the digital pin as output 45 | pinMode(GreenPin, OUTPUT); 46 | pinMode(BluePin, OUTPUT); 47 | } 48 | 49 | 50 | void loop() { 51 | // Calculate how long no data bucket was received 52 | unsigned long lastPacket = DMXSerial.noDataSince(); 53 | 54 | if (lastPacket < 5000) { 55 | // read recent DMX values and set pwm levels 56 | analogWrite(RedPin, DMXSerial.read(startChannel)); 57 | analogWrite(GreenPin, DMXSerial.read(startChannel + 1)); 58 | analogWrite(BluePin, DMXSerial.read(startChannel + 2)); 59 | 60 | } else { 61 | // Show pure red color, when no data was received since 5 seconds or more. 62 | analogWrite(RedPin, RedDefaultLevel); 63 | analogWrite(GreenPin, GreenDefaultLevel); 64 | analogWrite(BluePin, BlueDefaultLevel); 65 | } // if 66 | } 67 | 68 | // End. 69 | -------------------------------------------------------------------------------- /examples/DmxSerialSend/DmxSerialSend.ino: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DmxSerial - A hardware supported interface to DMX. 3 | // DmxSerialSend.ino: Sample DMX application for sending 3 DMX values. 4 | // There colors in the 3 lists (RedList, GreenList and BlueList) are placed into the DMX buffer with a slow fade. 5 | // DMXSerial works in the background and constantly sends the actual values over the DMX interface. 6 | // The actual values are also available on the built in PWM ports: 7 | // address 1 (red) -> also available on PWM Port 9 8 | // address 2 (green) -> also available on PWM Port 6 9 | // address 3 (blue) -> also available on PWM Port 5 10 | // 11 | // Copyright (c) 2011-2015 by Matthias Hertel, http://www.mathertel.de 12 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 13 | // 14 | // Documentation and samples are available at http://www.mathertel.de/Arduino 15 | // The repository is on github : https://github.com/mathertel/DMXSerial 16 | // The repository on github is made available in the Arduino Library Manager. 17 | // 18 | // 25.07.2011 creation of the DmxSerial library. 19 | // 10.09.2011 fully control the serial hardware register 20 | // without using the Arduino Serial (HardwareSerial) class to avoid ISR implementation conflicts. 21 | // 01.12.2011 include file and extension changed to work with the Arduino 1.0 environment 22 | // - - - - - 23 | 24 | #include 25 | 26 | // Constants for demo program 27 | 28 | const int RedPin = 9; // PWM output pin for Red Light. 29 | const int GreenPin = 6; // PWM output pin for Green Light. 30 | const int BluePin = 5; // PWM output pin for Blue Light. 31 | 32 | // The color fading pattern 33 | 34 | int RedList[] = {255, 128, 0, 0, 0, 128}; 35 | int GreenList[] = {0, 128, 255, 128, 0, 0}; 36 | int BlueList[] = {0, 0, 0, 128, 255, 128}; 37 | 38 | int RedLevel, GreenLevel, BlueLevel; 39 | 40 | int RedNow = 0; 41 | int GreenNow = 0; 42 | int BlueNow = 0; 43 | 44 | int state = 0; 45 | 46 | void setup() { 47 | DMXSerial.init(DMXController); 48 | 49 | pinMode(RedPin, OUTPUT); // sets the digital pin as output 50 | pinMode(GreenPin, OUTPUT); 51 | pinMode(BluePin, OUTPUT); 52 | } // setup 53 | 54 | 55 | // loop through the rainbow colors 56 | void loop() { 57 | RedLevel = RedList[state]; 58 | GreenLevel = GreenList[state]; 59 | BlueLevel = BlueList[state]; 60 | 61 | if ((RedLevel == RedNow) && (GreenLevel == GreenNow) && (BlueLevel == BlueNow)) { 62 | state += 1; 63 | if (state == 6) 64 | state = 0; 65 | 66 | } else { 67 | if (RedNow < RedLevel) RedNow++; 68 | if (RedNow > RedLevel) RedNow--; 69 | DMXSerial.write(1, RedNow); 70 | analogWrite(RedPin, RedNow); 71 | 72 | if (GreenNow < GreenLevel) GreenNow++; 73 | if (GreenNow > GreenLevel) GreenNow--; 74 | DMXSerial.write(2, GreenNow); 75 | analogWrite(GreenPin, GreenNow); 76 | 77 | if (BlueNow < BlueLevel) BlueNow++; 78 | if (BlueNow > BlueLevel) BlueNow--; 79 | DMXSerial.write(3, BlueNow); 80 | analogWrite(BluePin, BlueNow); 81 | } // if 82 | 83 | delayMicroseconds(2000); // wait a little bit 84 | } // loop 85 | -------------------------------------------------------------------------------- /examples/DmxSniff/DmxSniff.ino: -------------------------------------------------------------------------------- 1 | // DmxSerial - A hardware supported interface to DMX. 2 | // DmxSniff.ino: Sample DMX application for receiving and displaying all DMX channels 3 | // 4 | // Copyright (c) 2017 by Matthijs Kooijman, http://www.stderr.nl 5 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 6 | // 7 | // This example uses DMXSerial to receive DMX data, and then prints this 8 | // data to a different serial port. This only works on boards with 9 | // multiple UARTs (like the Mega2560), or with a virtual USB serial port 10 | // in addition to a normal UART (like the Leonardo or Micro). 11 | // 12 | // This example was tested with an Leonardo Board. 13 | // 14 | // Whenever the data changes, all channels are dumped through serial, 15 | // allowing to see changes pretty much in realtime. On a dumb terminal 16 | // (like the Arduino IDE serial console), the values are a bit hard to 17 | // read when they're still changing. When using a proper terminal (one 18 | // that understands ANSI escape codes), the data is nicely overwritten, 19 | // so it should be readable even while changing. On dumb terminals, 20 | // these escape codes can just be ignored. 21 | // 22 | // By default, all channels are captured and dumped, but the number of 23 | // channels and how to display them can be changed by modifying some 24 | // constants below. 25 | #include 26 | 27 | // The serial port to use to dump data. Should be a different one from 28 | // the UART used by DMXSerial. On the 32u4-based Leonardo and Micro, 29 | // this works as-is, because "Serial" is the virtual USB serial port and 30 | // Serial1 is the UART used by DMXSerial. On the Mega2560, you should 31 | // let DMXSerial use the second UART, by defining DMX_USE_PORT1 in 32 | // DMXSerial.cpp. 33 | 34 | #define SMARTSERIAL 35 | 36 | void setup() 37 | { 38 | Serial.begin(115200); 39 | while(!Serial) /* wait for Serial to be opened */; 40 | 41 | DMXSerial.init(DMXReceiver); 42 | Serial.println("DMX Sniffer..."); 43 | 44 | #if defined(SMARTSERIAL) 45 | // Reset terminal 46 | Serial.print(F("\x1b\x63")); 47 | // Clear screen 48 | Serial.print(F("\x1b[2J")); 49 | #endif 50 | } 51 | 52 | static constexpr const uint16_t channels = 512; 53 | static constexpr const uint16_t channels_per_line = 32; 54 | static constexpr const uint16_t channels_per_group = 8; 55 | 56 | void loop() 57 | { 58 | if (DMXSerial.dataUpdated()) { 59 | DMXSerial.resetUpdated(); 60 | #if defined(SMARTSERIAL) 61 | // For smarter terminals, reposition the cursor at the top left 62 | Serial.print(F("\x1b[1;1H")); 63 | #else 64 | // For the dumb consoles (like the Arduino IDE serial console), add 65 | // some newlines to separate subsequent dumps 66 | Serial.println(); 67 | Serial.println(); 68 | #endif 69 | 70 | for (uint16_t i = 0; i < channels; ++i) { 71 | // Channels are 1-based 72 | uint16_t channel = i + 1; 73 | 74 | if (i % channels_per_line == 0) { 75 | Serial.println(); 76 | // Prefix each line with the DMX channel number, adding spaces to align 77 | if (channel < 100) Serial.write(' '); 78 | if (channel < 10) Serial.write(' '); 79 | Serial.print(channel); 80 | Serial.print(": "); 81 | } else { 82 | // Print one space between channels, and two spaces between each group 83 | if (i % channels_per_group == 0) 84 | Serial.write(' '); 85 | Serial.write(' '); 86 | } 87 | 88 | // Print the actual channel value, padding with a zero if needed 89 | uint8_t value = DMXSerial.read(channel); 90 | if (value < 0x10) Serial.write('0'); 91 | Serial.print(value, HEX); 92 | } 93 | } 94 | } // loop() 95 | 96 | // End. 97 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for DMXSerial 3 | ####################################### 4 | 5 | 6 | ####################################### 7 | # Datatypes (KEYWORD1) 8 | ####################################### 9 | 10 | RDMDATA KEYWORD1 11 | DMXMode KEYWORD1 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | 17 | 18 | init KEYWORD2 19 | maxChannel KEYWORD2 20 | read KEYWORD2 21 | write KEYWORD2 22 | getBuffer KEYWORD2 23 | noDataSince KEYWORD2 24 | attachOnUpdate KEYWORD2 25 | dataUpdated KEYWORD2 26 | resetUpdated KEYWORD2 27 | term KEYWORD2 28 | 29 | ####################################### 30 | # Instances (KEYWORD2) 31 | ####################################### 32 | 33 | 34 | DMXSerial KEYWORD2 35 | 36 | 37 | ####################################### 38 | # Constants (LITERAL1) 39 | ####################################### 40 | 41 | DMXNone LITERAL1 42 | DMXController LITERAL1 43 | DMXReceiver LITERAL1 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=DMXSerial 2 | version=1.5.3 3 | author=Matthias Hertel 4 | maintainer=Matthias Hertel, http://www.mathertel.de 5 | sentence=Enables DMX communication using the built-in serial port for Arduino boards. 6 | paragraph=This is a library for sending and receiving DMX codes using the Arduino plattform. You can implement DMX devices and DMX controllers with this library. 7 | category=Communication 8 | url=http://www.mathertel.de/Arduino/DMXSerial.aspx 9 | architectures=avr,megaavr 10 | -------------------------------------------------------------------------------- /src/DMXSerial.cpp: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DMXSerial - A Arduino library for sending and receiving DMX using the builtin serial hardware port. 3 | // DMXSerial.cpp: Library implementation file 4 | // 5 | // Copyright (c) 2011-2020 by Matthias Hertel, http://www.mathertel.de 6 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 7 | // 8 | // Documentation and samples are available at http://www.mathertel.de/Arduino 9 | // Changelog: See DMXSerial.h 10 | // - - - - - 11 | 12 | #include "Arduino.h" 13 | #include "DMXSerial.h" 14 | 15 | 16 | // ----- forwards ----- 17 | 18 | void _DMXStartSending(); 19 | void _DMXStartReceiving(); 20 | 21 | // register interrupt for receiving data and frameerrors that calls _DMXReceived() 22 | void _DMXReceived(uint8_t data, uint8_t frameerror); 23 | void _DMXTransmitted(); 24 | 25 | 26 | // These functions all exist in the processor specific implementations: 27 | void _DMX_init(); 28 | void _DMX_setMode(); 29 | void _DMX_writeByte(uint8_t data); 30 | void _DMX_flush(); 31 | 32 | 33 | // ----- Serial UART Modes ----- 34 | 35 | // There are 4 different modes required while receiving and sending DMX using the Serial 36 | 37 | // State of receiving DMX Bytes 38 | typedef enum { 39 | OFF = 0, // Turn off 40 | RONLY = 1, // Receive DMX data only 41 | RDATA = 2, // Receive DMX data + Interrupt 42 | TBREAK = 3, // Transmit DMX Break + Interrupt on Transmission completed 43 | TDATA = 4, // Transmit DMX Data + Interrupt on Register empty 44 | TDONE = 5 // Transmit DMX Data + Interrupt on Transmission completed 45 | } __attribute__((packed)) DMXUARTMode; 46 | 47 | 48 | // Baud rate for DMX protocol 49 | #define DMXSPEED 250000L 50 | 51 | // the break timing is 10 bits (start + 8 data + 1 (even) parity) of this speed 52 | // the mark-after-break is 1 bit of this speed plus approx 6 usec 53 | // 100000 bit/sec is good: gives 100 usec break and 16 usec MAB 54 | // 1990 spec says transmitter must send >= 92 usec break and >= 12 usec MAB 55 | // receiver must accept 88 us break and 8 us MAB 56 | #define BREAKSPEED 100000L 57 | 58 | #define BREAKFORMAT SERIAL_8E2 59 | #define DMXFORMAT SERIAL_8N2 60 | #define DMXREADFORMAT SERIAL_8N1 61 | 62 | 63 | // ----- include processor specific definitions and functions. 64 | 65 | #if defined(ARDUINO_ARCH_AVR) 66 | #include "DMXSerial_avr.h" 67 | 68 | #elif defined(ARDUINO_ARCH_MEGAAVR) 69 | #include "DMXSerial_megaavr.h" 70 | 71 | #endif 72 | 73 | 74 | // ----- Enumerations ----- 75 | 76 | // State of receiving DMX Bytes 77 | typedef enum { 78 | STARTUP = 1, // wait for any interrupt BEFORE starting analyzing the DMX protocoll. 79 | IDLE = 2, // wait for a BREAK condition. 80 | BREAK = 3, // BREAK was detected. 81 | DATA = 4, // DMX data. 82 | DONE = 5 // All channels received. 83 | } __attribute__((packed)) DMXReceivingState; 84 | 85 | 86 | // ----- DMXSerial Private variables ----- 87 | // These variables are not class members because they have to be reached by the interrupt implementations. 88 | // don't use these variable from outside, use the appropriate methods. 89 | 90 | DMXMode _dmxMode; // Mode of Operation 91 | int _dmxModePin; // pin used for I/O direction. 92 | 93 | uint8_t _dmxRecvState; // Current State of receiving DMX Bytes 94 | int _dmxChannel; // the next channel byte to be sent. 95 | 96 | volatile int _dmxMaxChannel = 32; // the last channel used for sending (1..512). 97 | volatile unsigned long _dmxLastPacket = 0; // the last time (using the millis function) a packet was received. 98 | 99 | bool _dmxUpdated = true; // is set to true when new data arrived. 100 | 101 | // Array of DMX values (raw). 102 | // Entry 0 will never be used for DMX data but will store the startbyte (0 for DMX mode). 103 | uint8_t _dmxData[DMXSERIAL_MAX + 1]; 104 | 105 | // This pointer will point to the next byte in _dmxData; 106 | uint8_t *_dmxDataPtr; 107 | 108 | // This pointer will point to the last byte in _dmxData; 109 | uint8_t *_dmxDataLastPtr; 110 | 111 | // Create a single class instance. Multiple class instances (multiple simultaneous DMX ports) are not supported. 112 | DMXSerialClass DMXSerial; 113 | 114 | 115 | // ----- Class implementation ----- 116 | 117 | // Initialize the specified mode. 118 | void DMXSerialClass::init(int mode) 119 | { 120 | init(mode, DMXMODEPIN); 121 | } 122 | 123 | // (Re)Initialize the specified mode. 124 | // The mode parameter should be a value from enum DMXMode. 125 | void DMXSerialClass::init(int mode, int dmxModePin) 126 | { 127 | // initialize global variables 128 | _dmxMode = DMXNone; 129 | _dmxModePin = dmxModePin; 130 | _dmxRecvState = STARTUP; // initial state 131 | _dmxChannel = 0; 132 | _dmxDataPtr = _dmxData; 133 | _dmxLastPacket = millis(); // remember current (relative) time in msecs. 134 | 135 | _dmxMaxChannel = DMXSERIAL_MAX; // The default in Receiver mode is reading all possible 512 channels. 136 | _dmxDataLastPtr = _dmxData + _dmxMaxChannel; 137 | 138 | // initialize the DMX buffer 139 | // memset(_dmxData, 0, sizeof(_dmxData)); 140 | for (int n = 0; n < DMXSERIAL_MAX + 1; n++) 141 | _dmxData[n] = 0; 142 | 143 | // now start 144 | _dmxMode = (DMXMode)mode; 145 | 146 | if ((_dmxMode == DMXController) || (_dmxMode == DMXReceiver) || (_dmxMode == DMXProbe)) { 147 | // a valid mode was given 148 | // Setup external mode signal 149 | _DMX_init(); 150 | 151 | pinMode(_dmxModePin, OUTPUT); // enables the pin for output to control data direction 152 | digitalWrite(_dmxModePin, DmxModeIn); // data in direction, to avoid problems on the DMX line for now. 153 | 154 | if (_dmxMode == DMXController) { 155 | digitalWrite(_dmxModePin, DmxModeOut); // data Out direction 156 | _dmxMaxChannel = 32; // The default in Controller mode is sending 32 channels. 157 | _DMXStartSending(); 158 | 159 | } else if (_dmxMode == DMXReceiver) { 160 | // Setup Hardware 161 | _DMXStartReceiving(); 162 | 163 | // } else if (_dmxMode == DMXProbe) { 164 | // // don't setup the Hardware now 165 | 166 | } // if 167 | } // if 168 | } // init() 169 | 170 | 171 | // Set the maximum used channel. 172 | // This method can be called any time before or after the init() method. 173 | void DMXSerialClass::maxChannel(int channel) 174 | { 175 | if (channel < 1) 176 | channel = 1; 177 | if (channel > DMXSERIAL_MAX) 178 | channel = DMXSERIAL_MAX; 179 | _dmxMaxChannel = channel; 180 | _dmxDataLastPtr = _dmxData + channel; 181 | } // maxChannel 182 | 183 | 184 | // Read the current value of a channel. 185 | uint8_t DMXSerialClass::read(int channel) 186 | { 187 | // adjust parameter 188 | if (channel < 1) 189 | channel = 1; 190 | if (channel > DMXSERIAL_MAX) 191 | channel = DMXSERIAL_MAX; 192 | // read value from buffer 193 | return (_dmxData[channel]); 194 | } // read() 195 | 196 | 197 | // Write the value into the channel. 198 | // The value is just stored in the sending buffer and will be picked up 199 | // by the DMX sending interrupt routine. 200 | void DMXSerialClass::write(int channel, uint8_t value) 201 | { 202 | // adjust parameters 203 | if (channel < 1) 204 | channel = 1; 205 | if (channel > DMXSERIAL_MAX) 206 | channel = DMXSERIAL_MAX; 207 | 208 | // store value for later sending 209 | _dmxData[channel] = value; 210 | 211 | // Make sure we transmit enough channels for the ones used 212 | if (channel > _dmxMaxChannel) { 213 | _dmxMaxChannel = channel; 214 | _dmxDataLastPtr = _dmxData + _dmxMaxChannel; 215 | } // if 216 | } // write() 217 | 218 | 219 | // Return the DMX buffer for un-save direct but faster access 220 | uint8_t *DMXSerialClass::getBuffer() 221 | { 222 | return (_dmxData); 223 | } // getBuffer() 224 | 225 | 226 | // Calculate how long no data packet was received 227 | unsigned long DMXSerialClass::noDataSince() 228 | { 229 | unsigned long now = millis(); 230 | return (now - _dmxLastPacket); 231 | } // noDataSince() 232 | 233 | 234 | // return true when some DMX data was updated. 235 | bool DMXSerialClass::dataUpdated() 236 | { 237 | return (_dmxUpdated); 238 | } 239 | 240 | 241 | // reset DMX data update flag. 242 | void DMXSerialClass::resetUpdated() 243 | { 244 | _dmxUpdated = false; 245 | } 246 | 247 | 248 | // When mode is DMXProbe this function reads one DMX buffer and then returns. 249 | // wait a meaningful time on a packet. 250 | // return true when a packet has been received. 251 | bool DMXSerialClass::receive() 252 | { 253 | return (receive(DMXPROBE_RECV_MAX)); 254 | } // receive() 255 | 256 | 257 | // When mode is DMXProbe this function reads one DMX buffer and then returns. 258 | // wait approximately gives the number of msecs for waiting on a packet. 259 | // return true when a packet has been received. 260 | bool DMXSerialClass::receive(uint8_t wait) 261 | { 262 | bool ret = false; 263 | 264 | if (_dmxMode == DMXProbe) { 265 | _DMXStartReceiving(); 266 | // UCSRnA 267 | while ((wait > 0) && (_dmxRecvState != DONE)) { 268 | delay(1); 269 | wait--; 270 | } // while 271 | 272 | if (_dmxRecvState == DONE) { 273 | ret = true; 274 | } else { 275 | _DMX_setMode(DMXUARTMode::RONLY); 276 | } // if 277 | } // if 278 | 279 | return (ret); 280 | } // receive(wait) 281 | 282 | 283 | // Terminate operation 284 | void DMXSerialClass::term(void) 285 | { 286 | // Disable all USART Features, including Interrupts 287 | _DMX_setMode(DMXUARTMode::OFF); 288 | } // term() 289 | 290 | 291 | // ----- internal functions and interrupt implementations ----- 292 | 293 | 294 | // Setup Hardware for Sending 295 | void _DMXStartSending() 296 | { 297 | // Start sending a BREAK and send more bytes in UDRE ISR 298 | // Enable transmitter and interrupt 299 | _DMX_setMode(DMXUARTMode::TBREAK); 300 | _DMX_writeByte((uint8_t)0); 301 | } // _DMXStartSending() 302 | 303 | 304 | // Setup Hardware for Receiving 305 | void _DMXStartReceiving() 306 | { 307 | // Enable receiver and Receive interrupt 308 | _dmxDataPtr = _dmxData; 309 | _dmxRecvState = STARTUP; 310 | 311 | _DMX_setMode(DMXUARTMode::RDATA); 312 | _DMX_flush(); 313 | } // _DMXStartReceiving() 314 | 315 | 316 | // This function is called by the Interrupt Service Routine when a byte or frame error was received. 317 | // In DMXController mode this interrupt is disabled and will not occur. 318 | // In DMXReceiver mode when a byte was received it is stored to the dmxData buffer. 319 | void _DMXReceived(uint8_t data, uint8_t frameerror) 320 | { 321 | uint8_t DmxState = _dmxRecvState; //just load once from SRAM to increase speed 322 | 323 | if (DmxState == STARTUP) { 324 | // just ignore any first frame comming in 325 | _dmxRecvState = IDLE; 326 | return; 327 | } 328 | 329 | if (frameerror) { //check for break 330 | // break condition detected. 331 | _dmxRecvState = BREAK; 332 | _dmxDataPtr = _dmxData; 333 | 334 | } else if (DmxState == BREAK) { 335 | // first byte after a break was read. 336 | if (data == 0) { 337 | // normal DMX start code (0) detected 338 | _dmxRecvState = DATA; 339 | _dmxLastPacket = millis(); // remember current (relative) time in msecs. 340 | _dmxDataPtr++; // start saving data with channel # 1 341 | 342 | } else { 343 | // This might be a RDM or customer DMX command -> not implemented so wait for next BREAK ! 344 | _dmxRecvState = DONE; 345 | } // if 346 | 347 | } else if (DmxState == DATA) { 348 | // check for new data 349 | if (*_dmxDataPtr != data) { 350 | _dmxUpdated = true; 351 | // store received data into dmx data buffer. 352 | *_dmxDataPtr = data; 353 | } // if 354 | _dmxDataPtr++; 355 | 356 | if (_dmxDataPtr > _dmxDataLastPtr) { 357 | // all channels received. 358 | _dmxRecvState = DONE; 359 | } // if 360 | } // if 361 | 362 | if (_dmxRecvState == DONE) { 363 | if (_dmxMode == DMXProbe) { 364 | // stop creating interrupts on the serial port for now. 365 | _DMX_setMode(DMXUARTMode::RONLY); 366 | 367 | } else { 368 | // continue on DMXReceiver mode. 369 | _dmxRecvState = IDLE; // wait for next break 370 | } 371 | } // if 372 | 373 | } // _DMXReceived() 374 | 375 | 376 | // This function is called by the Transmission complete or Data Register empty interrupt routine. 377 | // When changing speed (after sending BREAK) we use TX finished interrupt that occurs shortly after the last stop bit is sent 378 | // When staying at the same speed (sending data bytes) we use data register empty interrupt that occurs shortly after the start bit of the *previous* byte 379 | // When sending a DMX sequence it just takes the next channel byte and sends it out. 380 | // In DMXController mode when the buffer was sent completely the DMX sequence will resent, starting with a BREAK pattern. 381 | // In DMXReceiver mode this interrupt is disabled and will not occur. 382 | void _DMXTransmitted() 383 | { 384 | if ((_dmxMode == DMXController) && (_dmxChannel == -1)) { 385 | // this occurs after the stop bits of the last data byte 386 | // start sending a BREAK and loop forever in ISR 387 | _DMX_setMode(DMXUARTMode::TBREAK); 388 | _DMX_writeByte((uint8_t)0); 389 | _dmxChannel = 0; // next time send start byte 390 | 391 | } else if (_dmxChannel == 0) { 392 | // this occurs after the stop bits of the break byte 393 | // now back to DMX speed: 250000baud 394 | // take next interrupt when data register empty (early) 395 | _DMX_setMode(DMXUARTMode::TDATA); 396 | 397 | // write start code 398 | _DMX_writeByte((uint8_t)0); 399 | _dmxChannel = 1; 400 | 401 | } else { 402 | if (_dmxChannel < _dmxMaxChannel) { 403 | // just send the next data 404 | _DMX_writeByte(_dmxData[_dmxChannel++]); 405 | } else { 406 | // last data 407 | _DMX_setMode(DMXUARTMode::TDONE); 408 | _DMX_writeByte(_dmxData[_dmxChannel]); 409 | _dmxChannel = -1; // this series is done. Next time: restart with break. 410 | } // if 411 | 412 | } // if 413 | } // _DMXTransmitted 414 | 415 | 416 | // The End 417 | -------------------------------------------------------------------------------- /src/DMXSerial.h: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DMXSerial - A Arduino library for sending and receiving DMX using the builtin serial hardware port. 3 | // DMXSerial.h: Library header file 4 | // 5 | // Copyright (c) 2011-2014 by Matthias Hertel, http://www.mathertel.de 6 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 7 | // 8 | // Documentation and samples are available at http://www.mathertel.de/Arduino 9 | // 25.07.2011 creation of the DMXSerial library. 10 | // 10.09.2011 fully control the serial hardware register 11 | // without using the Arduino Serial (HardwareSerial) class to avoid ISR implementation conflicts. 12 | // 01.12.2011 include file changed to work with the Arduino 1.0 environment 13 | // 28.12.2011 unused variable DmxCount removed 14 | // 10.05.2012 added method noDataSince to check how long no packet was received 15 | // 04.06.2012: set UCSRnA = 0 to use normal speed operation 16 | // 30.07.2012 corrected TX timings with UDRE and TX interrupts 17 | // fixed bug in 512-channel RX 18 | // 26.03.2013 #defines for the interrupt vector names 19 | // auto-increase _dmxMaxChannel 20 | // 15.05.2013 Arduino Leonard and Arduino MEGA compatibility 21 | // 19.05.2013 ATmega8 compatibility (beta) 22 | // 24.08.2013 Optimizations for speed and size. 23 | // Removed some "volatile" annotations. 24 | // 12.07.2014 added update flag 25 | // 19.03.2015 DMXModePin as optional parameter 26 | // 25.08.2016 SCOPEDEBUG removed. 27 | // 04.06.2017 Serial Initialization consolidated into _DMXSerialInit, 28 | // _DMXStartSending and _DMXStartReceiving functions. 29 | // 27.08.2017 DMXProbe mode finished. 30 | // 29.10.2017 documentation. 31 | // 32 | // 07.02.2020 Refactored to support multiple hardware layers. 33 | // 26.02.2020 Version 1.5.0 34 | // - - - - - 35 | 36 | #ifndef DmxSerial_h 37 | #define DmxSerial_h 38 | 39 | #include 40 | 41 | // ----- Constants ----- 42 | 43 | #define DMXSERIAL_MAX 512 ///< max. number of supported DMX data channels 44 | 45 | #define DMXMODEPIN 2 ///< Arduino pin 2 for controlling the data direction is the default value. 46 | #define DmxModeOut HIGH ///< set the level to HIGH for outgoing data direction 47 | #define DmxModeIn LOW ///< set the level to LOW for incoming data direction 48 | 49 | #define DMXPROBE_RECV_MAX 50 // standard maximum of waiting for a DMX packet in DMXPROBE mode. 50 | 51 | // ----- Enumerations ----- 52 | 53 | /** 54 | * Mode of Operation 55 | */ 56 | typedef enum { 57 | DMXNone, // unspecified 58 | DMXController , // always sending 59 | DMXReceiver, // always listening 60 | DMXProbe // send and receive upon request 61 | } DMXMode; 62 | 63 | 64 | // ----- Library Class ----- 65 | 66 | extern "C" { 67 | typedef void (*dmxUpdateFunction)(void); 68 | } 69 | 70 | /** 71 | * @brief Arduino library to send and receive DMX. 72 | * 73 | * The library works unchanged with the Arduino 2009, UNO, MEGA 2560 and Leonardo boards.
74 | * The Arduino MEGA 2560 boards use the serial port 0 on pins 0 an 1.
75 | * The Arduino Leonardo will use serial port 1, also on pins 0 an 1. (on the 32u4 boards the first USART is USART1)
76 | * This is consistent with the Layout of the Arduino DMX Shield http://www.mathertel.de/Arduino/DMXShield.aspx. 77 | */ 78 | class DMXSerialClass 79 | { 80 | public: 81 | /** 82 | * @brief Initialize or re-initialize the specified mode. 83 | * This function can be called to switch to restart or switch the operatin mode. 84 | * @param [in] mode The mode of operation to be started. A value from enum DMXMode; 85 | * @return void 86 | */ 87 | void init (int mode); 88 | 89 | 90 | /** 91 | * @brief Initialize the specified mode including a specific mode pin. 92 | * This function can be called to switch to restart or switch the operatin mode. 93 | * @param [in] mode The mode of operation to be started. A value from enum DMXMode; 94 | * @param [in] modePin The pin number for switching communication direction. 95 | * @return void 96 | */ 97 | void init (int mode, int modePin); 98 | 99 | /** 100 | * @brief Set the maximum used channel for DMXController mode. 101 | * @param [in] channel The highest channel that will be transferred. 102 | * @return void 103 | */ 104 | void maxChannel (int channel); 105 | 106 | /** 107 | * @brief Read the current value of a channel. 108 | * @param [in] channel The channel number. 109 | * @return uint8_t The current value. 110 | */ 111 | uint8_t read (int channel); 112 | 113 | /** 114 | * @brief Write a new value to a channel. 115 | * This function also can be called in DMXReceiver mode to set a channel value even when no data is received. 116 | * It will be overwritten by the next received package. 117 | * @param [in] channel The channel number. 118 | * @param [in] value The current value. 119 | * @return void 120 | */ 121 | void write (int channel, uint8_t value); 122 | 123 | /** 124 | * @brief Get a pointer to DMX Buffer. 125 | * This is the internal byte-array where the current DMX values are stored. 126 | * @return uint8_t DMX values buffer. 127 | */ 128 | uint8_t *getBuffer(); 129 | 130 | /** 131 | * @brief Return the duration since data was received. 132 | * On startup the internal timer is reset too. 133 | * @return long milliseconds since last pdata package. 134 | */ 135 | unsigned long noDataSince(); 136 | 137 | /** 138 | * @brief Check for changed data. 139 | * Check DMX data was updated by a received package since last resetUpdated() call or onUpdate callback. 140 | * @return true Some DMX data was updated. 141 | * @return false No updated data. 142 | */ 143 | bool dataUpdated(); 144 | 145 | /** 146 | * @brief Reset DMX data update flag. 147 | */ 148 | void resetUpdated(); 149 | 150 | /** 151 | * @brief Actively wait for an incoming DMX packet. 152 | * This function waits DMXPROBE_RECV_MAX milliseconds for a new package to receive. 153 | * @return true when a package was received. 154 | * @return false after timeout no package was received. 155 | */ 156 | bool receive(); 157 | 158 | /** 159 | * @brief Actively wait for an incoming DMX packet. 160 | * This function waits the specified milliseconds for a new package to receive. 161 | * @param wait Milliseconds to wait for a new package. 162 | * @return true when a package was received. 163 | * @return false after timeout no package was received. 164 | */ 165 | bool receive(uint8_t wait); 166 | 167 | /** 168 | * @brief Terminate the current operation mode. 169 | */ 170 | void term(); 171 | 172 | private: 173 | // Not used. 174 | // all private information is in the global _dmxXXX variables for speed and code size optimization. 175 | // @see DMXSerial.cpp. 176 | 177 | }; 178 | 179 | // Use the DMXSerial library through the DMXSerial object. 180 | // There is only one DMX port supported and DMXSerial is a static object. 181 | extern DMXSerialClass DMXSerial; 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /src/DMXSerial_avr.h: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DMXSerial - A Arduino library for sending and receiving DMX using the builtin serial hardware port. 3 | // DMXSerial_avr.h: Hardware specific functions for AVR processors like ATmega168 and ATmega328 used in Aurduino UNO. 4 | // Also supported boards are Leonardo and Mega2560. 5 | // 6 | // Copyright (c) 2011-2020 by Matthias Hertel, http://www.mathertel.de 7 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 8 | // - - - - - 9 | 10 | // global variables and functions are prefixed with "_DMX_" 11 | 12 | // ----- ATMega specific hardware related functions ----- 13 | 14 | #ifndef DMXSERIAL_AVR_H 15 | #define DMXSERIAL_AVR_H 16 | 17 | #if defined(DMXFORMAT) && defined(ARDUINO_ARCH_AVR) 18 | 19 | #include "Arduino.h" 20 | #include "DMXSerial.h" 21 | #include "avr/io.h" 22 | #include "avr/interrupt.h" 23 | 24 | // ----- Constants ----- 25 | 26 | // Define port & bit values for Hardware Serial Port. 27 | // The library works unchanged with the Arduino 2009, UNO, MEGA 2560 and Leonardo boards. 28 | // The Arduino MEGA 2560 boards use the serial port 0 on pins 0 an 1. 29 | // The Arduino Leonardo will use serial port 1, also on pins 0 an 1. (on the 32u4 boards the first USART is USART1) 30 | // This is consistent to the Layout of the Arduino DMX Shield http://www.mathertel.de/Arduino/DMXShield.aspx. 31 | 32 | // For using the serial port 1 on a Arduino MEGA 2560 board, enable the following DMX_USE_PORT1 definition. 33 | // #define DMX_USE_PORT1 34 | 35 | #if !defined(DMX_USE_PORT1) && defined(USART_RXC_vect) 36 | // These definitions are used on ATmega8 boards 37 | #define UCSRnA UCSRA // Control and Status Register A 38 | #define TXCn TXC // Transmit buffer clear 39 | #define RXCn RXC 40 | 41 | #define UCSRnB UCSRB // USART Control and Status Register B 42 | 43 | #define RXCIEn RXCIE // Enable Receive Complete Interrupt 44 | #define TXCIEn TXCIE // Enable Transmission Complete Interrupt 45 | #define UDRIEn UDRIE // Enable Data Register Empty Interrupt 46 | #define RXENn RXEN // Enable Receiving 47 | #define TXENn TXEN // Enable Sending 48 | 49 | #define UCSRnC UCSRC // Control and Status Register C 50 | #define USBSn USBS // Stop bit select 0=1bit, 1=2bits 51 | #define UCSZn0 UCSZ0 // Character size 00=5, 01=6, 10=7, 11=8 bits 52 | #define UPMn0 UPM0 // Parity setting 00=N, 10=E, 11=O 53 | 54 | #define UBRRnH UBRRH // USART Baud Rate Register High 55 | #define UBRRnL UBRRL // USART Baud Rate Register Low 56 | 57 | #define UDRn UDR // USART Data Register 58 | #define UDREn UDRE // USART Data Ready 59 | #define FEn FE // Frame Error 60 | 61 | #define USARTn_RX_vect USART_RXC_vect // Interrupt Data received 62 | #define USARTn_TX_vect USART_TXC_vect // Interrupt Data sent 63 | #define USARTn_UDRE_vect USART_UDRE_vect // Interrupt Data Register empty 64 | 65 | 66 | #elif !defined(DMX_USE_PORT1) && defined(USART_RX_vect) 67 | // These definitions are used on ATmega168p and ATmega328p boards 68 | // like the Arduino Diecimila, Duemilanove, 2009, Uno 69 | #define UCSRnA UCSR0A 70 | #define RXCn RXC0 71 | #define TXCn TXC0 72 | #define UCSRnB UCSR0B 73 | #define RXCIEn RXCIE0 74 | #define TXCIEn TXCIE0 75 | #define UDRIEn UDRIE0 76 | #define RXENn RXEN0 77 | #define TXENn TXEN0 78 | #define UCSRnC UCSR0C 79 | #define USBSn USBS0 80 | #define UCSZn0 UCSZ00 81 | #define UPMn0 UPM00 82 | #define UBRRnH UBRR0H 83 | #define UBRRnL UBRR0L 84 | #define UDRn UDR0 85 | #define UDREn UDRE0 86 | #define FEn FE0 87 | #define USARTn_RX_vect USART_RX_vect 88 | #define USARTn_TX_vect USART_TX_vect 89 | #define USARTn_UDRE_vect USART_UDRE_vect 90 | 91 | #elif !defined(DMX_USE_PORT1) && defined(USART0_RX_vect) 92 | // These definitions are used on ATmega1280 and ATmega2560 boards 93 | // like the Arduino MEGA boards 94 | #define UCSRnA UCSR0A 95 | #define RXCn RXC0 96 | #define TXCn TXC0 97 | #define UCSRnB UCSR0B 98 | #define RXCIEn RXCIE0 99 | #define TXCIEn TXCIE0 100 | #define UDRIEn UDRIE0 101 | #define RXENn RXEN0 102 | #define TXENn TXEN0 103 | #define UCSRnC UCSR0C 104 | #define USBSn USBS0 105 | #define UCSZn0 UCSZ00 106 | #define UPMn0 UPM00 107 | #define UBRRnH UBRR0H 108 | #define UBRRnL UBRR0L 109 | #define UDRn UDR0 110 | #define UDREn UDRE0 111 | #define FEn FE0 112 | #define USARTn_RX_vect USART0_RX_vect 113 | #define USARTn_TX_vect USART0_TX_vect 114 | #define USARTn_UDRE_vect USART0_UDRE_vect 115 | 116 | #elif defined(DMX_USE_PORT1) || defined(USART1_RX_vect) 117 | // These definitions are used for using serial port 1 118 | // on ATmega32U4 boards like Arduino Leonardo, Esplora 119 | // You can use it on other boards with USART1 by enabling the DMX_USE_PORT1 port definition 120 | #define UCSRnA UCSR1A 121 | #define RXCn RXC1 122 | #define TXCn TXC1 123 | #define UCSRnB UCSR1B 124 | #define RXCIEn RXCIE1 125 | #define TXCIEn TXCIE1 126 | #define UDRIEn UDRIE1 127 | #define RXENn RXEN1 128 | #define TXENn TXEN1 129 | #define UCSRnC UCSR1C 130 | #define USBSn USBS1 131 | #define UCSZn0 UCSZ10 132 | #define UPMn0 UPM10 133 | #define UBRRnH UBRR1H 134 | #define UBRRnL UBRR1L 135 | #define UDRn UDR1 136 | #define UDREn UDRE1 137 | #define FEn FE1 138 | #define USARTn_RX_vect USART1_RX_vect 139 | #define USARTn_TX_vect USART1_TX_vect 140 | #define USARTn_UDRE_vect USART1_UDRE_vect 141 | 142 | #endif 143 | 144 | 145 | // ----- ATMega specific Hardware abstraction functions ----- 146 | 147 | // calculate prescaler from baud rate and cpu clock rate at compile time. 148 | // This is a processor specific formula from the datasheet. 149 | // It implements rounding of ((clock / 16) / baud) - 1. 150 | #define CalcPreScale(B) (((((F_CPU) / 8) / (B)) - 1) / 2) 151 | 152 | const int32_t _DMX_dmxPreScale = CalcPreScale(DMXSPEED); // BAUD prescale factor for DMX speed. 153 | const int32_t _DMX_breakPreScale = CalcPreScale(BREAKSPEED); // BAUD prescale factor for BREAK speed. 154 | 155 | 156 | // initialize mode independent registers. 157 | void _DMX_init() 158 | { 159 | // 04.06.2012: use normal speed operation 160 | UCSRnA = 0; 161 | } // _DMX_init() 162 | 163 | 164 | /// Initialize the Hardware UART serial port registers to the required mode. 165 | void _DMX_setMode(DMXUARTMode mode) 166 | { 167 | if (mode == DMXUARTMode::OFF) { 168 | UCSRnB = 0; 169 | 170 | } else if (mode == DMXUARTMode::RONLY) { 171 | // assign the baud_setting to the USART Baud Rate Register 172 | UBRRnH = _DMX_dmxPreScale >> 8; 173 | UBRRnL = _DMX_dmxPreScale; 174 | // enable USART functions RX, TX, Interrupts 175 | UCSRnB = (1 << RXENn); 176 | // stop bits and character size 177 | UCSRnC = DMXREADFORMAT; // accept data packets after first stop bit 178 | 179 | } else if (mode == DMXUARTMode::RDATA) { 180 | UBRRnH = _DMX_dmxPreScale >> 8; 181 | UBRRnL = _DMX_dmxPreScale; 182 | UCSRnB = (1 << RXENn) | (1 << RXCIEn); 183 | UCSRnC = DMXREADFORMAT; // accept data packets after first stop bit 184 | 185 | } else if (mode == DMXUARTMode::TBREAK) { 186 | UBRRnH = _DMX_breakPreScale >> 8; 187 | UBRRnL = _DMX_breakPreScale; 188 | UCSRnB = ((1 << TXENn) | (1 << TXCIEn)); 189 | UCSRnC = BREAKFORMAT; 190 | 191 | } else if (mode == DMXUARTMode::TDATA) { 192 | UBRRnH = _DMX_dmxPreScale >> 8; 193 | UBRRnL = _DMX_dmxPreScale; 194 | UCSRnB = ((1 << TXENn) | (1 << UDRIEn)); 195 | UCSRnC = DMXFORMAT; // send with 2 stop bits for compatibility 196 | 197 | } else if (mode == DMXUARTMode::TDONE) { 198 | UBRRnH = _DMX_dmxPreScale >> 8; 199 | UBRRnL = _DMX_dmxPreScale; 200 | UCSRnB = ((1 << TXENn) | (1 << TXCIEn)); 201 | UCSRnC = DMXFORMAT; // send with 2 stop bits for compatibility 202 | } // if 203 | } // _DMX_setMode() 204 | 205 | 206 | // flush all incoming data packets in the queue 207 | void _DMX_flush() 208 | { 209 | uint8_t voiddata; 210 | while (UCSRnA & (1 << RXCn)) { 211 | voiddata = UDRn; // get data 212 | } 213 | } 214 | 215 | 216 | // send the next byte after current byte was sent completely. 217 | inline void _DMX_writeByte(uint8_t data) 218 | { 219 | // putting data into buffer sends the data 220 | UDRn = data; 221 | } // _DMX_writeByte 222 | 223 | 224 | // This Interrupt Service Routine is called when a byte or frame error was received. 225 | // In DMXController mode this interrupt is disabled and will not occur. 226 | // In DMXReceiver mode when a byte or frame error was received 227 | ISR(USARTn_RX_vect) 228 | { 229 | uint8_t rxferr = (UCSRnA & (1 << FEn)); // get state before data! 230 | uint8_t rxdata = UDRn; // get data 231 | _DMXReceived(rxdata, rxferr); 232 | } // ISR(USARTn_RX_vect) 233 | 234 | 235 | // Interrupt service routines that are called when the actual byte was sent. 236 | ISR(USARTn_TX_vect) 237 | { 238 | _DMXTransmitted(); 239 | } // ISR(USARTn_TX_vect) 240 | 241 | 242 | // this interrupt occurs after data register was emptied by handing it over to the shift register. 243 | ISR(USARTn_UDRE_vect) 244 | { 245 | _DMXTransmitted(); 246 | } // ISR(USARTn_UDRE_vect) 247 | 248 | 249 | #endif 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /src/DMXSerial_megaavr.h: -------------------------------------------------------------------------------- 1 | // - - - - - 2 | // DMXSerial - A Arduino library for sending and receiving DMX using the builtin serial hardware port. 3 | // DMXSerial_magaavr.h: Hardware specific functions for MEGAAVR processors like 4809 used in Arduino Every. 4 | 5 | // Copyright (c) 2011-2020 by Matthias Hertel, http://www.mathertel.de 6 | // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx 7 | // - - - - - 8 | 9 | // global variables and functions are prefixed with "_DMX_" 10 | 11 | // ----- MegaAVR specific Hardware abstraction functions ----- 12 | 13 | #ifndef DMXSERIAL_MEGAAVR_H 14 | #define DMXSERIAL_MEGAAVR_H 15 | 16 | #if defined(DMXFORMAT) && defined(ARDUINO_ARCH_MEGAAVR) 17 | 18 | #include "Arduino.h" 19 | #include "DMXSerial.h" 20 | #include "avr/io.h" 21 | 22 | 23 | int32_t _DMX_dmxDivider; // BAUD Devider factor for DMX speed. 24 | int32_t _DMX_breakDivider; // BAUD Devider factor for BREAK speed. 25 | 26 | 27 | /// Initialize the Hardware MUX and UART serial port. 28 | void _DMX_init() 29 | { 30 | int32_t baud; 31 | 32 | int8_t oscErr = SIGROW.OSC16ERR5V; 33 | 34 | // calculate DMX speed Divider 35 | baud = (DMXSPEED * (1024 + oscErr)) / 1024; 36 | _DMX_dmxDivider = (64 * F_CPU) / (16 * baud); 37 | 38 | // calculate BREAK speed Divider 39 | baud = (BREAKSPEED * (1024 + oscErr)) / 1024; 40 | _DMX_breakDivider = (64 * F_CPU) / (16 * baud); 41 | 42 | // disable interrupts during initialization 43 | uint8_t oldSREG = SREG; 44 | cli(); 45 | 46 | // Setup port mux 47 | PORTMUX.USARTROUTEA |= PORTMUX_USART1_ALT1_gc; 48 | 49 | // Disable CLK2X, clock normal rate 50 | (USART1).CTRLB = USART_RXMODE_NORMAL_gc; 51 | 52 | //Set up the rx & tx pins 53 | pinMode(PIN_WIRE_HWSERIAL1_RX, INPUT_PULLUP); 54 | pinMode(PIN_WIRE_HWSERIAL1_TX, OUTPUT); 55 | 56 | // enable interrupts again, restore SREG content 57 | SREG = oldSREG; 58 | } // _DMX_init() 59 | 60 | 61 | /// Initialize the Hardware UART serial port to the required mode. 62 | void _DMX_setMode(DMXUARTMode mode) 63 | { 64 | uint16_t baud_setting; 65 | uint8_t flags; 66 | uint8_t format; 67 | 68 | // disable interrupts during initialization 69 | uint8_t oldSREG = SREG; 70 | cli(); 71 | 72 | if (mode == DMXUARTMode::OFF) { 73 | // Disable transmitter and receiver 74 | (USART1).CTRLB = USART_RXMODE_NORMAL_gc; // (USART_RXEN_bm | USART_TXEN_bm); 75 | (USART1).CTRLA = 0; // disable all interrupts 76 | 77 | } else if (mode == DMXUARTMode::RONLY) { 78 | (USART1).BAUD = (int16_t)_DMX_dmxDivider; // assign the baud_divider, a.k.a. BAUD (USART Baud Rate Register) 79 | (USART1).CTRLC = DMXREADFORMAT; // accept data packets after first stop bit 80 | (USART1).CTRLB = USART_RXEN_bm | USART_RXMODE_NORMAL_gc; // Enable receiver only, normal speed 81 | (USART1).CTRLA = 0; // disable all interrupts 82 | 83 | } else if (mode == DMXUARTMode::RDATA) { 84 | (USART1).BAUD = (int16_t)_DMX_dmxDivider; // assign the baud_divider, a.k.a. BAUD (USART Baud Rate Register) 85 | (USART1).CTRLC = DMXREADFORMAT; // accept data packets after first stop bit 86 | (USART1).CTRLB = USART_RXEN_bm | USART_RXMODE_NORMAL_gc; // Enable receiver only, normal speed 87 | (USART1).CTRLA = USART_RXCIE_bm; // enable receive complete interrupt 88 | 89 | } else if (mode == DMXUARTMode::TBREAK) { 90 | // start UART with break settings, don't enable interrupts yet 91 | (USART1).CTRLB = 0; // no operation 92 | (USART1).CTRLA = 0; // disable all interrupts 93 | 94 | (USART1).BAUD = (int16_t)_DMX_breakDivider; // assign the baud_divider, a.k.a. BAUD (USART Baud Rate Register) 95 | (USART1).CTRLC = BREAKFORMAT; // Set USART mode of operation 96 | (USART1).CTRLB = USART_TXEN_bm; // Enable transmitter only, normal speed 97 | pinMode(PIN_WIRE_HWSERIAL1_TX, OUTPUT); // is required again after disabling UART 98 | (USART1).STATUS = USART_TXCIF_bm; // clear transmit complete flag 99 | (USART1).CTRLA = USART_TXCIE_bm; // enable transmit complete interrupt 100 | 101 | } else if (mode == DMXUARTMode::TDATA) { 102 | // switch to dmx data mode 103 | (USART1).CTRLA = 0; // disable all interrupts 104 | (USART1).BAUD = (int16_t)_DMX_dmxDivider; // assign the baud_divider, a.k.a. BAUD (USART Baud Rate Register) 105 | (USART1).CTRLC = DMXFORMAT; // send with 2 stop bits for compatibility 106 | (USART1).CTRLA = USART_DREIE_bm; // enable data register empty interrupt 107 | 108 | } else if (mode == DMXUARTMode::TDONE) { 109 | (USART1).BAUD = (int16_t)_DMX_dmxDivider; // assign the baud_divider, a.k.a. BAUD (USART Baud Rate Register) 110 | (USART1).CTRLC = DMXFORMAT; // send with 2 stop bits for compatibility 111 | (USART1).STATUS = USART_TXCIF_bm; // clear transmit complete flag 112 | (USART1).CTRLA = USART_TXCIE_bm; // enable transmit complete interrupt 113 | } // if 114 | 115 | // enable interrupts again, restore SREG content 116 | SREG = oldSREG; 117 | } // _DMX_setMode() 118 | 119 | 120 | // flush all incomming data packets in the queue 121 | void _DMX_flush() 122 | { 123 | uint8_t voiddata = (USART1).RXDATAL; 124 | } 125 | 126 | 127 | // send the next byte after current byte was sent completely. 128 | inline void _DMX_writeByte(uint8_t data) 129 | { 130 | // putting data into TXDATAL sends the data 131 | (USART1).TXDATAL = data; 132 | } // _DMX_writeByte 133 | 134 | 135 | // This Interrupt Service Routine is called when a byte or frame error was received. 136 | // In DMXController mode this interrupt is disabled and will not occur. 137 | // In DMXReceiver mode when a byte or frame error was received 138 | ISR(USART1_RXC_vect) 139 | { 140 | register8_t rxferr = (USART1).RXDATAH & USART_FERR_bm; 141 | register8_t rxdata = (USART1).RXDATAL; 142 | _DMXReceived(rxdata, rxferr); 143 | } // ISR(USART1_RXC_vect) 144 | 145 | 146 | // Interrupt service routines that are called when the actual byte was sent. 147 | ISR(USART1_TXC_vect) 148 | { 149 | _DMXTransmitted(); 150 | } // ISR(USART1_TXC_vect) 151 | 152 | 153 | // this interrupt occurs after data register was emptied by handing it over to the shift register. 154 | ISR(USART1_DRE_vect) 155 | { 156 | _DMXTransmitted(); 157 | } // ISR(USART1_DRE_vect) 158 | 159 | #endif 160 | 161 | #endif 162 | --------------------------------------------------------------------------------