Commit 1f7a20c1 authored by Eric Duminil's avatar Eric Duminil
Browse files

Merge branch 'develop'

parents da1f41c0 34496cfe
...@@ -10,7 +10,7 @@ The room should be ventilated as soon as one LED turns red. ...@@ -10,7 +10,7 @@ The room should be ventilated as soon as one LED turns red.
The *CO<sub>2</sub> Ampel* can: The *CO<sub>2</sub> Ampel* can:
* Display CO2 concentration on LED ring. * Display CO<sub>2</sub> concentration on LED ring.
* Allow calibration. * Allow calibration.
* Get current time over [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol) * Get current time over [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol)
* Send data over [MQTT](https://en.wikipedia.org/wiki/MQTT). * Send data over [MQTT](https://en.wikipedia.org/wiki/MQTT).
...@@ -76,15 +76,15 @@ In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Ente ...@@ -76,15 +76,15 @@ In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Ente
* `calibrate` (Starts calibration process). * `calibrate` (Starts calibration process).
* `calibrate 600` (Starts calibration process, to given ppm). * `calibrate 600` (Starts calibration process, to given ppm).
* `calibrate! 600` (Calibrates right now, to given ppm). * `calibrate! 600` (Calibrates right now, to given ppm).
* `co2 1500` (Sets co2 level, for debugging purposes). * `co2 1500` (Sets CO<sub>2</sub> level, for debugging purposes).
* `color 0xFF0015` (Shows color, specified as RGB, for debugging). * `color 0xFF0015` (Shows color, specified as RGB, for debugging).
* `csv 60` (Sets CSV writing interval, in s). * `csv 60` (Sets CSV writing interval, in s).
* `format_filesystem` (Deletes the whole filesystem). * `format_filesystem` (Deletes the whole filesystem).
* `free` (Displays available heap space). * `free` (Displays available heap space).
* `led 0/1` (Turns LEDs on/off).
* `local_ip` (Displays local IP and current SSID). * `local_ip` (Displays local IP and current SSID).
* `lora 300` (Sets LoRaWAN sending interval, in s). * `lora 300` (Sets LoRaWAN sending interval, in s).
* `mqtt 60` (Sets MQTT sending interval, in s). * `mqtt 60` (Sets MQTT sending interval, in s).
* `night_mode` (Toggles night mode on/off).
* `reset` (Restarts the ESP). * `reset` (Restarts the ESP).
* `reset_scd` (Resets SCD30). * `reset_scd` (Resets SCD30).
* `send_local_ip` (Sends local IP and SSID via MQTT. Can be useful to find sensor). * `send_local_ip` (Sends local IP and SSID via MQTT. Can be useful to find sensor).
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#endif #endif
#include "util.h" #include "util.h"
#include "ntp.h"
#include "sensor_console.h" #include "sensor_console.h"
#include "co2_sensor.h" #include "co2_sensor.h"
#include "led_effects.h" #include "led_effects.h"
......
...@@ -88,10 +88,7 @@ void setup() { ...@@ -88,10 +88,7 @@ void setup() {
#ifdef AMPEL_WIFI #ifdef AMPEL_WIFI
wifi::connect(ampel.sensorId); wifi::connect(ampel.sensorId);
Serial.print(F("WiFi - Status: ")); if (wifi::connected()) {
Serial.println(WiFi.status());
if (WiFi.status() == WL_CONNECTED) {
# ifdef AMPEL_HTTP # ifdef AMPEL_HTTP
web_server::initialize(); web_server::initialize();
# endif # endif
...@@ -203,7 +200,7 @@ void checkFlashButton() { ...@@ -203,7 +200,7 @@ void checkFlashButton() {
void keepServicesAlive() { void keepServicesAlive() {
#ifdef AMPEL_WIFI #ifdef AMPEL_WIFI
if (WiFi.status() == WL_CONNECTED) { if (wifi::connected()) {
# if defined(ESP8266) # if defined(ESP8266)
//NOTE: Sadly, there seems to be a bug in the current MDNS implementation. //NOTE: Sadly, there seems to be a bug in the current MDNS implementation.
// It stops working after 2 minutes. And forcing a restart leads to a memory leak. // It stops working after 2 minutes. And forcing a restart leads to a memory leak.
......
#include "co2_sensor.h" #include "co2_sensor.h"
#include "config.h"
#include "ntp.h"
#include "led_effects.h"
#include "sensor_console.h"
#include <Wire.h>
// The SCD30 from Sensirion is a high quality Nondispersive Infrared (NDIR) based CO₂ sensor capable of detecting 400 to 10000ppm with an accuracy of ±(30ppm+3%).
// https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
#include "src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h" // From: http://librarymanager/All#SparkFun_SCD30
namespace config { namespace config {
// UPPERCASE values should be defined in config.h // UPPERCASE values should be defined in config.h
uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor). uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor).
......
#ifndef CO2_SENSOR_H_ #ifndef CO2_SENSOR_H_
#define CO2_SENSOR_H_ #define CO2_SENSOR_H_
// The SCD30 from Sensirion is a high quality Nondispersive Infrared (NDIR) based CO₂ sensor capable of detecting 400 to 10000ppm with an accuracy of ±(30ppm+3%). #include <stdint.h> // For uint16_t
// https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
#include "src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h" // From: http://librarymanager/All#SparkFun_SCD30
#include "config.h"
#include "led_effects.h"
#include "util.h"
#include "sensor_console.h"
#include <Wire.h>
namespace config { namespace config {
extern uint16_t measurement_timestep; // [s] Value between 2 and 1800 (range for SCD30 sensor) extern uint16_t measurement_timestep; // [s] Value between 2 and 1800 (range for SCD30 sensor)
...@@ -18,7 +11,6 @@ namespace config { ...@@ -18,7 +11,6 @@ namespace config {
} }
namespace sensor { namespace sensor {
extern SCD30 scd30;
extern uint16_t co2; extern uint16_t co2;
extern float temperature; extern float temperature;
extern float humidity; extern float humidity;
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
# define MEASUREMENT_TIMESTEP 60 // [s] Value between 2 and 1800 (range for SCD30 sensor) # define MEASUREMENT_TIMESTEP 60 // [s] Value between 2 and 1800 (range for SCD30 sensor)
// How often should measurements be appended to CSV ? // How often should measurements be appended to CSV ?
// Probably a good idea to use a multiple of MEASUREMENT_TIMESTEP, so that averages can be calculated
// Set to 0 if you want to send values after each measurement // Set to 0 if you want to send values after each measurement
// WARNING: Writing too often might damage the ESP memory // WARNING: Writing too often might damage the ESP memory
# define CSV_INTERVAL 300 // [s] # define CSV_INTERVAL 300 // [s]
...@@ -54,7 +53,7 @@ ...@@ -54,7 +53,7 @@
// Should the sensor try to calibrate itself? // Should the sensor try to calibrate itself?
// Sensirion recommends 7 days of continuous readings with at least 1 hour a day of 'fresh air' for self-calibration to complete. // Sensirion recommends 7 days of continuous readings with at least 1 hour a day of 'fresh air' for self-calibration to complete.
# define AUTO_CALIBRATE_SENSOR true // [true / false] # define AUTO_CALIBRATE_SENSOR false // [true / false]
/** /**
* LEDs * LEDs
...@@ -106,12 +105,12 @@ ...@@ -106,12 +105,12 @@
# define ALLOW_MQTT_COMMANDS false # define ALLOW_MQTT_COMMANDS false
// How often should measurements be sent to MQTT server? // How often should measurements be sent to MQTT server?
// Probably a good idea to use a multiple of MEASUREMENT_TIMESTEP, so that averages can be calculated
// Set to 0 if you want to send values after each measurement // Set to 0 if you want to send values after each measurement
// # define MQTT_SENDING_INTERVAL MEASUREMENT_TIMESTEP * 5 // [s] // # define MQTT_SENDING_INTERVAL MEASUREMENT_TIMESTEP * 5 // [s]
# define MQTT_SENDING_INTERVAL 60 // [s] # define MQTT_SENDING_INTERVAL 60 // [s]
# define MQTT_SERVER "test.mosquitto.org" // MQTT server URL or IP address # define MQTT_SERVER "test.mosquitto.org" // MQTT server URL or IP address
# define MQTT_PORT 8883 # define MQTT_PORT 8883
# define MQTT_ENCRYPTED true // Set to false for unencrypted MQTT (e.g. with port 1883). If undefined, MQTT_ENCRYPTED will be set to true.
# define MQTT_USER "" # define MQTT_USER ""
# define MQTT_PASSWORD "" # define MQTT_PASSWORD ""
...@@ -120,10 +119,7 @@ ...@@ -120,10 +119,7 @@
*/ */
// 1) Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE". // 1) Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE".
// 2) Region and transceiver type should be specified in: // 2) If you need to, region and transceiver type can be specified in lorawan.cpp. Default is "Europe 868"
// * Arduino/libraries/MCCI_LoRaWAN_LMIC_library/project_config/lmic_project_config.h for Arduino IDE
// * platformio.ini for PlatformIO
// See https://github.com/mcci-catena/arduino-lmic#configuration for more information
// 3) It has been tested with "TTGO ESP32 SX1276 LoRa 868" and will only work with an ESP32 + LoRa modem // 3) It has been tested with "TTGO ESP32 SX1276 LoRa 868" and will only work with an ESP32 + LoRa modem
// 4) In order to use LoRaWAN, a gateway should be close to the co2ampel, and an account, an application and a device should be registered, // 4) In order to use LoRaWAN, a gateway should be close to the co2ampel, and an account, an application and a device should be registered,
// e.g. on https://www.thethingsnetwork.org/docs/applications/ // e.g. on https://www.thethingsnetwork.org/docs/applications/
......
#include "csv_writer.h" #include "csv_writer.h"
#include "config.h"
#include "ntp.h"
#include "led_effects.h"
#include "sensor_console.h"
namespace config { namespace config {
// Values should be defined in config.h // Values should be defined in config.h
uint16_t csv_interval = CSV_INTERVAL; // [s] uint16_t csv_interval = CSV_INTERVAL; // [s]
......
...@@ -11,11 +11,6 @@ ...@@ -11,11 +11,6 @@
# error Board should be either ESP8266 or ESP832 # error Board should be either ESP8266 or ESP832
#endif #endif
#include "config.h"
#include "util.h"
#include "led_effects.h"
#include "sensor_console.h"
namespace config { namespace config {
extern uint16_t csv_interval; // [s] extern uint16_t csv_interval; // [s]
} }
......
#include "led_effects.h" #include "led_effects.h"
#include "config.h"
#include "sensor_console.h"
// Adafruit NeoPixel (Arduino library for controlling single-wire-based LED pixels and strip)
// https://github.com/adafruit/Adafruit_NeoPixel
// Documentation : http://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html
#include "src/lib/Adafruit_NeoPixel/Adafruit_NeoPixel.h"
/***************************************************************** /*****************************************************************
* Configuration * * Configuration *
*****************************************************************/ *****************************************************************/
...@@ -12,7 +21,7 @@ namespace config { ...@@ -12,7 +21,7 @@ namespace config {
const uint8_t brightness_amplitude = config::max_brightness - config::min_brightness; const uint8_t brightness_amplitude = config::max_brightness - config::min_brightness;
const int kitt_tail = 3; // How many dimmer LEDs follow in K.I.T.T. wheel const int kitt_tail = 3; // How many dimmer LEDs follow in K.I.T.T. wheel
const uint16_t poor_air_quality_ppm = 1600; // Above this threshold, LED breathing effect is faster. const uint16_t poor_air_quality_ppm = 1600; // Above this threshold, LED breathing effect is faster.
bool night_mode = false; //NOTE: Use a class instead? NightMode could then be another state. bool display_led = true; // Will be set to false during "night mode".
#if !defined(LED_COUNT) #if !defined(LED_COUNT)
# define LED_COUNT 12 # define LED_COUNT 12
...@@ -78,7 +87,7 @@ namespace led_effects { ...@@ -78,7 +87,7 @@ namespace led_effects {
} }
void showColor(int32_t color) { void showColor(int32_t color) {
config::night_mode = true; // In order to avoid overwriting the desired color next time CO2 is displayed config::display_led = false; // In order to avoid overwriting the desired color next time CO2 is displayed
pixels.setBrightness(255); pixels.setBrightness(255);
pixels.fill(color); pixels.fill(color);
pixels.show(); pixels.show();
...@@ -88,17 +97,21 @@ namespace led_effects { ...@@ -88,17 +97,21 @@ namespace led_effects {
pixels.begin(); pixels.begin();
pixels.setBrightness(config::max_brightness); pixels.setBrightness(config::max_brightness);
LEDsOff(); LEDsOff();
sensor_console::defineCommand("night_mode", toggleNightMode, F("(Toggles night mode on/off)")); sensor_console::defineIntCommand("led", turnLEDsOnOff, F("0/1 (Turns LEDs on/off)"));
sensor_console::defineIntCommand("color", showColor, F("0xFF0015 (Shows color, specified as RGB, for debugging)")); sensor_console::defineIntCommand("color", showColor, F("0xFF0015 (Shows color, specified as RGB, for debugging)"));
} }
void toggleNightMode() { void toggleNightMode() {
config::night_mode = !config::night_mode; turnLEDsOnOff(!config::display_led);
if (config::night_mode) { }
Serial.println(F("NIGHT MODE!"));
LEDsOff(); void turnLEDsOnOff(int32_t display_led) {
config::display_led = display_led;
if (config::display_led) {
Serial.println(F("LEDs are on!"));
} else { } else {
Serial.println(F("DAY MODE!")); Serial.println(F("Night mode!"));
LEDsOff();
} }
} }
...@@ -106,7 +119,7 @@ namespace led_effects { ...@@ -106,7 +119,7 @@ namespace led_effects {
void showWaitingLED(uint32_t color) { void showWaitingLED(uint32_t color) {
using namespace config; using namespace config;
delay(80); delay(80);
if (night_mode) { if (!display_led) {
return; return;
} }
static uint16_t kitt_offset = 0; static uint16_t kitt_offset = 0;
...@@ -162,7 +175,7 @@ namespace led_effects { ...@@ -162,7 +175,7 @@ namespace led_effects {
* Fills the whole ring with green, yellow, orange or black, depending on co2 input and CO2_TICKS. * Fills the whole ring with green, yellow, orange or black, depending on co2 input and CO2_TICKS.
*/ */
void displayCO2color(uint16_t co2) { void displayCO2color(uint16_t co2) {
if (config::night_mode) { if (!config::display_led) {
return; return;
} }
pixels.setBrightness(config::max_brightness); pixels.setBrightness(config::max_brightness);
...@@ -177,7 +190,7 @@ namespace led_effects { ...@@ -177,7 +190,7 @@ namespace led_effects {
} }
void showRainbowWheel(uint16_t duration_ms) { void showRainbowWheel(uint16_t duration_ms) {
if (config::night_mode) { if (!config::display_led) {
return; return;
} }
static uint16_t wheel_offset = 0; static uint16_t wheel_offset = 0;
...@@ -195,7 +208,7 @@ namespace led_effects { ...@@ -195,7 +208,7 @@ namespace led_effects {
} }
void redAlert() { void redAlert() {
if (config::night_mode) { if (!config::display_led) {
onBoardLEDOn(); onBoardLEDOn();
delay(500); delay(500);
onBoardLEDOff(); onBoardLEDOff();
...@@ -218,7 +231,7 @@ namespace led_effects { ...@@ -218,7 +231,7 @@ namespace led_effects {
* been released or after every LED has been turned off. * been released or after every LED has been turned off.
*/ */
bool countdownToZero() { bool countdownToZero() {
if (config::night_mode) { if (!config::display_led) {
Serial.println(F("Night mode. Not doing anything.")); Serial.println(F("Night mode. Not doing anything."));
delay(1000); // Wait for a while, to avoid coming back to this function too many times when button is pressed. delay(1000); // Wait for a while, to avoid coming back to this function too many times when button is pressed.
return false; return false;
......
#ifndef LED_EFFECTS_H_INCLUDED #ifndef LED_EFFECTS_H_INCLUDED
#define LED_EFFECTS_H_INCLUDED #define LED_EFFECTS_H_INCLUDED
#include <Arduino.h>
#include "config.h"
#include "sensor_console.h"
// Adafruit NeoPixel (Arduino library for controlling single-wire-based LED pixels and strip) #include <stdint.h> // For uint32_t
// https://github.com/adafruit/Adafruit_NeoPixel
// Documentation : http://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html
#include "src/lib/Adafruit_NeoPixel/Adafruit_NeoPixel.h"
namespace color { namespace color {
const uint32_t red = 0xFF0000; const uint32_t red = 0xFF0000;
...@@ -22,6 +16,7 @@ namespace led_effects { ...@@ -22,6 +16,7 @@ namespace led_effects {
void onBoardLEDOff(); void onBoardLEDOff();
void onBoardLEDOn(); void onBoardLEDOn();
void toggleNightMode(); void toggleNightMode();
void turnLEDsOnOff(int32_t);
void LEDsOff(); void LEDsOff();
void setupRing(); void setupRing();
......
#include "lorawan.h" #include "lorawan.h"
#if defined(AMPEL_LORAWAN) && defined(ESP32) #if defined(AMPEL_LORAWAN) && defined(ESP32)
#include "led_effects.h"
#include "sensor_console.h"
#include "util.h"
#include "ntp.h"
/*** Define region and transceiver type, and ignore lmic_project_config.h from lmic library ***/
// Those values are probably okay if you're in Europe.
#define ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS
#define CFG_eu868 1
#define CFG_sx1276_radio 1
/****************************************************************************************/
// Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE"
// Tested successfully with v3.2.0 and connected to a thethingsnetwork.org app.
#include <lmic.h>
#include <SPI.h>
#include <hal/hal.h>
#include <arduino_lmic_hal_boards.h>
namespace config { namespace config {
#if defined(CFG_eu868)
const char *lorawan_frequency_plan = "Europe 868";
#elif defined(CFG_us915)
const char *lorawan_frequency_plan = "US 915";
#elif defined(CFG_au915)
const char *lorawan_frequency_plan = "Australia 915";
#elif defined(CFG_as923)
const char *lorawan_frequency_plan = "Asia 923";
#elif defined(CFG_kr920)
const char *lorawan_frequency_plan = "Korea 920";
#elif defined(CFG_in866)
const char *lorawan_frequency_plan = "India 866";
#else
# error "Region should be specified"
#endif
// Values should be defined in config.h // Values should be defined in config.h
uint16_t lorawan_sending_interval = LORAWAN_SENDING_INTERVAL; // [s] uint16_t lorawan_sending_interval = LORAWAN_SENDING_INTERVAL; // [s]
...@@ -36,7 +71,9 @@ namespace lorawan { ...@@ -36,7 +71,9 @@ namespace lorawan {
char last_transmission[23] = ""; char last_transmission[23] = "";
void initialize() { void initialize() {
Serial.println(F("Starting LoRaWAN. Frequency plan : " LMIC_FREQUENCY_PLAN " MHz.")); Serial.print(F("Starting LoRaWAN. Frequency plan : "));
Serial.print(config::lorawan_frequency_plan);
Serial.println(F(" MHz."));
// More info about pin mapping : https://github.com/mcci-catena/arduino-lmic#pin-mapping // More info about pin mapping : https://github.com/mcci-catena/arduino-lmic#pin-mapping
// Has been tested successfully with ESP32 TTGO LoRa32 V1, and might work with other ESP32+LoRa boards. // Has been tested successfully with ESP32 TTGO LoRa32 V1, and might work with other ESP32+LoRa boards.
...@@ -51,7 +88,7 @@ namespace lorawan { ...@@ -51,7 +88,7 @@ namespace lorawan {
} }
// Checks if OTAA is connected, or if payload should be sent. // Checks if OTAA is connected, or if payload should be sent.
// NOTE: while a transaction is in process (i.e. until the TXcomplete event has been received, no blocking code (e.g. delay loops etc.) are allowed, otherwise the LMIC/OS code might miss the event. // NOTE: while a transaction is in process (i.e. until the TXcomplete event has been received), no blocking code (e.g. delay loops etc.) are allowed, otherwise the LMIC/OS code might miss the event.
// If this rule is not followed, a typical symptom is that the first send is ok and all following ones end with the 'TX not complete' failure. // If this rule is not followed, a typical symptom is that the first send is ok and all following ones end with the 'TX not complete' failure.
void process() { void process() {
os_runloop_once(); os_runloop_once();
......
...@@ -3,39 +3,15 @@ ...@@ -3,39 +3,15 @@
#include "config.h" #include "config.h"
#if defined(AMPEL_LORAWAN) && defined(ESP32) # if defined(AMPEL_LORAWAN) && defined(ESP32)
#include <Arduino.h>
// Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE".
// Tested successfully with v3.2.0 and connected to a thethingsnetwork.org app.
#include <lmic.h>
#include <hal/hal.h>
#include <arduino_lmic_hal_boards.h>
#include <SPI.h>
#include "led_effects.h" #include <stdint.h> // For uint32_t & uint16_t
#include "sensor_console.h"
#include "util.h"
namespace config { namespace config {
extern uint16_t lorawan_sending_interval; // [s] extern uint16_t lorawan_sending_interval; // [s]
extern const char *lorawan_frequency_plan; // e.g. "Europe 868"
} }
#if defined(CFG_eu868)
# define LMIC_FREQUENCY_PLAN "Europe 868"
#elif defined(CFG_us915)
# define LMIC_FREQUENCY_PLAN "US 915"
#elif defined(CFG_au915)
# define LMIC_FREQUENCY_PLAN "Australia 915"
#elif defined(CFG_as923)
# define LMIC_FREQUENCY_PLAN "Asia 923"
#elif defined(CFG_kr920)
# define LMIC_FREQUENCY_PLAN "Korea 920"
#elif defined(CFG_in866)
# define LMIC_FREQUENCY_PLAN "India 866"
#else
# error "Region should be specified"
#endif
namespace lorawan { namespace lorawan {
extern bool waiting_for_confirmation; extern bool waiting_for_confirmation;
extern bool connected; extern bool connected;
...@@ -47,5 +23,5 @@ namespace lorawan { ...@@ -47,5 +23,5 @@ namespace lorawan {
void setLoRaInterval(int32_t sending_interval); void setLoRaInterval(int32_t sending_interval);
} }
#endif # endif
#endif #endif
#include "mqtt.h" #include "mqtt.h"
#include "config.h"
#include "led_effects.h"
#include "sensor_console.h"
#include "wifi_util.h"
#include "ntp.h"
#include "src/lib/PubSubClient/src/PubSubClient.h"
#if defined(ESP8266)
# include <ESP8266WiFi.h>
#elif defined(ESP32)
# include <WiFi.h>
#endif
namespace config { namespace config {
// Values should be defined in config.h // Values should be defined in config.h
uint16_t mqtt_sending_interval = MQTT_SENDING_INTERVAL; // [s] uint16_t mqtt_sending_interval = MQTT_SENDING_INTERVAL; // [s]
...@@ -12,10 +25,16 @@ namespace config { ...@@ -12,10 +25,16 @@ namespace config {
const bool allow_mqtt_commands = ALLOW_MQTT_COMMANDS; const bool allow_mqtt_commands = ALLOW_MQTT_COMMANDS;
const unsigned long wait_after_fail = 900; // [s] Wait 15 minutes after an MQTT connection fail, before trying again. const unsigned long wait_after_fail = 900; // [s] Wait 15 minutes after an MQTT connection fail, before trying again.
} }
#if defined(ESP32)
# include <WiFiClientSecure.h> #if MQTT_ENCRYPTED
#endif # if defined(ESP32)
# include <WiFiClientSecure.h>
# endif
WiFiClientSecure espClient; WiFiClientSecure espClient;
#else
WiFiClient espClient;
#endif
PubSubClient mqttClient(espClient); PubSubClient mqttClient(espClient);
namespace mqtt { namespace mqtt {
...@@ -30,9 +49,11 @@ namespace mqtt { ...@@ -30,9 +49,11 @@ namespace mqtt {
void initialize(const char *sensorId) { void initialize(const char *sensorId) {
json_sensor_format = PSTR("{\"time\":\"%s\", \"co2\":%d, \"temp\":%.1f, \"rh\":%.1f}"); json_sensor_format = PSTR("{\"time\":\"%s\", \"co2\":%d, \"temp\":%.1f, \"rh\":%.1f}");
snprintf(publish_topic, sizeof(publish_topic), "CO2sensors/%s", sensorId); snprintf(publish_topic, sizeof(publish_topic), "CO2sensors/%s", sensorId);
#if MQTT_ENCRYPTED
// The sensor doesn't check the fingerprint of the MQTT broker, because otherwise this fingerprint should be updated // The sensor doesn't check the fingerprint of the MQTT broker, because otherwise this fingerprint should be updated
// on the sensor every 3 months. The connection can still be encrypted, though: // on the sensor every 3 months. The connection can still be encrypted, though:
espClient.setInsecure(); // If not available for ESP32, please update Arduino IDE / PlatformIO espClient.setInsecure(); // If not available for ESP32, please update Arduino IDE / PlatformIO
#endif
mqttClient.setServer(config::mqtt_server, config::mqtt_port); mqttClient.setServer(config::mqtt_server, config::mqtt_port);
sensor_console::defineIntCommand("mqtt", setMQTTinterval, F("60 (Sets MQTT sending interval, in s)")); sensor_console::defineIntCommand("mqtt", setMQTTinterval, F("60 (Sets MQTT sending interval, in s)"));
...@@ -91,7 +112,13 @@ namespace mqtt { ...@@ -91,7 +112,13 @@ namespace mqtt {
// No WIFI // No WIFI
return; return;
} }
Serial.print(F("MQTT - Attempting connection..."));
Serial.print(F("MQTT - Attempting connection to "));
Serial.print(MQTT_SERVER);
Serial.print(MQTT_ENCRYPTED ? F(" (Encrypted") : F(" (Unencrypted"));
Serial.print(F(", port "));
Serial.print(MQTT_PORT);
Serial.print(F(") ..."));
led_effects::onBoardLEDOn(); led_effects::onBoardLEDOn();
// Wait for connection, at most 15s (default) // Wait for connection, at most 15s (default)
......
#ifndef MQTT_H_INCLUDED #ifndef MQTT_H_INCLUDED
#define MQTT_H_INCLUDED #define MQTT_H_INCLUDED
#include <Arduino.h> #include <stdint.h> // For uint32_t & uint16_t
#include "config.h" #include "config.h"
#include "led_effects.h" #if !defined(MQTT_ENCRYPTED)
#include "sensor_console.h" # define MQTT_ENCRYPTED true // Old config files might not define it, and encryption was on by default.
#include "src/lib/PubSubClient/src/PubSubClient.h" #endif
#include "wifi_util.h"
namespace config { namespace config {
extern uint16_t mqtt_sending_interval; // [s] extern uint16_t mqtt_sending_interval; // [s]
......
#include "ntp.h"
#include "sensor_console.h"
#include "config.h"
#include <WiFiUdp.h> // required for NTP
#include "src/lib/NTPClient/NTPClient.h" // NTP
namespace config {
const char *ntp_server = NTP_SERVER;
const long utc_offset_in_seconds = UTC_OFFSET_IN_SECONDS; // UTC+1
}
//NOTE: ESP32 sometimes couldn't access the NTP server, and every loop would take +1000ms
// ifdefs could be used to define functions specific to ESP32, e.g. with configTime
namespace ntp {
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, config::ntp_server, config::utc_offset_in_seconds, 60000UL);
bool connected_at_least_once = false;
void setLocalTime(int32_t unix_seconds);
void initialize() {
timeClient.begin();
sensor_console::defineIntCommand("set_time", ntp::setLocalTime, F("1618829570 (Sets time to the given UNIX time)"));
}
void update() {
connected_at_least_once |= timeClient.update();
}
void getLocalTime(char *timestamp) {
timeClient.getFormattedDate(timestamp);
}
void setLocalTime(int32_t unix_seconds) {
char time[23];
timeClient.getFormattedDate(time);
Serial.print(F("Current time : "));
Serial.println(time);
if (connected_at_least_once) {
Serial.println(F("NTP update already happened. Not changing anything."));
return;
}
Serial.print(F("Setting UNIX time to : "));
Serial.println(unix_seconds);
timeClient.setEpochTime(unix_seconds - seconds());
timeClient.getFormattedDate(time);
Serial.print(F("Current time : "));
Serial.println(time);
}
}
#ifndef AMPEL_TIME_H_INCLUDED
#define AMPEL_TIME_H_INCLUDED
namespace ntp {
void initialize();
void update();
void getLocalTime(char *timestamp);
}
//NOTE: Only use seconds() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over.
#define seconds() (millis() / 1000UL)
#endif
#ifndef SENSOR_CONSOLE_H_INCLUDED #ifndef SENSOR_CONSOLE_H_INCLUDED
#define SENSOR_CONSOLE_H_INCLUDED #define SENSOR_CONSOLE_H_INCLUDED
#include <Arduino.h> #include <Arduino.h> // For Flash strings, uint8_t and int32_t
/** Other scripts can use this namespace, in order to define commands, via callbacks. /** Other scripts can use this namespace, in order to define commands, via callbacks.
* Those callbacks can then be used to send commands to the sensor (reset, calibrate, night mode, ...) * Those callbacks can then be used to send commands to the sensor (reset, calibrate, led on/off, ...)
* The callbacks can either have no parameter, or one int32_t parameter. * The callbacks can either have no parameter, or one int32_t parameter.
*/ */
...@@ -12,7 +12,7 @@ namespace sensor_console { ...@@ -12,7 +12,7 @@ namespace sensor_console {
void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring); void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring);
void defineStringCommand(const char *name, void (*function)(char*), const __FlashStringHelper *doc_fstring); void defineStringCommand(const char *name, void (*function)(char*), const __FlashStringHelper *doc_fstring);
void processSerialInput(const byte in_byte); void processSerialInput(const uint8_t in_byte);
void execute(const char *command_line); void execute(const char *command_line);
} }
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#include "Adafruit_NeoPixel.h" #include "Adafruit_NeoPixel.h"
#if defined(TARGET_LPC1768) #if defined(TARGET_LPC1768)
#include <time.h> #include <time.h>
#endif #endif
#if defined(NRF52) || defined(NRF52_SERIES) #if defined(NRF52) || defined(NRF52_SERIES)
...@@ -57,6 +57,14 @@ ...@@ -57,6 +57,14 @@
//#define NRF52_DISABLE_INT //#define NRF52_DISABLE_INT
#endif #endif
#if defined(ARDUINO_ARCH_NRF52840)
#if defined __has_include
#if __has_include(<pinDefinitions.h>)
#include <pinDefinitions.h>
#endif
#endif
#endif
/*! /*!
@brief NeoPixel constructor when length, pin and pixel type are known @brief NeoPixel constructor when length, pin and pixel type are known
at compile-time. at compile-time.
...@@ -69,11 +77,21 @@ ...@@ -69,11 +77,21 @@
pixel. pixel.
@return Adafruit_NeoPixel object. Call the begin() function before use. @return Adafruit_NeoPixel object. Call the begin() function before use.
*/ */
Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) : Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, int16_t p, neoPixelType t)
begun(false), brightness(0), pixels(NULL), endTime(0) { : begun(false), brightness(0), pixels(NULL), endTime(0) {
updateType(t); updateType(t);
updateLength(n); updateLength(n);
setPin(p); setPin(p);
#if defined(ARDUINO_ARCH_RP2040)
// Find a free SM on one of the PIO's
sm = pio_claim_unused_sm(pio, false); // don't panic
// Try pio1 if SM not found
if (sm < 0) {
pio = pio1;
sm = pio_claim_unused_sm(pio, true); // panic if no SM is free
}
init = true;
#endif
} }
/*! /*!
...@@ -86,12 +104,13 @@ Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) : ...@@ -86,12 +104,13 @@ Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) :
'new' keyword with the first constructor syntax (length, pin, 'new' keyword with the first constructor syntax (length, pin,
type). type).
*/ */
Adafruit_NeoPixel::Adafruit_NeoPixel() : Adafruit_NeoPixel::Adafruit_NeoPixel()
:
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
is800KHz(true), is800KHz(true),
#endif #endif
begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL), begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0),
rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { pixels(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) {
} }
/*! /*!
...@@ -99,14 +118,15 @@ Adafruit_NeoPixel::Adafruit_NeoPixel() : ...@@ -99,14 +118,15 @@ Adafruit_NeoPixel::Adafruit_NeoPixel() :
*/ */
Adafruit_NeoPixel::~Adafruit_NeoPixel() { Adafruit_NeoPixel::~Adafruit_NeoPixel() {
free(pixels); free(pixels);
if(pin >= 0) pinMode(pin, INPUT); if (pin >= 0)
pinMode(pin, INPUT);
} }
/*! /*!
@brief Configure NeoPixel pin for output. @brief Configure NeoPixel pin for output.
*/ */
void Adafruit_NeoPixel::begin(void) { void Adafruit_NeoPixel::begin(void) {
if(pin >= 0) { if (pin >= 0) {
pinMode(pin, OUTPUT); pinMode(pin, OUTPUT);
digitalWrite(pin, LOW); digitalWrite(pin, LOW);
} }
...@@ -128,7 +148,7 @@ void Adafruit_NeoPixel::updateLength(uint16_t n) { ...@@ -128,7 +148,7 @@ void Adafruit_NeoPixel::updateLength(uint16_t n) {
// Allocate new data -- note: ALL PIXELS ARE CLEARED // Allocate new data -- note: ALL PIXELS ARE CLEARED
numBytes = n * ((wOffset == rOffset) ? 3 : 4); numBytes = n * ((wOffset == rOffset) ? 3 : 4);
if((pixels = (uint8_t *)malloc(numBytes))) { if ((pixels = (uint8_t *)malloc(numBytes))) {
memset(pixels, 0, numBytes); memset(pixels, 0, numBytes);
numLEDs = n; numLEDs = n;
} else { } else {
...@@ -159,36 +179,71 @@ void Adafruit_NeoPixel::updateType(neoPixelType t) { ...@@ -159,36 +179,71 @@ void Adafruit_NeoPixel::updateType(neoPixelType t) {
wOffset = (t >> 6) & 0b11; // See notes in header file wOffset = (t >> 6) & 0b11; // See notes in header file
rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets
gOffset = (t >> 2) & 0b11; gOffset = (t >> 2) & 0b11;
bOffset = t & 0b11; bOffset = t & 0b11;
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
is800KHz = (t < 256); // 400 KHz flag is 1<<8 is800KHz = (t < 256); // 400 KHz flag is 1<<8
#endif #endif
// If bytes-per-pixel has changed (and pixel data was previously // If bytes-per-pixel has changed (and pixel data was previously
// allocated), re-allocate to new size. Will clear any data. // allocated), re-allocate to new size. Will clear any data.
if(pixels) { if (pixels) {
bool newThreeBytesPerPixel = (wOffset == rOffset); bool newThreeBytesPerPixel = (wOffset == rOffset);
if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs); if (newThreeBytesPerPixel != oldThreeBytesPerPixel)
updateLength(numLEDs);
}
}
// RP2040 specific driver
#if defined(ARDUINO_ARCH_RP2040)
void Adafruit_NeoPixel::rp2040Init(uint8_t pin, bool is800KHz)
{
uint offset = pio_add_program(pio, &ws2812_program);
if (is800KHz)
{
// 800kHz, 8 bit transfers
ws2812_program_init(pio, sm, offset, pin, 800000, 8);
}
else
{
// 400kHz, 8 bit transfers
ws2812_program_init(pio, sm, offset, pin, 400000, 8);
} }
} }
// Not a user API
void Adafruit_NeoPixel::rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz)
{
if (this->init)
{
// On first pass through initialise the PIO
rp2040Init(pin, is800KHz);
this->init = false;
}
while(numBytes--)
// Bits for transmission must be shifted to top 8 bits
pio_sm_put_blocking(pio, sm, ((uint32_t)*pixels++)<< 24);
}
#endif
#if defined(ESP8266) #if defined(ESP8266)
// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution
extern "C" void ICACHE_RAM_ATTR espShow( extern "C" IRAM_ATTR void espShow(uint16_t pin, uint8_t *pixels,
uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); uint32_t numBytes, uint8_t type);
#elif defined(ESP32) #elif defined(ESP32)
extern "C" void espShow( extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint32_t numBytes,
uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); uint8_t type);
#endif // ESP8266 #endif // ESP8266
#if defined(K210) #if defined(K210)
#define KENDRYTE_K210 1 #define KENDRYTE_K210 1
#endif #endif
#if defined(KENDRYTE_K210) #if defined(KENDRYTE_K210)
extern "C" void k210Show( extern "C" void k210Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes,
uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz); boolean is800KHz);
#endif //KENDRYTE_K210 #endif // KENDRYTE_K210
/*! /*!
@brief Transmit pixel data in RAM to NeoPixels. @brief Transmit pixel data in RAM to NeoPixels.
@note On most architectures, interrupts are temporarily disabled in @note On most architectures, interrupts are temporarily disabled in
...@@ -202,7 +257,8 @@ extern "C" void k210Show( ...@@ -202,7 +257,8 @@ extern "C" void k210Show(
*/ */
void Adafruit_NeoPixel::show(void) { void Adafruit_NeoPixel::show(void) {
if(!pixels) return; if (!pixels)
return;
// Data latch = 300+ microsecond pause in the output stream. Rather than // Data latch = 300+ microsecond pause in the output stream. Rather than
// put a delay at the end of the function, the ending time is noted and // put a delay at the end of the function, the ending time is noted and
...@@ -210,36 +266,35 @@ void Adafruit_NeoPixel::show(void) { ...@@ -210,36 +266,35 @@ void Adafruit_NeoPixel::show(void) {
// subsequent round of data until the latch time has elapsed. This // subsequent round of data until the latch time has elapsed. This
// allows the mainline code to start generating the next frame of data // allows the mainline code to start generating the next frame of data
// rather than stalling for the latch. // rather than stalling for the latch.
while(!canShow()); while (!canShow())
// endTime is a private member (rather than global var) so that multiple ;
// instances on different pins can be quickly issued in succession (each // endTime is a private member (rather than global var) so that multiple
// instance doesn't delay the next). // instances on different pins can be quickly issued in succession (each
// instance doesn't delay the next).
// In order to make this code runtime-configurable to work with any pin,
// SBI/CBI instructions are eschewed in favor of full PORT writes via the // In order to make this code runtime-configurable to work with any pin,
// OUT or ST instructions. It relies on two facts: that peripheral // SBI/CBI instructions are eschewed in favor of full PORT writes via the
// functions (such as PWM) take precedence on output pins, so our PORT- // OUT or ST instructions. It relies on two facts: that peripheral
// wide writes won't interfere, and that interrupts are globally disabled // functions (such as PWM) take precedence on output pins, so our PORT-
// while data is being issued to the LEDs, so no other code will be // wide writes won't interfere, and that interrupts are globally disabled
// accessing the PORT. The code takes an initial 'snapshot' of the PORT // while data is being issued to the LEDs, so no other code will be
// state, computes 'pin high' and 'pin low' values, and writes these back // accessing the PORT. The code takes an initial 'snapshot' of the PORT
// to the PORT register as needed. // state, computes 'pin high' and 'pin low' values, and writes these back
// to the PORT register as needed.
// NRF52 may use PWM + DMA (if available), may not need to disable interrupt
#if !( defined(NRF52) || defined(NRF52_SERIES) ) // NRF52 may use PWM + DMA (if available), may not need to disable interrupt
#if !(defined(NRF52) || defined(NRF52_SERIES))
noInterrupts(); // Need 100% focus on instruction timing noInterrupts(); // Need 100% focus on instruction timing
#endif #endif
#if defined(__AVR__) #if defined(__AVR__)
// AVR MCUs -- ATmega & ATtiny (no XMEGA) --------------------------------- // AVR MCUs -- ATmega & ATtiny (no XMEGA) ---------------------------------
volatile uint16_t volatile uint16_t i = numBytes; // Loop counter
i = numBytes; // Loop counter volatile uint8_t *ptr = pixels, // Pointer to next byte
volatile uint8_t b = *ptr++, // Current byte value
*ptr = pixels, // Pointer to next byte hi, // PORT w/output bit set high
b = *ptr++, // Current byte value lo; // PORT w/output bit set low
hi, // PORT w/output bit set high
lo; // PORT w/output bit set low
// Hand-tuned assembly code issues data to the LED drivers at a specific // Hand-tuned assembly code issues data to the LED drivers at a specific
// rate. There's separate code for different CPU speeds (8, 12, 16 MHz) // rate. There's separate code for different CPU speeds (8, 12, 16 MHz)
...@@ -259,10 +314,10 @@ void Adafruit_NeoPixel::show(void) { ...@@ -259,10 +314,10 @@ void Adafruit_NeoPixel::show(void) {
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) #if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL)
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
volatile uint8_t n1, n2 = 0; // First, next bits out volatile uint8_t n1, n2 = 0; // First, next bits out
// Squeezing an 800 KHz stream out of an 8 MHz chip requires code // Squeezing an 800 KHz stream out of an 8 MHz chip requires code
// specific to each PORT register. // specific to each PORT register.
...@@ -273,14 +328,15 @@ void Adafruit_NeoPixel::show(void) { ...@@ -273,14 +328,15 @@ void Adafruit_NeoPixel::show(void) {
// PORTD OUTPUT ---------------------------------------------------- // PORTD OUTPUT ----------------------------------------------------
#if defined(PORTD) #if defined(PORTD)
#if defined(PORTB) || defined(PORTC) || defined(PORTF) #if defined(PORTB) || defined(PORTC) || defined(PORTF)
if(port == &PORTD) { if (port == &PORTD) {
#endif // defined(PORTB/C/F) #endif // defined(PORTB/C/F)
hi = PORTD | pinMask; hi = PORTD | pinMask;
lo = PORTD & ~pinMask; lo = PORTD & ~pinMask;
n1 = lo; n1 = lo;
if(b & 0x80) n1 = hi; if (b & 0x80)
n1 = hi;
// Dirty trick: RJMPs proceeding to the next instruction are used // Dirty trick: RJMPs proceeding to the next instruction are used
// to delay two clock cycles in one instruction word (rather than // to delay two clock cycles in one instruction word (rather than
...@@ -289,360 +345,618 @@ void Adafruit_NeoPixel::show(void) { ...@@ -289,360 +345,618 @@ void Adafruit_NeoPixel::show(void) {
// relative branch. // relative branch.
asm volatile( asm volatile(
"headD:" "\n\t" // Clk Pseudocode "headD:"
// Bit 7: "\n\t" // Clk Pseudocode
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi // Bit 7:
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1 "\n\t" // 1 PORT = hi
"rjmp .+0" "\n\t" // 2 nop nop "mov %[n2] , %[lo]"
"sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40) "\n\t" // 1 n2 = lo
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "\n\t" // 1 PORT = n1
"rjmp .+0" "\n\t" // 2 nop nop "rjmp .+0"
// Bit 6: "\n\t" // 2 nop nop
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "sbrc %[byte] , 6"
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo "\n\t" // 1-2 if(b & 0x40)
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2 "mov %[n2] , %[hi]"
"rjmp .+0" "\n\t" // 2 nop nop "\n\t" // 0-1 n2 = hi
"sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20) "out %[port] , %[lo]"
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi "\n\t" // 1 PORT = lo
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop "\n\t" // 2 nop nop
// Bit 5: // Bit 6:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "out %[port] , %[hi]"
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo "\n\t" // 1 PORT = hi
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1 "mov %[n1] , %[lo]"
"rjmp .+0" "\n\t" // 2 nop nop "\n\t" // 1 n1 = lo
"sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10) "out %[port] , %[n2]"
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi "\n\t" // 1 PORT = n2
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop "\n\t" // 2 nop nop
// Bit 4: "sbrc %[byte] , 5"
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "\n\t" // 1-2 if(b & 0x20)
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo "mov %[n1] , %[hi]"
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2 "\n\t" // 0-1 n1 = hi
"rjmp .+0" "\n\t" // 2 nop nop "out %[port] , %[lo]"
"sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08) "\n\t" // 1 PORT = lo
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi "rjmp .+0"
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "\n\t" // 2 nop nop
"rjmp .+0" "\n\t" // 2 nop nop // Bit 5:
// Bit 3: "out %[port] , %[hi]"
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "\n\t" // 1 PORT = hi
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo "mov %[n2] , %[lo]"
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1 "\n\t" // 1 n2 = lo
"rjmp .+0" "\n\t" // 2 nop nop "out %[port] , %[n1]"
"sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04) "\n\t" // 1 PORT = n1
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi "rjmp .+0"
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "\n\t" // 2 nop nop
"rjmp .+0" "\n\t" // 2 nop nop "sbrc %[byte] , 4"
// Bit 2: "\n\t" // 1-2 if(b & 0x10)
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "mov %[n2] , %[hi]"
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo "\n\t" // 0-1 n2 = hi
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2 "out %[port] , %[lo]"
"rjmp .+0" "\n\t" // 2 nop nop "\n\t" // 1 PORT = lo
"sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02) "rjmp .+0"
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi "\n\t" // 2 nop nop
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo // Bit 4:
"rjmp .+0" "\n\t" // 2 nop nop "out %[port] , %[hi]"
// Bit 1: "\n\t" // 1 PORT = hi
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "mov %[n1] , %[lo]"
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo "\n\t" // 1 n1 = lo
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1 "out %[port] , %[n2]"
"rjmp .+0" "\n\t" // 2 nop nop "\n\t" // 1 PORT = n2
"sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01) "rjmp .+0"
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi "\n\t" // 2 nop nop
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "sbrc %[byte] , 3"
"sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet) "\n\t" // 1-2 if(b & 0x08)
// Bit 0: "mov %[n1] , %[hi]"
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi "\n\t" // 0-1 n1 = hi
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo "out %[port] , %[lo]"
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2 "\n\t" // 1 PORT = lo
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ "rjmp .+0"
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) "\n\t" // 2 nop nop
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi // Bit 3:
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo "out %[port] , %[hi]"
"brne headD" "\n" // 2 while(i) (Z flag set above) "\n\t" // 1 PORT = hi
: [byte] "+r" (b), "mov %[n2] , %[lo]"
[n1] "+r" (n1), "\n\t" // 1 n2 = lo
[n2] "+r" (n2), "out %[port] , %[n1]"
[count] "+w" (i) "\n\t" // 1 PORT = n1
: [port] "I" (_SFR_IO_ADDR(PORTD)), "rjmp .+0"
[ptr] "e" (ptr), "\n\t" // 2 nop nop
[hi] "r" (hi), "sbrc %[byte] , 2"
[lo] "r" (lo)); "\n\t" // 1-2 if(b & 0x04)
"mov %[n2] , %[hi]"
#if defined(PORTB) || defined(PORTC) || defined(PORTF) "\n\t" // 0-1 n2 = hi
"out %[port] , %[lo]"
"\n\t" // 1 PORT = lo
"rjmp .+0"
"\n\t" // 2 nop nop
// Bit 2:
"out %[port] , %[hi]"
"\n\t" // 1 PORT = hi
"mov %[n1] , %[lo]"
"\n\t" // 1 n1 = lo
"out %[port] , %[n2]"
"\n\t" // 1 PORT = n2
"rjmp .+0"
"\n\t" // 2 nop nop
"sbrc %[byte] , 1"
"\n\t" // 1-2 if(b & 0x02)
"mov %[n1] , %[hi]"
"\n\t" // 0-1 n1 = hi
"out %[port] , %[lo]"
"\n\t" // 1 PORT = lo
"rjmp .+0"
"\n\t" // 2 nop nop
// Bit 1:
"out %[port] , %[hi]"
"\n\t" // 1 PORT = hi
"mov %[n2] , %[lo]"
"\n\t" // 1 n2 = lo
"out %[port] , %[n1]"
"\n\t" // 1 PORT = n1
"rjmp .+0"
"\n\t" // 2 nop nop
"sbrc %[byte] , 0"
"\n\t" // 1-2 if(b & 0x01)
"mov %[n2] , %[hi]"
"\n\t" // 0-1 n2 = hi
"out %[port] , %[lo]"
"\n\t" // 1 PORT = lo
"sbiw %[count], 1"
"\n\t" // 2 i-- (don't act on Z flag yet)
// Bit 0:
"out %[port] , %[hi]"
"\n\t" // 1 PORT = hi
"mov %[n1] , %[lo]"
"\n\t" // 1 n1 = lo
"out %[port] , %[n2]"
"\n\t" // 1 PORT = n2
"ld %[byte] , %a[ptr]+"
"\n\t" // 2 b = *ptr++
"sbrc %[byte] , 7"
"\n\t" // 1-2 if(b & 0x80)
"mov %[n1] , %[hi]"
"\n\t" // 0-1 n1 = hi
"out %[port] , %[lo]"
"\n\t" // 1 PORT = lo
"brne headD"
"\n" // 2 while(i) (Z flag set above)
: [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTD)), [ptr] "e"(ptr), [hi] "r"(hi),
[lo] "r"(lo));
#if defined(PORTB) || defined(PORTC) || defined(PORTF)
} else // other PORT(s) } else // other PORT(s)
#endif // defined(PORTB/C/F) #endif // defined(PORTB/C/F)
#endif // defined(PORTD) #endif // defined(PORTD)
// PORTB OUTPUT ---------------------------------------------------- // PORTB OUTPUT ----------------------------------------------------
#if defined(PORTB) #if defined(PORTB)
#if defined(PORTD) || defined(PORTC) || defined(PORTF) #if defined(PORTD) || defined(PORTC) || defined(PORTF)
if(port == &PORTB) { if (port == &PORTB) {
#endif // defined(PORTD/C/F) #endif // defined(PORTD/C/F)
// Same as above, just switched to PORTB and stripped of comments. // Same as above, just switched to PORTB and stripped of comments.
hi = PORTB | pinMask; hi = PORTB | pinMask;
lo = PORTB & ~pinMask; lo = PORTB & ~pinMask;
n1 = lo; n1 = lo;
if(b & 0x80) n1 = hi; if (b & 0x80)
n1 = hi;
asm volatile( asm volatile(
"headB:" "\n\t" "headB:"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[lo]"
"sbrc %[byte] , 6" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 6"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[hi]"
"sbrc %[byte] , 5" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[lo]"
"sbrc %[byte] , 4" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n2]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 5"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[hi]"
"sbrc %[byte] , 3" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[lo]"
"sbrc %[byte] , 2" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 4"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[hi]"
"sbrc %[byte] , 1" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[lo]"
"sbrc %[byte] , 0" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n2]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"sbiw %[count], 1" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 3"
"out %[port] , %[n2]" "\n\t" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t" "mov %[n1] , %[hi]"
"sbrc %[byte] , 7" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"brne headB" "\n" "rjmp .+0"
: [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) "\n\t"
: [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), "out %[port] , %[hi]"
[lo] "r" (lo)); "\n\t"
"mov %[n2] , %[lo]"
#if defined(PORTD) || defined(PORTC) || defined(PORTF) "\n\t"
"out %[port] , %[n1]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 2"
"\n\t"
"mov %[n2] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"rjmp .+0"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n1] , %[lo]"
"\n\t"
"out %[port] , %[n2]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 1"
"\n\t"
"mov %[n1] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"rjmp .+0"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n2] , %[lo]"
"\n\t"
"out %[port] , %[n1]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 0"
"\n\t"
"mov %[n2] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"sbiw %[count], 1"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n1] , %[lo]"
"\n\t"
"out %[port] , %[n2]"
"\n\t"
"ld %[byte] , %a[ptr]+"
"\n\t"
"sbrc %[byte] , 7"
"\n\t"
"mov %[n1] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"brne headB"
"\n"
: [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTB)), [ptr] "e"(ptr), [hi] "r"(hi),
[lo] "r"(lo));
#if defined(PORTD) || defined(PORTC) || defined(PORTF)
} }
#endif #endif
#if defined(PORTC) || defined(PORTF) #if defined(PORTC) || defined(PORTF)
else else
#endif // defined(PORTC/F) #endif // defined(PORTC/F)
#endif // defined(PORTB) #endif // defined(PORTB)
// PORTC OUTPUT ---------------------------------------------------- // PORTC OUTPUT ----------------------------------------------------
#if defined(PORTC) #if defined(PORTC)
#if defined(PORTD) || defined(PORTB) || defined(PORTF) #if defined(PORTD) || defined(PORTB) || defined(PORTF)
if(port == &PORTC) { if (port == &PORTC) {
#endif // defined(PORTD/B/F) #endif // defined(PORTD/B/F)
// Same as above, just switched to PORTC and stripped of comments. // Same as above, just switched to PORTC and stripped of comments.
hi = PORTC | pinMask; hi = PORTC | pinMask;
lo = PORTC & ~pinMask; lo = PORTC & ~pinMask;
n1 = lo; n1 = lo;
if(b & 0x80) n1 = hi; if (b & 0x80)
n1 = hi;
asm volatile( asm volatile(
"headC:" "\n\t" "headC:"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[lo]"
"sbrc %[byte] , 6" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 6"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[hi]"
"sbrc %[byte] , 5" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[lo]"
"sbrc %[byte] , 4" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n2]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 5"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[hi]"
"sbrc %[byte] , 3" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[lo]"
"sbrc %[byte] , 2" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 4"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[hi]"
"sbrc %[byte] , 1" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[lo]"
"sbrc %[byte] , 0" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n2]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"sbiw %[count], 1" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 3"
"out %[port] , %[n2]" "\n\t" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t" "mov %[n1] , %[hi]"
"sbrc %[byte] , 7" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"brne headC" "\n" "rjmp .+0"
: [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) "\n\t"
: [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), "out %[port] , %[hi]"
[lo] "r" (lo)); "\n\t"
"mov %[n2] , %[lo]"
#if defined(PORTD) || defined(PORTB) || defined(PORTF) "\n\t"
"out %[port] , %[n1]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 2"
"\n\t"
"mov %[n2] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"rjmp .+0"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n1] , %[lo]"
"\n\t"
"out %[port] , %[n2]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 1"
"\n\t"
"mov %[n1] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"rjmp .+0"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n2] , %[lo]"
"\n\t"
"out %[port] , %[n1]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 0"
"\n\t"
"mov %[n2] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"sbiw %[count], 1"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n1] , %[lo]"
"\n\t"
"out %[port] , %[n2]"
"\n\t"
"ld %[byte] , %a[ptr]+"
"\n\t"
"sbrc %[byte] , 7"
"\n\t"
"mov %[n1] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"brne headC"
"\n"
: [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTC)), [ptr] "e"(ptr), [hi] "r"(hi),
[lo] "r"(lo));
#if defined(PORTD) || defined(PORTB) || defined(PORTF)
} }
#endif // defined(PORTD/B/F) #endif // defined(PORTD/B/F)
#if defined(PORTF) #if defined(PORTF)
else else
#endif #endif
#endif // defined(PORTC) #endif // defined(PORTC)
// PORTF OUTPUT ---------------------------------------------------- // PORTF OUTPUT ----------------------------------------------------
#if defined(PORTF) #if defined(PORTF)
#if defined(PORTD) || defined(PORTB) || defined(PORTC) #if defined(PORTD) || defined(PORTB) || defined(PORTC)
if(port == &PORTF) { if (port == &PORTF) {
#endif // defined(PORTD/B/C) #endif // defined(PORTD/B/C)
hi = PORTF | pinMask; hi = PORTF | pinMask;
lo = PORTF & ~pinMask; lo = PORTF & ~pinMask;
n1 = lo; n1 = lo;
if(b & 0x80) n1 = hi; if (b & 0x80)
n1 = hi;
asm volatile( asm volatile(
"headF:" "\n\t" "headF:"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[lo]"
"sbrc %[byte] , 6" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 6"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[hi]"
"sbrc %[byte] , 5" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[lo]"
"sbrc %[byte] , 4" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n2]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 5"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[hi]"
"sbrc %[byte] , 3" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[lo]"
"sbrc %[byte] , 2" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n1]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 4"
"out %[port] , %[n2]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n2] , %[hi]"
"sbrc %[byte] , 1" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n2] , %[lo]" "\n\t" "out %[port] , %[hi]"
"out %[port] , %[n1]" "\n\t" "\n\t"
"rjmp .+0" "\n\t" "mov %[n1] , %[lo]"
"sbrc %[byte] , 0" "\n\t" "\n\t"
"mov %[n2] , %[hi]" "\n\t" "out %[port] , %[n2]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"sbiw %[count], 1" "\n\t" "rjmp .+0"
"out %[port] , %[hi]" "\n\t" "\n\t"
"mov %[n1] , %[lo]" "\n\t" "sbrc %[byte] , 3"
"out %[port] , %[n2]" "\n\t" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t" "mov %[n1] , %[hi]"
"sbrc %[byte] , 7" "\n\t" "\n\t"
"mov %[n1] , %[hi]" "\n\t" "out %[port] , %[lo]"
"out %[port] , %[lo]" "\n\t" "\n\t"
"brne headF" "\n" "rjmp .+0"
: [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) "\n\t"
: [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), "out %[port] , %[hi]"
[lo] "r" (lo)); "\n\t"
"mov %[n2] , %[lo]"
#if defined(PORTD) || defined(PORTB) || defined(PORTC) "\n\t"
"out %[port] , %[n1]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 2"
"\n\t"
"mov %[n2] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"rjmp .+0"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n1] , %[lo]"
"\n\t"
"out %[port] , %[n2]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 1"
"\n\t"
"mov %[n1] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"rjmp .+0"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n2] , %[lo]"
"\n\t"
"out %[port] , %[n1]"
"\n\t"
"rjmp .+0"
"\n\t"
"sbrc %[byte] , 0"
"\n\t"
"mov %[n2] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"sbiw %[count], 1"
"\n\t"
"out %[port] , %[hi]"
"\n\t"
"mov %[n1] , %[lo]"
"\n\t"
"out %[port] , %[n2]"
"\n\t"
"ld %[byte] , %a[ptr]+"
"\n\t"
"sbrc %[byte] , 7"
"\n\t"
"mov %[n1] , %[hi]"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"brne headF"
"\n"
: [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTF)), [ptr] "e"(ptr), [hi] "r"(hi),
[lo] "r"(lo));
#if defined(PORTD) || defined(PORTB) || defined(PORTC)
} }
#endif // defined(PORTD/B/C) #endif // defined(PORTD/B/C)
#endif // defined(PORTF) #endif // defined(PORTF)
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
...@@ -660,41 +974,56 @@ void Adafruit_NeoPixel::show(void) { ...@@ -660,41 +974,56 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit; volatile uint8_t next, bit;
hi = *port | pinMask; hi = *port | pinMask;
lo = *port & ~pinMask; lo = *port & ~pinMask;
next = lo; next = lo;
bit = 8; bit = 8;
asm volatile( asm volatile("head20:"
"head20:" "\n\t" // Clk Pseudocode (T = 0) "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) "st %a[port], %[hi]"
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) "\n\t" // 2 PORT = hi (T = 2)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) "sbrc %[byte] , 7"
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6) "\n\t" // 1-2 if(b & 128)
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) "mov %[next], %[hi]"
"dec %[bit]" "\n\t" // 1 bit-- (T = 8) "\n\t" // 0-1 next = hi (T = 4)
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0) "st %a[port], %[next]"
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) "\n\t" // 2 PORT = next (T = 6)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) "mov %[next] , %[lo]"
"rjmp .+0" "\n\t" // 2 nop nop (T = 14) "\n\t" // 1 next = lo (T = 7)
"rjmp .+0" "\n\t" // 2 nop nop (T = 16) "dec %[bit]"
"rjmp .+0" "\n\t" // 2 nop nop (T = 18) "\n\t" // 1 bit-- (T = 8)
"rjmp head20" "\n\t" // 2 -> head20 (next bit out) "breq nextbyte20"
"nextbyte20:" "\n\t" // (T = 10) "\n\t" // 1-2 if(bit == 0)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) "rol %[byte]"
"nop" "\n\t" // 1 nop (T = 13) "\n\t" // 1 b <<= 1 (T = 10)
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14) "st %a[port], %[lo]"
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16) "\n\t" // 2 PORT = lo (T = 12)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) "rjmp .+0"
"brne head20" "\n" // 2 if(i != 0) -> (next byte) "\n\t" // 2 nop nop (T = 14)
: [port] "+e" (port), "rjmp .+0"
[byte] "+r" (b), "\n\t" // 2 nop nop (T = 16)
[bit] "+r" (bit), "rjmp .+0"
[next] "+r" (next), "\n\t" // 2 nop nop (T = 18)
[count] "+w" (i) "rjmp head20"
: [hi] "r" (hi), "\n\t" // 2 -> head20 (next bit out)
[lo] "r" (lo), "nextbyte20:"
[ptr] "e" (ptr)); "\n\t" // (T = 10)
"st %a[port], %[lo]"
"\n\t" // 2 PORT = lo (T = 12)
"nop"
"\n\t" // 1 nop (T = 13)
"ldi %[bit] , 8"
"\n\t" // 1 bit = 8 (T = 14)
"ld %[byte] , %a[ptr]+"
"\n\t" // 2 b = *ptr++ (T = 16)
"sbiw %[count], 1"
"\n\t" // 2 i-- (T = 18)
"brne head20"
"\n" // 2 if(i != 0) -> (next byte)
: [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
[next] "+r"(next), [count] "+w"(i)
: [hi] "r"(hi), [lo] "r"(lo), [ptr] "e"(ptr));
} }
#endif // NEO_KHZ400 #endif // NEO_KHZ400
...@@ -702,7 +1031,7 @@ void Adafruit_NeoPixel::show(void) { ...@@ -702,7 +1031,7 @@ void Adafruit_NeoPixel::show(void) {
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL)
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
// In the 12 MHz case, an optimized 800 KHz datastream (no dead time // In the 12 MHz case, an optimized 800 KHz datastream (no dead time
...@@ -717,253 +1046,397 @@ void Adafruit_NeoPixel::show(void) { ...@@ -717,253 +1046,397 @@ void Adafruit_NeoPixel::show(void) {
// PORTD OUTPUT ---------------------------------------------------- // PORTD OUTPUT ----------------------------------------------------
#if defined(PORTD) #if defined(PORTD)
#if defined(PORTB) || defined(PORTC) || defined(PORTF) #if defined(PORTB) || defined(PORTC) || defined(PORTF)
if(port == &PORTD) { if (port == &PORTD) {
#endif // defined(PORTB/C/F) #endif // defined(PORTB/C/F)
hi = PORTD | pinMask; hi = PORTD | pinMask;
lo = PORTD & ~pinMask; lo = PORTD & ~pinMask;
next = lo; next = lo;
if(b & 0x80) next = hi; if (b & 0x80)
next = hi;
// Don't "optimize" the OUT calls into the bitTime subroutine; // Don't "optimize" the OUT calls into the bitTime subroutine;
// we're exploiting the RCALL and RET as 3- and 4-cycle NOPs! // we're exploiting the RCALL and RET as 3- and 4-cycle NOPs!
asm volatile( asm volatile("headD:"
"headD:" "\n\t" // (T = 0) "\n\t" // (T = 0)
"out %[port], %[hi]" "\n\t" // (T = 1) "out %[port], %[hi]"
"rcall bitTimeD" "\n\t" // Bit 7 (T = 15) "\n\t" // (T = 1)
"out %[port], %[hi]" "\n\t" "rcall bitTimeD"
"rcall bitTimeD" "\n\t" // Bit 6 "\n\t" // Bit 7 (T = 15)
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeD" "\n\t" // Bit 5 "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeD"
"rcall bitTimeD" "\n\t" // Bit 4 "\n\t" // Bit 6
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeD" "\n\t" // Bit 3 "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeD"
"rcall bitTimeD" "\n\t" // Bit 2 "\n\t" // Bit 5
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeD" "\n\t" // Bit 1 "\n\t"
// Bit 0: "rcall bitTimeD"
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1) "\n\t" // Bit 4
"rjmp .+0" "\n\t" // 2 nop nop (T = 3) "out %[port], %[hi]"
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5) "\n\t"
"out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6) "rcall bitTimeD"
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) "\n\t" // Bit 3
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) "out %[port], %[hi]"
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9) "\n\t"
"nop" "\n\t" // 1 (T = 10) "rcall bitTimeD"
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11) "\n\t" // Bit 2
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 13) "out %[port], %[hi]"
"brne headD" "\n\t" // 2 if(i != 0) -> (next byte) "\n\t"
"rjmp doneD" "\n\t" "rcall bitTimeD"
"bitTimeD:" "\n\t" // nop nop nop (T = 4) "\n\t" // Bit 1
"out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5) // Bit 0:
"mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6) "out %[port] , %[hi]"
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7) "\n\t" // 1 PORT = hi (T = 1)
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) "rjmp .+0"
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9) "\n\t" // 2 nop nop (T = 3)
"nop" "\n\t" // 1 (T = 10) "ld %[byte] , %a[ptr]+"
"out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11) "\n\t" // 2 b = *ptr++ (T = 5)
"ret" "\n\t" // 4 nop nop nop nop (T = 15) "out %[port] , %[next]"
"doneD:" "\n" "\n\t" // 1 PORT = next (T = 6)
: [byte] "+r" (b), "mov %[next] , %[lo]"
[next] "+r" (next), "\n\t" // 1 next = lo (T = 7)
[count] "+w" (i) "sbrc %[byte] , 7"
: [port] "I" (_SFR_IO_ADDR(PORTD)), "\n\t" // 1-2 if(b & 0x80) (T = 8)
[ptr] "e" (ptr), "mov %[next] , %[hi]"
[hi] "r" (hi), "\n\t" // 0-1 next = hi (T = 9)
[lo] "r" (lo)); "nop"
"\n\t" // 1 (T = 10)
#if defined(PORTB) || defined(PORTC) || defined(PORTF) "out %[port] , %[lo]"
"\n\t" // 1 PORT = lo (T = 11)
"sbiw %[count], 1"
"\n\t" // 2 i-- (T = 13)
"brne headD"
"\n\t" // 2 if(i != 0) -> (next byte)
"rjmp doneD"
"\n\t"
"bitTimeD:"
"\n\t" // nop nop nop (T = 4)
"out %[port], %[next]"
"\n\t" // 1 PORT = next (T = 5)
"mov %[next], %[lo]"
"\n\t" // 1 next = lo (T = 6)
"rol %[byte]"
"\n\t" // 1 b <<= 1 (T = 7)
"sbrc %[byte], 7"
"\n\t" // 1-2 if(b & 0x80) (T = 8)
"mov %[next], %[hi]"
"\n\t" // 0-1 next = hi (T = 9)
"nop"
"\n\t" // 1 (T = 10)
"out %[port], %[lo]"
"\n\t" // 1 PORT = lo (T = 11)
"ret"
"\n\t" // 4 nop nop nop nop (T = 15)
"doneD:"
"\n"
: [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTD)), [ptr] "e"(ptr),
[hi] "r"(hi), [lo] "r"(lo));
#if defined(PORTB) || defined(PORTC) || defined(PORTF)
} else // other PORT(s) } else // other PORT(s)
#endif // defined(PORTB/C/F) #endif // defined(PORTB/C/F)
#endif // defined(PORTD) #endif // defined(PORTD)
// PORTB OUTPUT ---------------------------------------------------- // PORTB OUTPUT ----------------------------------------------------
#if defined(PORTB) #if defined(PORTB)
#if defined(PORTD) || defined(PORTC) || defined(PORTF) #if defined(PORTD) || defined(PORTC) || defined(PORTF)
if(port == &PORTB) { if (port == &PORTB) {
#endif // defined(PORTD/C/F) #endif // defined(PORTD/C/F)
hi = PORTB | pinMask; hi = PORTB | pinMask;
lo = PORTB & ~pinMask; lo = PORTB & ~pinMask;
next = lo; next = lo;
if(b & 0x80) next = hi; if (b & 0x80)
next = hi;
// Same as above, just set for PORTB & stripped of comments // Same as above, just set for PORTB & stripped of comments
asm volatile( asm volatile("headB:"
"headB:" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeB"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeB"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeB"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeB" "\n\t" "\n\t"
"out %[port] , %[hi]" "\n\t" "rcall bitTimeB"
"rjmp .+0" "\n\t" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t" "out %[port], %[hi]"
"out %[port] , %[next]" "\n\t" "\n\t"
"mov %[next] , %[lo]" "\n\t" "rcall bitTimeB"
"sbrc %[byte] , 7" "\n\t" "\n\t"
"mov %[next] , %[hi]" "\n\t" "out %[port], %[hi]"
"nop" "\n\t" "\n\t"
"out %[port] , %[lo]" "\n\t" "rcall bitTimeB"
"sbiw %[count], 1" "\n\t" "\n\t"
"brne headB" "\n\t" "out %[port], %[hi]"
"rjmp doneB" "\n\t" "\n\t"
"bitTimeB:" "\n\t" "rcall bitTimeB"
"out %[port], %[next]" "\n\t" "\n\t"
"mov %[next], %[lo]" "\n\t" "out %[port] , %[hi]"
"rol %[byte]" "\n\t" "\n\t"
"sbrc %[byte], 7" "\n\t" "rjmp .+0"
"mov %[next], %[hi]" "\n\t" "\n\t"
"nop" "\n\t" "ld %[byte] , %a[ptr]+"
"out %[port], %[lo]" "\n\t" "\n\t"
"ret" "\n\t" "out %[port] , %[next]"
"doneB:" "\n" "\n\t"
: [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) "mov %[next] , %[lo]"
: [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), "\n\t"
[lo] "r" (lo)); "sbrc %[byte] , 7"
"\n\t"
#if defined(PORTD) || defined(PORTC) || defined(PORTF) "mov %[next] , %[hi]"
"\n\t"
"nop"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"sbiw %[count], 1"
"\n\t"
"brne headB"
"\n\t"
"rjmp doneB"
"\n\t"
"bitTimeB:"
"\n\t"
"out %[port], %[next]"
"\n\t"
"mov %[next], %[lo]"
"\n\t"
"rol %[byte]"
"\n\t"
"sbrc %[byte], 7"
"\n\t"
"mov %[next], %[hi]"
"\n\t"
"nop"
"\n\t"
"out %[port], %[lo]"
"\n\t"
"ret"
"\n\t"
"doneB:"
"\n"
: [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTB)), [ptr] "e"(ptr),
[hi] "r"(hi), [lo] "r"(lo));
#if defined(PORTD) || defined(PORTC) || defined(PORTF)
} }
#endif #endif
#if defined(PORTC) || defined(PORTF) #if defined(PORTC) || defined(PORTF)
else else
#endif // defined(PORTC/F) #endif // defined(PORTC/F)
#endif // defined(PORTB) #endif // defined(PORTB)
// PORTC OUTPUT ---------------------------------------------------- // PORTC OUTPUT ----------------------------------------------------
#if defined(PORTC) #if defined(PORTC)
#if defined(PORTD) || defined(PORTB) || defined(PORTF) #if defined(PORTD) || defined(PORTB) || defined(PORTF)
if(port == &PORTC) { if (port == &PORTC) {
#endif // defined(PORTD/B/F) #endif // defined(PORTD/B/F)
hi = PORTC | pinMask; hi = PORTC | pinMask;
lo = PORTC & ~pinMask; lo = PORTC & ~pinMask;
next = lo; next = lo;
if(b & 0x80) next = hi; if (b & 0x80)
next = hi;
// Same as above, just set for PORTC & stripped of comments // Same as above, just set for PORTC & stripped of comments
asm volatile( asm volatile("headC:"
"headC:" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeC"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeC"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeC"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port] , %[hi]" "\n\t" "rcall bitTimeC"
"rjmp .+0" "\n\t" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t" "out %[port], %[hi]"
"out %[port] , %[next]" "\n\t" "\n\t"
"mov %[next] , %[lo]" "\n\t" "rcall bitTimeC"
"sbrc %[byte] , 7" "\n\t" "\n\t"
"mov %[next] , %[hi]" "\n\t" "out %[port], %[hi]"
"nop" "\n\t" "\n\t"
"out %[port] , %[lo]" "\n\t" "rcall bitTimeC"
"sbiw %[count], 1" "\n\t" "\n\t"
"brne headC" "\n\t" "out %[port], %[hi]"
"rjmp doneC" "\n\t" "\n\t"
"bitTimeC:" "\n\t" "rcall bitTimeC"
"out %[port], %[next]" "\n\t" "\n\t"
"mov %[next], %[lo]" "\n\t" "out %[port] , %[hi]"
"rol %[byte]" "\n\t" "\n\t"
"sbrc %[byte], 7" "\n\t" "rjmp .+0"
"mov %[next], %[hi]" "\n\t" "\n\t"
"nop" "\n\t" "ld %[byte] , %a[ptr]+"
"out %[port], %[lo]" "\n\t" "\n\t"
"ret" "\n\t" "out %[port] , %[next]"
"doneC:" "\n" "\n\t"
: [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) "mov %[next] , %[lo]"
: [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), "\n\t"
[lo] "r" (lo)); "sbrc %[byte] , 7"
"\n\t"
#if defined(PORTD) || defined(PORTB) || defined(PORTF) "mov %[next] , %[hi]"
"\n\t"
"nop"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"sbiw %[count], 1"
"\n\t"
"brne headC"
"\n\t"
"rjmp doneC"
"\n\t"
"bitTimeC:"
"\n\t"
"out %[port], %[next]"
"\n\t"
"mov %[next], %[lo]"
"\n\t"
"rol %[byte]"
"\n\t"
"sbrc %[byte], 7"
"\n\t"
"mov %[next], %[hi]"
"\n\t"
"nop"
"\n\t"
"out %[port], %[lo]"
"\n\t"
"ret"
"\n\t"
"doneC:"
"\n"
: [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTC)), [ptr] "e"(ptr),
[hi] "r"(hi), [lo] "r"(lo));
#if defined(PORTD) || defined(PORTB) || defined(PORTF)
} }
#endif // defined(PORTD/B/F) #endif // defined(PORTD/B/F)
#if defined(PORTF) #if defined(PORTF)
else else
#endif #endif
#endif // defined(PORTC) #endif // defined(PORTC)
// PORTF OUTPUT ---------------------------------------------------- // PORTF OUTPUT ----------------------------------------------------
#if defined(PORTF) #if defined(PORTF)
#if defined(PORTD) || defined(PORTB) || defined(PORTC) #if defined(PORTD) || defined(PORTB) || defined(PORTC)
if(port == &PORTF) { if (port == &PORTF) {
#endif // defined(PORTD/B/C) #endif // defined(PORTD/B/C)
hi = PORTF | pinMask; hi = PORTF | pinMask;
lo = PORTF & ~pinMask; lo = PORTF & ~pinMask;
next = lo; next = lo;
if(b & 0x80) next = hi; if (b & 0x80)
next = hi;
// Same as above, just set for PORTF & stripped of comments // Same as above, just set for PORTF & stripped of comments
asm volatile( asm volatile("headF:"
"headF:" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeC"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeC"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "rcall bitTimeC"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port], %[hi]" "\n\t" "out %[port], %[hi]"
"rcall bitTimeC" "\n\t" "\n\t"
"out %[port] , %[hi]" "\n\t" "rcall bitTimeC"
"rjmp .+0" "\n\t" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t" "out %[port], %[hi]"
"out %[port] , %[next]" "\n\t" "\n\t"
"mov %[next] , %[lo]" "\n\t" "rcall bitTimeC"
"sbrc %[byte] , 7" "\n\t" "\n\t"
"mov %[next] , %[hi]" "\n\t" "out %[port], %[hi]"
"nop" "\n\t" "\n\t"
"out %[port] , %[lo]" "\n\t" "rcall bitTimeC"
"sbiw %[count], 1" "\n\t" "\n\t"
"brne headF" "\n\t" "out %[port], %[hi]"
"rjmp doneC" "\n\t" "\n\t"
"bitTimeC:" "\n\t" "rcall bitTimeC"
"out %[port], %[next]" "\n\t" "\n\t"
"mov %[next], %[lo]" "\n\t" "out %[port] , %[hi]"
"rol %[byte]" "\n\t" "\n\t"
"sbrc %[byte], 7" "\n\t" "rjmp .+0"
"mov %[next], %[hi]" "\n\t" "\n\t"
"nop" "\n\t" "ld %[byte] , %a[ptr]+"
"out %[port], %[lo]" "\n\t" "\n\t"
"ret" "\n\t" "out %[port] , %[next]"
"doneC:" "\n" "\n\t"
: [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) "mov %[next] , %[lo]"
: [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), "\n\t"
[lo] "r" (lo)); "sbrc %[byte] , 7"
"\n\t"
#if defined(PORTD) || defined(PORTB) || defined(PORTC) "mov %[next] , %[hi]"
"\n\t"
"nop"
"\n\t"
"out %[port] , %[lo]"
"\n\t"
"sbiw %[count], 1"
"\n\t"
"brne headF"
"\n\t"
"rjmp doneC"
"\n\t"
"bitTimeC:"
"\n\t"
"out %[port], %[next]"
"\n\t"
"mov %[next], %[lo]"
"\n\t"
"rol %[byte]"
"\n\t"
"sbrc %[byte], 7"
"\n\t"
"mov %[next], %[hi]"
"\n\t"
"nop"
"\n\t"
"out %[port], %[lo]"
"\n\t"
"ret"
"\n\t"
"doneC:"
"\n"
: [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
: [port] "I"(_SFR_IO_ADDR(PORTF)), [ptr] "e"(ptr),
[hi] "r"(hi), [lo] "r"(lo));
#if defined(PORTD) || defined(PORTB) || defined(PORTC)
} }
#endif // defined(PORTD/B/C) #endif // defined(PORTD/B/C)
#endif // defined(PORTF) #endif // defined(PORTF)
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
...@@ -974,45 +1447,64 @@ void Adafruit_NeoPixel::show(void) { ...@@ -974,45 +1447,64 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit; volatile uint8_t next, bit;
hi = *port | pinMask; hi = *port | pinMask;
lo = *port & ~pinMask; lo = *port & ~pinMask;
next = lo; next = lo;
bit = 8; bit = 8;
asm volatile( asm volatile("head30:"
"head30:" "\n\t" // Clk Pseudocode (T = 0) "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) "st %a[port], %[hi]"
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) "\n\t" // 2 PORT = hi (T = 2)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) "sbrc %[byte] , 7"
"rjmp .+0" "\n\t" // 2 nop nop (T = 6) "\n\t" // 1-2 if(b & 128)
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8) "mov %[next], %[hi]"
"rjmp .+0" "\n\t" // 2 nop nop (T = 10) "\n\t" // 0-1 next = hi (T = 4)
"rjmp .+0" "\n\t" // 2 nop nop (T = 12) "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop (T = 14) "\n\t" // 2 nop nop (T = 6)
"nop" "\n\t" // 1 nop (T = 15) "st %a[port], %[next]"
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17) "\n\t" // 2 PORT = next (T = 8)
"rjmp .+0" "\n\t" // 2 nop nop (T = 19) "rjmp .+0"
"dec %[bit]" "\n\t" // 1 bit-- (T = 20) "\n\t" // 2 nop nop (T = 10)
"breq nextbyte30" "\n\t" // 1-2 if(bit == 0) "rjmp .+0"
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22) "\n\t" // 2 nop nop (T = 12)
"rjmp .+0" "\n\t" // 2 nop nop (T = 24) "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop (T = 26) "\n\t" // 2 nop nop (T = 14)
"rjmp .+0" "\n\t" // 2 nop nop (T = 28) "nop"
"rjmp head30" "\n\t" // 2 -> head30 (next bit out) "\n\t" // 1 nop (T = 15)
"nextbyte30:" "\n\t" // (T = 22) "st %a[port], %[lo]"
"nop" "\n\t" // 1 nop (T = 23) "\n\t" // 2 PORT = lo (T = 17)
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24) "rjmp .+0"
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26) "\n\t" // 2 nop nop (T = 19)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 28) "dec %[bit]"
"brne head30" "\n" // 1-2 if(i != 0) -> (next byte) "\n\t" // 1 bit-- (T = 20)
: [port] "+e" (port), "breq nextbyte30"
[byte] "+r" (b), "\n\t" // 1-2 if(bit == 0)
[bit] "+r" (bit), "rol %[byte]"
[next] "+r" (next), "\n\t" // 1 b <<= 1 (T = 22)
[count] "+w" (i) "rjmp .+0"
: [hi] "r" (hi), "\n\t" // 2 nop nop (T = 24)
[lo] "r" (lo), "rjmp .+0"
[ptr] "e" (ptr)); "\n\t" // 2 nop nop (T = 26)
"rjmp .+0"
"\n\t" // 2 nop nop (T = 28)
"rjmp head30"
"\n\t" // 2 -> head30 (next bit out)
"nextbyte30:"
"\n\t" // (T = 22)
"nop"
"\n\t" // 1 nop (T = 23)
"ldi %[bit] , 8"
"\n\t" // 1 bit = 8 (T = 24)
"ld %[byte] , %a[ptr]+"
"\n\t" // 2 b = *ptr++ (T = 26)
"sbiw %[count], 1"
"\n\t" // 2 i-- (T = 28)
"brne head30"
"\n" // 1-2 if(i != 0) -> (next byte)
: [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
[next] "+r"(next), [count] "+w"(i)
: [hi] "r"(hi), [lo] "r"(lo), [ptr] "e"(ptr));
} }
#endif // NEO_KHZ400 #endif // NEO_KHZ400
...@@ -1020,7 +1512,7 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1020,7 +1512,7 @@ void Adafruit_NeoPixel::show(void) {
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L)
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
// WS2811 and WS2812 have different hi/lo duty cycles; this is // WS2811 and WS2812 have different hi/lo duty cycles; this is
...@@ -1031,42 +1523,58 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1031,42 +1523,58 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit; volatile uint8_t next, bit;
hi = *port | pinMask; hi = *port | pinMask;
lo = *port & ~pinMask; lo = *port & ~pinMask;
next = lo; next = lo;
bit = 8; bit = 8;
asm volatile( asm volatile("head20:"
"head20:" "\n\t" // Clk Pseudocode (T = 0) "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) "st %a[port], %[hi]"
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128) "\n\t" // 2 PORT = hi (T = 2)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) "sbrc %[byte], 7"
"dec %[bit]" "\n\t" // 1 bit-- (T = 5) "\n\t" // 1-2 if(b & 128)
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7) "mov %[next], %[hi]"
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8) "\n\t" // 0-1 next = hi (T = 4)
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above) "dec %[bit]"
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) "\n\t" // 1 bit-- (T = 5)
"rjmp .+0" "\n\t" // 2 nop nop (T = 12) "st %a[port], %[next]"
"nop" "\n\t" // 1 nop (T = 13) "\n\t" // 2 PORT = next (T = 7)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) "mov %[next] , %[lo]"
"nop" "\n\t" // 1 nop (T = 16) "\n\t" // 1 next = lo (T = 8)
"rjmp .+0" "\n\t" // 2 nop nop (T = 18) "breq nextbyte20"
"rjmp head20" "\n\t" // 2 -> head20 (next bit out) "\n\t" // 1-2 if(bit == 0) (from dec above)
"nextbyte20:" "\n\t" // (T = 10) "rol %[byte]"
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11) "\n\t" // 1 b <<= 1 (T = 10)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13) "rjmp .+0"
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) "\n\t" // 2 nop nop (T = 12)
"nop" "\n\t" // 1 nop (T = 16) "nop"
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) "\n\t" // 1 nop (T = 13)
"brne head20" "\n" // 2 if(i != 0) -> (next byte) "st %a[port], %[lo]"
: [port] "+e" (port), "\n\t" // 2 PORT = lo (T = 15)
[byte] "+r" (b), "nop"
[bit] "+r" (bit), "\n\t" // 1 nop (T = 16)
[next] "+r" (next), "rjmp .+0"
[count] "+w" (i) "\n\t" // 2 nop nop (T = 18)
: [ptr] "e" (ptr), "rjmp head20"
[hi] "r" (hi), "\n\t" // 2 -> head20 (next bit out)
[lo] "r" (lo)); "nextbyte20:"
"\n\t" // (T = 10)
"ldi %[bit] , 8"
"\n\t" // 1 bit = 8 (T = 11)
"ld %[byte] , %a[ptr]+"
"\n\t" // 2 b = *ptr++ (T = 13)
"st %a[port], %[lo]"
"\n\t" // 2 PORT = lo (T = 15)
"nop"
"\n\t" // 1 nop (T = 16)
"sbiw %[count], 1"
"\n\t" // 2 i-- (T = 18)
"brne head20"
"\n" // 2 if(i != 0) -> (next byte)
: [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
[next] "+r"(next), [count] "+w"(i)
: [ptr] "e"(ptr), [hi] "r"(hi), [lo] "r"(lo));
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 KHz } else { // 400 KHz
...@@ -1078,269 +1586,368 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1078,269 +1586,368 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit; volatile uint8_t next, bit;
hi = *port | pinMask; hi = *port | pinMask;
lo = *port & ~pinMask; lo = *port & ~pinMask;
next = lo; next = lo;
bit = 8; bit = 8;
asm volatile( asm volatile("head40:"
"head40:" "\n\t" // Clk Pseudocode (T = 0) "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) "st %a[port], %[hi]"
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) "\n\t" // 2 PORT = hi (T = 2)
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4) "sbrc %[byte] , 7"
"rjmp .+0" "\n\t" // 2 nop nop (T = 6) "\n\t" // 1-2 if(b & 128)
"rjmp .+0" "\n\t" // 2 nop nop (T = 8) "mov %[next] , %[hi]"
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10) "\n\t" // 0-1 next = hi (T = 4)
"rjmp .+0" "\n\t" // 2 nop nop (T = 12) "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop (T = 14) "\n\t" // 2 nop nop (T = 6)
"rjmp .+0" "\n\t" // 2 nop nop (T = 16) "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop (T = 18) "\n\t" // 2 nop nop (T = 8)
"rjmp .+0" "\n\t" // 2 nop nop (T = 20) "st %a[port], %[next]"
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22) "\n\t" // 2 PORT = next (T = 10)
"nop" "\n\t" // 1 nop (T = 23) "rjmp .+0"
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24) "\n\t" // 2 nop nop (T = 12)
"dec %[bit]" "\n\t" // 1 bit-- (T = 25) "rjmp .+0"
"breq nextbyte40" "\n\t" // 1-2 if(bit == 0) "\n\t" // 2 nop nop (T = 14)
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27) "rjmp .+0"
"nop" "\n\t" // 1 nop (T = 28) "\n\t" // 2 nop nop (T = 16)
"rjmp .+0" "\n\t" // 2 nop nop (T = 30) "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop (T = 32) "\n\t" // 2 nop nop (T = 18)
"rjmp .+0" "\n\t" // 2 nop nop (T = 34) "rjmp .+0"
"rjmp .+0" "\n\t" // 2 nop nop (T = 36) "\n\t" // 2 nop nop (T = 20)
"rjmp .+0" "\n\t" // 2 nop nop (T = 38) "st %a[port], %[lo]"
"rjmp head40" "\n\t" // 2 -> head40 (next bit out) "\n\t" // 2 PORT = lo (T = 22)
"nextbyte40:" "\n\t" // (T = 27) "nop"
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28) "\n\t" // 1 nop (T = 23)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30) "mov %[next] , %[lo]"
"rjmp .+0" "\n\t" // 2 nop nop (T = 32) "\n\t" // 1 next = lo (T = 24)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34) "dec %[bit]"
"rjmp .+0" "\n\t" // 2 nop nop (T = 36) "\n\t" // 1 bit-- (T = 25)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 38) "breq nextbyte40"
"brne head40" "\n" // 1-2 if(i != 0) -> (next byte) "\n\t" // 1-2 if(bit == 0)
: [port] "+e" (port), "rol %[byte]"
[byte] "+r" (b), "\n\t" // 1 b <<= 1 (T = 27)
[bit] "+r" (bit), "nop"
[next] "+r" (next), "\n\t" // 1 nop (T = 28)
[count] "+w" (i) "rjmp .+0"
: [ptr] "e" (ptr), "\n\t" // 2 nop nop (T = 30)
[hi] "r" (hi), "rjmp .+0"
[lo] "r" (lo)); "\n\t" // 2 nop nop (T = 32)
"rjmp .+0"
"\n\t" // 2 nop nop (T = 34)
"rjmp .+0"
"\n\t" // 2 nop nop (T = 36)
"rjmp .+0"
"\n\t" // 2 nop nop (T = 38)
"rjmp head40"
"\n\t" // 2 -> head40 (next bit out)
"nextbyte40:"
"\n\t" // (T = 27)
"ldi %[bit] , 8"
"\n\t" // 1 bit = 8 (T = 28)
"ld %[byte] , %a[ptr]+"
"\n\t" // 2 b = *ptr++ (T = 30)
"rjmp .+0"
"\n\t" // 2 nop nop (T = 32)
"st %a[port], %[lo]"
"\n\t" // 2 PORT = lo (T = 34)
"rjmp .+0"
"\n\t" // 2 nop nop (T = 36)
"sbiw %[count], 1"
"\n\t" // 2 i-- (T = 38)
"brne head40"
"\n" // 1-2 if(i != 0) -> (next byte)
: [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
[next] "+r"(next), [count] "+w"(i)
: [ptr] "e"(ptr), [hi] "r"(hi), [lo] "r"(lo));
} }
#endif // NEO_KHZ400 #endif // NEO_KHZ400
#else #else
#error "CPU SPEED NOT SUPPORTED" #error "CPU SPEED NOT SUPPORTED"
#endif // end F_CPU ifdefs on __AVR__ #endif // end F_CPU ifdefs on __AVR__
// END AVR ---------------------------------------------------------------- // END AVR ----------------------------------------------------------------
#elif defined(__arm__) #elif defined(__arm__)
// ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due --------------------------- // ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due, RP2040 -------------------
#if defined(ARDUINO_ARCH_RP2040)
// Use PIO
rp2040Show(pin, pixels, numBytes, is800KHz);
#if defined(TEENSYDUINO) && defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6 #elif defined(TEENSYDUINO) && \
#define CYCLES_800_T0H (F_CPU / 4000000) defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6
#define CYCLES_800_T1H (F_CPU / 1250000) #define CYCLES_800_T0H (F_CPU / 4000000)
#define CYCLES_800 (F_CPU / 800000) #define CYCLES_800_T1H (F_CPU / 1250000)
#define CYCLES_400_T0H (F_CPU / 2000000) #define CYCLES_800 (F_CPU / 800000)
#define CYCLES_400_T1H (F_CPU / 833333) #define CYCLES_400_T0H (F_CPU / 2000000)
#define CYCLES_400 (F_CPU / 400000) #define CYCLES_400_T1H (F_CPU / 833333)
#define CYCLES_400 (F_CPU / 400000)
uint8_t *p = pixels, uint8_t *p = pixels, *end = p + numBytes, pix, mask;
*end = p + numBytes, pix, mask; volatile uint8_t *set = portSetRegister(pin), *clr = portClearRegister(pin);
volatile uint8_t *set = portSetRegister(pin), uint32_t cyc;
*clr = portClearRegister(pin);
uint32_t cyc;
ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
cyc = ARM_DWT_CYCCNT + CYCLES_800; cyc = ARM_DWT_CYCCNT + CYCLES_800;
while(p < end) { while (p < end) {
pix = *p++; pix = *p++;
for(mask = 0x80; mask; mask >>= 1) { for (mask = 0x80; mask; mask >>= 1) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_800); while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
cyc = ARM_DWT_CYCCNT; ;
cyc = ARM_DWT_CYCCNT;
*set = 1; *set = 1;
if(pix & mask) { if (pix & mask) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H)
;
} else { } else {
while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H)
;
} }
*clr = 1; *clr = 1;
} }
} }
while(ARM_DWT_CYCCNT - cyc < CYCLES_800); while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
;
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 kHz bitstream } else { // 400 kHz bitstream
cyc = ARM_DWT_CYCCNT + CYCLES_400; cyc = ARM_DWT_CYCCNT + CYCLES_400;
while(p < end) { while (p < end) {
pix = *p++; pix = *p++;
for(mask = 0x80; mask; mask >>= 1) { for (mask = 0x80; mask; mask >>= 1) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_400); while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
cyc = ARM_DWT_CYCCNT; ;
cyc = ARM_DWT_CYCCNT;
*set = 1; *set = 1;
if(pix & mask) { if (pix & mask) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H)
;
} else { } else {
while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H)
;
} }
*clr = 1; *clr = 1;
} }
} }
while(ARM_DWT_CYCCNT - cyc < CYCLES_400); while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
;
} }
#endif // NEO_KHZ400 #endif // NEO_KHZ400
#elif defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__)) #elif defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__))
#define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000) #define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000)
#define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000) #define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000)
#define CYCLES_800 (F_CPU_ACTUAL / 800000) #define CYCLES_800 (F_CPU_ACTUAL / 800000)
#define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000) #define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000)
#define CYCLES_400_T1H (F_CPU_ACTUAL / 833333) #define CYCLES_400_T1H (F_CPU_ACTUAL / 833333)
#define CYCLES_400 (F_CPU_ACTUAL / 400000) #define CYCLES_400 (F_CPU_ACTUAL / 400000)
uint8_t *p = pixels, uint8_t *p = pixels, *end = p + numBytes, pix, mask;
*end = p + numBytes, pix, mask; volatile uint32_t *set = portSetRegister(pin), *clr = portClearRegister(pin);
volatile uint32_t *set = portSetRegister(pin), uint32_t cyc, msk = digitalPinToBitMask(pin);
*clr = portClearRegister(pin);
uint32_t cyc, ARM_DEMCR |= ARM_DEMCR_TRCENA;
msk = digitalPinToBitMask(pin);
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
cyc = ARM_DWT_CYCCNT + CYCLES_800; cyc = ARM_DWT_CYCCNT + CYCLES_800;
while(p < end) { while (p < end) {
pix = *p++; pix = *p++;
for(mask = 0x80; mask; mask >>= 1) { for (mask = 0x80; mask; mask >>= 1) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_800); while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
cyc = ARM_DWT_CYCCNT; ;
cyc = ARM_DWT_CYCCNT;
*set = msk; *set = msk;
if(pix & mask) { if (pix & mask) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H)
;
} else { } else {
while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H)
;
} }
*clr = msk; *clr = msk;
} }
} }
while(ARM_DWT_CYCCNT - cyc < CYCLES_800); while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
;
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 kHz bitstream } else { // 400 kHz bitstream
cyc = ARM_DWT_CYCCNT + CYCLES_400; cyc = ARM_DWT_CYCCNT + CYCLES_400;
while(p < end) { while (p < end) {
pix = *p++; pix = *p++;
for(mask = 0x80; mask; mask >>= 1) { for (mask = 0x80; mask; mask >>= 1) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_400); while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
cyc = ARM_DWT_CYCCNT; ;
cyc = ARM_DWT_CYCCNT;
*set = msk; *set = msk;
if(pix & mask) { if (pix & mask) {
while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H)
;
} else { } else {
while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H)
;
} }
*clr = msk; *clr = msk;
} }
} }
while(ARM_DWT_CYCCNT - cyc < CYCLES_400); while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
;
} }
#endif // NEO_KHZ400 #endif // NEO_KHZ400
#elif defined(TEENSYDUINO) && defined(__MKL26Z64__) // Teensy-LC #elif defined(TEENSYDUINO) && defined(__MKL26Z64__) // Teensy-LC
#if F_CPU == 48000000 #if F_CPU == 48000000
uint8_t *p = pixels, uint8_t *p = pixels, pix, count, dly, bitmask = digitalPinToBitMask(pin);
pix, count, dly,
bitmask = digitalPinToBitMask(pin);
volatile uint8_t *reg = portSetRegister(pin); volatile uint8_t *reg = portSetRegister(pin);
uint32_t num = numBytes; uint32_t num = numBytes;
asm volatile( asm volatile("L%=_begin:"
"L%=_begin:" "\n\t" "\n\t"
"ldrb %[pix], [%[p], #0]" "\n\t" "ldrb %[pix], [%[p], #0]"
"lsl %[pix], #24" "\n\t" "\n\t"
"movs %[count], #7" "\n\t" "lsl %[pix], #24"
"L%=_loop:" "\n\t" "\n\t"
"lsl %[pix], #1" "\n\t" "movs %[count], #7"
"bcs L%=_loop_one" "\n\t" "\n\t"
"L%=_loop_zero:" "\n\t" "L%=_loop:"
"strb %[bitmask], [%[reg], #0]" "\n\t" "\n\t"
"movs %[dly], #4" "\n\t" "lsl %[pix], #1"
"L%=_loop_delay_T0H:" "\n\t" "\n\t"
"sub %[dly], #1" "\n\t" "bcs L%=_loop_one"
"bne L%=_loop_delay_T0H" "\n\t" "\n\t"
"strb %[bitmask], [%[reg], #4]" "\n\t" "L%=_loop_zero:"
"movs %[dly], #13" "\n\t" "\n\t"
"L%=_loop_delay_T0L:" "\n\t" "strb %[bitmask], [%[reg], #0]"
"sub %[dly], #1" "\n\t" "\n\t"
"bne L%=_loop_delay_T0L" "\n\t" "movs %[dly], #4"
"b L%=_next" "\n\t" "\n\t"
"L%=_loop_one:" "\n\t" "L%=_loop_delay_T0H:"
"strb %[bitmask], [%[reg], #0]" "\n\t" "\n\t"
"movs %[dly], #13" "\n\t" "sub %[dly], #1"
"L%=_loop_delay_T1H:" "\n\t" "\n\t"
"sub %[dly], #1" "\n\t" "bne L%=_loop_delay_T0H"
"bne L%=_loop_delay_T1H" "\n\t" "\n\t"
"strb %[bitmask], [%[reg], #4]" "\n\t" "strb %[bitmask], [%[reg], #4]"
"movs %[dly], #4" "\n\t" "\n\t"
"L%=_loop_delay_T1L:" "\n\t" "movs %[dly], #13"
"sub %[dly], #1" "\n\t" "\n\t"
"bne L%=_loop_delay_T1L" "\n\t" "L%=_loop_delay_T0L:"
"nop" "\n\t" "\n\t"
"L%=_next:" "\n\t" "sub %[dly], #1"
"sub %[count], #1" "\n\t" "\n\t"
"bne L%=_loop" "\n\t" "bne L%=_loop_delay_T0L"
"lsl %[pix], #1" "\n\t" "\n\t"
"bcs L%=_last_one" "\n\t" "b L%=_next"
"L%=_last_zero:" "\n\t" "\n\t"
"strb %[bitmask], [%[reg], #0]" "\n\t" "L%=_loop_one:"
"movs %[dly], #4" "\n\t" "\n\t"
"L%=_last_delay_T0H:" "\n\t" "strb %[bitmask], [%[reg], #0]"
"sub %[dly], #1" "\n\t" "\n\t"
"bne L%=_last_delay_T0H" "\n\t" "movs %[dly], #13"
"strb %[bitmask], [%[reg], #4]" "\n\t" "\n\t"
"movs %[dly], #10" "\n\t" "L%=_loop_delay_T1H:"
"L%=_last_delay_T0L:" "\n\t" "\n\t"
"sub %[dly], #1" "\n\t" "sub %[dly], #1"
"bne L%=_last_delay_T0L" "\n\t" "\n\t"
"b L%=_repeat" "\n\t" "bne L%=_loop_delay_T1H"
"L%=_last_one:" "\n\t" "\n\t"
"strb %[bitmask], [%[reg], #0]" "\n\t" "strb %[bitmask], [%[reg], #4]"
"movs %[dly], #13" "\n\t" "\n\t"
"L%=_last_delay_T1H:" "\n\t" "movs %[dly], #4"
"sub %[dly], #1" "\n\t" "\n\t"
"bne L%=_last_delay_T1H" "\n\t" "L%=_loop_delay_T1L:"
"strb %[bitmask], [%[reg], #4]" "\n\t" "\n\t"
"movs %[dly], #1" "\n\t" "sub %[dly], #1"
"L%=_last_delay_T1L:" "\n\t" "\n\t"
"sub %[dly], #1" "\n\t" "bne L%=_loop_delay_T1L"
"bne L%=_last_delay_T1L" "\n\t" "\n\t"
"nop" "\n\t" "nop"
"L%=_repeat:" "\n\t" "\n\t"
"add %[p], #1" "\n\t" "L%=_next:"
"sub %[num], #1" "\n\t" "\n\t"
"bne L%=_begin" "\n\t" "sub %[count], #1"
"L%=_done:" "\n\t" "\n\t"
: [p] "+r" (p), "bne L%=_loop"
[pix] "=&r" (pix), "\n\t"
[count] "=&r" (count), "lsl %[pix], #1"
[dly] "=&r" (dly), "\n\t"
[num] "+r" (num) "bcs L%=_last_one"
: [bitmask] "r" (bitmask), "\n\t"
[reg] "r" (reg) "L%=_last_zero:"
); "\n\t"
"strb %[bitmask], [%[reg], #0]"
"\n\t"
"movs %[dly], #4"
"\n\t"
"L%=_last_delay_T0H:"
"\n\t"
"sub %[dly], #1"
"\n\t"
"bne L%=_last_delay_T0H"
"\n\t"
"strb %[bitmask], [%[reg], #4]"
"\n\t"
"movs %[dly], #10"
"\n\t"
"L%=_last_delay_T0L:"
"\n\t"
"sub %[dly], #1"
"\n\t"
"bne L%=_last_delay_T0L"
"\n\t"
"b L%=_repeat"
"\n\t"
"L%=_last_one:"
"\n\t"
"strb %[bitmask], [%[reg], #0]"
"\n\t"
"movs %[dly], #13"
"\n\t"
"L%=_last_delay_T1H:"
"\n\t"
"sub %[dly], #1"
"\n\t"
"bne L%=_last_delay_T1H"
"\n\t"
"strb %[bitmask], [%[reg], #4]"
"\n\t"
"movs %[dly], #1"
"\n\t"
"L%=_last_delay_T1L:"
"\n\t"
"sub %[dly], #1"
"\n\t"
"bne L%=_last_delay_T1L"
"\n\t"
"nop"
"\n\t"
"L%=_repeat:"
"\n\t"
"add %[p], #1"
"\n\t"
"sub %[num], #1"
"\n\t"
"bne L%=_begin"
"\n\t"
"L%=_done:"
"\n\t"
: [p] "+r"(p), [pix] "=&r"(pix), [count] "=&r"(count),
[dly] "=&r"(dly), [num] "+r"(num)
: [bitmask] "r"(bitmask), [reg] "r"(reg));
#else #else
#error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" #error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz"
#endif // F_CPU == 48000000 #endif // F_CPU == 48000000
// Begin of support for nRF52 based boards ------------------------- // Begin of support for nRF52 based boards -------------------------
#elif defined(NRF52) || defined(NRF52_SERIES) #elif defined(NRF52) || defined(NRF52_SERIES)
// [[[Begin of the Neopixel NRF52 EasyDMA implementation // [[[Begin of the Neopixel NRF52 EasyDMA implementation
...@@ -1369,16 +1976,16 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1369,16 +1976,16 @@ void Adafruit_NeoPixel::show(void) {
//#define MAGIC_T1H 12UL | (0x8000) // 0.75us //#define MAGIC_T1H 12UL | (0x8000) // 0.75us
// WS2812B (rev B) timing is 0.4 and 0.8 us // WS2812B (rev B) timing is 0.4 and 0.8 us
#define MAGIC_T0H 6UL | (0x8000) // 0.375us #define MAGIC_T0H 6UL | (0x8000) // 0.375us
#define MAGIC_T1H 13UL | (0x8000) // 0.8125us #define MAGIC_T1H 13UL | (0x8000) // 0.8125us
// WS2811 (400 khz) timing is 0.5 and 1.2 // WS2811 (400 khz) timing is 0.5 and 1.2
#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us #define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us
#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us #define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us
// For 400Khz, we double value of CTOPVAL // For 400Khz, we double value of CTOPVAL
#define CTOPVAL 20UL // 1.25us #define CTOPVAL 20UL // 1.25us
#define CTOPVAL_400KHz 40UL // 2.5us #define CTOPVAL_400KHz 40UL // 2.5us
// ---------- END Constants for the EasyDMA implementation ------------- // ---------- END Constants for the EasyDMA implementation -------------
// //
...@@ -1390,14 +1997,14 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1390,14 +1997,14 @@ void Adafruit_NeoPixel::show(void) {
// The number of cycles was hand picked and is guaranteed to be 100% // The number of cycles was hand picked and is guaranteed to be 100%
// organic to preserve freshness and high accuracy. // organic to preserve freshness and high accuracy.
// ---------- BEGIN Constants for cycle counter implementation --------- // ---------- BEGIN Constants for cycle counter implementation ---------
#define CYCLES_800_T0H 18 // ~0.36 uS #define CYCLES_800_T0H 18 // ~0.36 uS
#define CYCLES_800_T1H 41 // ~0.76 uS #define CYCLES_800_T1H 41 // ~0.76 uS
#define CYCLES_800 71 // ~1.25 uS #define CYCLES_800 71 // ~1.25 uS
#define CYCLES_400_T0H 26 // ~0.50 uS #define CYCLES_400_T0H 26 // ~0.50 uS
#define CYCLES_400_T1H 70 // ~1.26 uS #define CYCLES_400_T1H 70 // ~1.26 uS
#define CYCLES_400 156 // ~2.50 uS #define CYCLES_400 156 // ~2.50 uS
// ---------- END of Constants for cycle counter implementation -------- // ---------- END of Constants for cycle counter implementation --------
// To support both the SoftDevice + Neopixels we use the EasyDMA // To support both the SoftDevice + Neopixels we use the EasyDMA
// feature from the NRF25. However this technique implies to // feature from the NRF25. However this technique implies to
...@@ -1409,55 +2016,60 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1409,55 +2016,60 @@ void Adafruit_NeoPixel::show(void) {
// //
// If there is not enough memory, we will fall back to cycle counter // If there is not enough memory, we will fall back to cycle counter
// using DWT // using DWT
uint32_t pattern_size = numBytes*8*sizeof(uint16_t)+2*sizeof(uint16_t); uint32_t pattern_size =
uint16_t* pixels_pattern = NULL; numBytes * 8 * sizeof(uint16_t) + 2 * sizeof(uint16_t);
uint16_t *pixels_pattern = NULL;
NRF_PWM_Type* pwm = NULL; NRF_PWM_Type *pwm = NULL;
// Try to find a free PWM device, which is not enabled // Try to find a free PWM device, which is not enabled
// and has no connected pins // and has no connected pins
NRF_PWM_Type* PWM[] = { NRF_PWM_Type *PWM[] = {
NRF_PWM0, NRF_PWM1, NRF_PWM2 NRF_PWM0,
NRF_PWM1,
NRF_PWM2
#if defined(NRF_PWM3) #if defined(NRF_PWM3)
,NRF_PWM3 ,
NRF_PWM3
#endif #endif
}; };
for(unsigned int device = 0; device < (sizeof(PWM)/sizeof(PWM[0])); device++) { for (unsigned int device = 0; device < (sizeof(PWM) / sizeof(PWM[0]));
if( (PWM[device]->ENABLE == 0) && device++) {
if ((PWM[device]->ENABLE == 0) &&
(PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) && (PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) &&
(PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) && (PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) &&
(PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) && (PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) &&
(PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk) (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk)) {
) {
pwm = PWM[device]; pwm = PWM[device];
break; break;
} }
} }
// only malloc if there is PWM device available // only malloc if there is PWM device available
if ( pwm != NULL ) { if (pwm != NULL) {
#if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc
pixels_pattern = (uint16_t *) rtos_malloc(pattern_size); pixels_pattern = (uint16_t *)rtos_malloc(pattern_size);
#else #else
pixels_pattern = (uint16_t *) malloc(pattern_size); pixels_pattern = (uint16_t *)malloc(pattern_size);
#endif #endif
} }
// Use the identified device to choose the implementation // Use the identified device to choose the implementation
// If a PWM device is available use DMA // If a PWM device is available use DMA
if( (pixels_pattern != NULL) && (pwm != NULL) ) { if ((pixels_pattern != NULL) && (pwm != NULL)) {
uint16_t pos = 0; // bit position uint16_t pos = 0; // bit position
for(uint16_t n=0; n<numBytes; n++) { for (uint16_t n = 0; n < numBytes; n++) {
uint8_t pix = pixels[n]; uint8_t pix = pixels[n];
for(uint8_t mask=0x80; mask>0; mask >>= 1) { for (uint8_t mask = 0x80; mask > 0; mask >>= 1) {
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
if( !is800KHz ) { if (!is800KHz) {
pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz; pixels_pattern[pos] =
}else (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz;
#endif } else
#endif
{ {
pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H; pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H;
} }
...@@ -1474,15 +2086,16 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1474,15 +2086,16 @@ void Adafruit_NeoPixel::show(void) {
pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos); pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
// Set the PWM to use the 16MHz clock // Set the PWM to use the 16MHz clock
pwm->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos); pwm->PRESCALER =
(PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
// Setting of the maximum count // Setting of the maximum count
// but keeping it on 16Mhz allows for more granularity just // but keeping it on 16Mhz allows for more granularity just
// in case someone wants to do more fine-tuning of the timing. // in case someone wants to do more fine-tuning of the timing.
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
if( !is800KHz ) { if (!is800KHz) {
pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos); pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos);
}else } else
#endif #endif
{ {
pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos); pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos);
...@@ -1501,10 +2114,10 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1501,10 +2114,10 @@ void Adafruit_NeoPixel::show(void) {
pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos; pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos;
// Calculation of the number of steps loaded from memory. // Calculation of the number of steps loaded from memory.
pwm->SEQ[0].CNT = (pattern_size/sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos; pwm->SEQ[0].CNT = (pattern_size / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos;
// The following settings are ignored with the current config. // The following settings are ignored with the current config.
pwm->SEQ[0].REFRESH = 0; pwm->SEQ[0].REFRESH = 0;
pwm->SEQ[0].ENDDELAY = 0; pwm->SEQ[0].ENDDELAY = 0;
// The Neopixel implementation is a blocking algorithm. DMA // The Neopixel implementation is a blocking algorithm. DMA
...@@ -1512,29 +2125,28 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1512,29 +2125,28 @@ void Adafruit_NeoPixel::show(void) {
// operation we enable the interruption for the end of sequence // operation we enable the interruption for the end of sequence
// and block the execution thread until the event flag is set by // and block the execution thread until the event flag is set by
// the peripheral. // the peripheral.
// pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<<PWM_INTEN_SEQEND0_Pos); // pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<<PWM_INTEN_SEQEND0_Pos);
// PSEL must be configured before enabling PWM // PSEL must be configured before enabling PWM
#if defined(ARDUINO_ARCH_NRF52840) #if defined(ARDUINO_ARCH_NRF52840)
pwm->PSEL.OUT[0] = g_APinDescription[pin].name; pwm->PSEL.OUT[0] = g_APinDescription[pin].name;
#else #else
pwm->PSEL.OUT[0] = g_ADigitalPinMap[pin]; pwm->PSEL.OUT[0] = g_ADigitalPinMap[pin];
#endif #endif
// Enable the PWM // Enable the PWM
pwm->ENABLE = 1; pwm->ENABLE = 1;
// After all of this and many hours of reading the documentation // After all of this and many hours of reading the documentation
// we are ready to start the sequence... // we are ready to start the sequence...
pwm->EVENTS_SEQEND[0] = 0; pwm->EVENTS_SEQEND[0] = 0;
pwm->TASKS_SEQSTART[0] = 1; pwm->TASKS_SEQSTART[0] = 1;
// But we have to wait for the flag to be set. // But we have to wait for the flag to be set.
while(!pwm->EVENTS_SEQEND[0]) while (!pwm->EVENTS_SEQEND[0]) {
{ #if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840)
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840)
yield(); yield();
#endif #endif
} }
// Before leave we clear the flag for the event. // Before leave we clear the flag for the event.
...@@ -1548,40 +2160,39 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1548,40 +2160,39 @@ void Adafruit_NeoPixel::show(void) {
pwm->PSEL.OUT[0] = 0xFFFFFFFFUL; pwm->PSEL.OUT[0] = 0xFFFFFFFFUL;
#if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free
rtos_free(pixels_pattern); rtos_free(pixels_pattern);
#else #else
free(pixels_pattern); free(pixels_pattern);
#endif #endif
}// End of DMA implementation } // End of DMA implementation
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
else{ else {
#ifndef ARDUINO_ARCH_NRF52840 #ifndef ARDUINO_ARCH_NRF52840
// Fall back to DWT // Fall back to DWT
#if defined(ARDUINO_NRF52_ADAFRUIT) #if defined(ARDUINO_NRF52_ADAFRUIT)
// Bluefruit Feather 52 uses freeRTOS // Bluefruit Feather 52 uses freeRTOS
// Critical Section is used since it does not block SoftDevice execution // Critical Section is used since it does not block SoftDevice execution
taskENTER_CRITICAL(); taskENTER_CRITICAL();
#elif defined(NRF52_DISABLE_INT) #elif defined(NRF52_DISABLE_INT)
// If you are using the Bluetooth SoftDevice we advise you to not disable // If you are using the Bluetooth SoftDevice we advise you to not disable
// the interrupts. Disabling the interrupts even for short periods of time // the interrupts. Disabling the interrupts even for short periods of time
// causes the SoftDevice to stop working. // causes the SoftDevice to stop working.
// Disable the interrupts only in cases where you need high performance for // Disable the interrupts only in cases where you need high performance for
// the LEDs and if you are not using the EasyDMA feature. // the LEDs and if you are not using the EasyDMA feature.
__disable_irq(); __disable_irq();
#endif #endif
NRF_GPIO_Type* nrf_port = (NRF_GPIO_Type*) digitalPinToPort(pin); NRF_GPIO_Type *nrf_port = (NRF_GPIO_Type *)digitalPinToPort(pin);
uint32_t pinMask = digitalPinToBitMask(pin); uint32_t pinMask = digitalPinToBitMask(pin);
uint32_t CYCLES_X00 = CYCLES_800; uint32_t CYCLES_X00 = CYCLES_800;
uint32_t CYCLES_X00_T1H = CYCLES_800_T1H; uint32_t CYCLES_X00_T1H = CYCLES_800_T1H;
uint32_t CYCLES_X00_T0H = CYCLES_800_T0H; uint32_t CYCLES_X00_T0H = CYCLES_800_T0H;
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
if( !is800KHz ) if (!is800KHz) {
{ CYCLES_X00 = CYCLES_400;
CYCLES_X00 = CYCLES_400;
CYCLES_X00_T1H = CYCLES_400_T1H; CYCLES_X00_T1H = CYCLES_400_T1H;
CYCLES_X00_T0H = CYCLES_400_T0H; CYCLES_X00_T0H = CYCLES_400_T0H;
} }
...@@ -1592,36 +2203,39 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1592,36 +2203,39 @@ void Adafruit_NeoPixel::show(void) {
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
// Tries to re-send the frame if is interrupted by the SoftDevice. // Tries to re-send the frame if is interrupted by the SoftDevice.
while(1) { while (1) {
uint8_t *p = pixels; uint8_t *p = pixels;
uint32_t cycStart = DWT->CYCCNT; uint32_t cycStart = DWT->CYCCNT;
uint32_t cyc = 0; uint32_t cyc = 0;
for(uint16_t n=0; n<numBytes; n++) { for (uint16_t n = 0; n < numBytes; n++) {
uint8_t pix = *p++; uint8_t pix = *p++;
for(uint8_t mask = 0x80; mask; mask >>= 1) { for (uint8_t mask = 0x80; mask; mask >>= 1) {
while(DWT->CYCCNT - cyc < CYCLES_X00); while (DWT->CYCCNT - cyc < CYCLES_X00)
cyc = DWT->CYCCNT; ;
cyc = DWT->CYCCNT;
nrf_port->OUTSET |= pinMask; nrf_port->OUTSET |= pinMask;
if(pix & mask) { if (pix & mask) {
while(DWT->CYCCNT - cyc < CYCLES_X00_T1H); while (DWT->CYCCNT - cyc < CYCLES_X00_T1H)
;
} else { } else {
while(DWT->CYCCNT - cyc < CYCLES_X00_T0H); while (DWT->CYCCNT - cyc < CYCLES_X00_T0H)
;
} }
nrf_port->OUTCLR |= pinMask; nrf_port->OUTCLR |= pinMask;
} }
} }
while(DWT->CYCCNT - cyc < CYCLES_X00); while (DWT->CYCCNT - cyc < CYCLES_X00)
;
// If total time longer than 25%, resend the whole data. // If total time longer than 25%, resend the whole data.
// Since we are likely to be interrupted by SoftDevice // Since we are likely to be interrupted by SoftDevice
if ( (DWT->CYCCNT - cycStart) < ( 8*numBytes*((CYCLES_X00*5)/4) ) ) { if ((DWT->CYCCNT - cycStart) < (8 * numBytes * ((CYCLES_X00 * 5) / 4))) {
break; break;
} }
...@@ -1629,40 +2243,44 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1629,40 +2243,44 @@ void Adafruit_NeoPixel::show(void) {
delayMicroseconds(300); delayMicroseconds(300);
} }
// Enable interrupts again // Enable interrupts again
#if defined(ARDUINO_NRF52_ADAFRUIT) #if defined(ARDUINO_NRF52_ADAFRUIT)
taskEXIT_CRITICAL(); taskEXIT_CRITICAL();
#elif defined(NRF52_DISABLE_INT) #elif defined(NRF52_DISABLE_INT)
__enable_irq(); __enable_irq();
#endif #endif
#endif #endif
} }
// END of NRF52 implementation // END of NRF52 implementation
#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__) // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo and others #elif defined(__SAMD21E17A__) || defined(__SAMD21G18A__) || \
defined(__SAMD21E18A__) || defined(__SAMD21J18A__) || \
defined (__SAMD11C14A__)
// Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo
// and others
// Tried this with a timer/counter, couldn't quite get adequate // Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs... // resolution. So yay, you get a load of goofball NOPs...
uint8_t *ptr, *end, p, bitMask, portNum; uint8_t *ptr, *end, p, bitMask, portNum;
uint32_t pinMask; uint32_t pinMask;
portNum = g_APinDescription[pin].ulPort; portNum = g_APinDescription[pin].ulPort;
pinMask = 1ul << g_APinDescription[pin].ulPin; pinMask = 1ul << g_APinDescription[pin].ulPin;
ptr = pixels; ptr = pixels;
end = ptr + numBytes; end = ptr + numBytes;
p = *ptr++; p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg),
*clr = &(PORT->Group[portNum].OUTCLR.reg); *clr = &(PORT->Group[portNum].OUTCLR.reg);
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
for(;;) { for (;;) {
*set = pinMask; *set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"); asm("nop; nop; nop; nop; nop; nop; nop; nop;");
if(p & bitMask) { if (p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;" asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;"); "nop; nop; nop; nop;");
...@@ -1673,20 +2291,21 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1673,20 +2291,21 @@ void Adafruit_NeoPixel::show(void) {
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;"); "nop; nop; nop; nop;");
} }
if(bitMask >>= 1) { if (bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;"); asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
} else { } else {
if(ptr >= end) break; if (ptr >= end)
p = *ptr++; break;
p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
} }
} }
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 KHz bitstream } else { // 400 KHz bitstream
for(;;) { for (;;) {
*set = pinMask; *set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
if(p & bitMask) { if (p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;" asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
...@@ -1703,29 +2322,195 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1703,29 +2322,195 @@ void Adafruit_NeoPixel::show(void) {
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"); "nop; nop; nop; nop; nop; nop; nop; nop;");
if(bitMask >>= 1) { if (bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop;"); asm("nop; nop; nop; nop; nop; nop; nop;");
} else { } else {
if(ptr >= end) break; if (ptr >= end)
p = *ptr++; break;
p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
} }
} }
} }
#endif #endif
#elif defined (__SAMD51__) // M4 //----
#elif defined(XMC1100_XMC2GO) || defined(XMC1100_H_BRIDGE2GO) || defined(XMC1100_Boot_Kit) || defined(XMC1300_Boot_Kit)
// XMC1100/1200/1300 with ARM Cortex M0 are running with 32MHz, XMC1400 runs with 48MHz so may not work
// Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs...
uint8_t *ptr, *end, p, bitMask, portNum, bit; uint8_t *ptr, *end, p, bitMask, portNum;
uint32_t pinMask; uint32_t pinMask;
portNum = g_APinDescription[pin].ulPort;
pinMask = 1ul << g_APinDescription[pin].ulPin;
ptr = pixels; ptr = pixels;
end = ptr + numBytes; end = ptr + numBytes;
p = *ptr++; p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
XMC_GPIO_PORT_t* XMC_port = mapping_port_pin[ pin ].port;
uint8_t XMC_pin = mapping_port_pin[ pin ].pin;
uint32_t omrhigh = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_HIGH << XMC_pin;
uint32_t omrlow = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_LOW << XMC_pin;
#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) {
#endif
for(;;) {
XMC_port->OMR = omrhigh;
asm("nop; nop; nop; nop;");
if(p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop;");
XMC_port->OMR = omrlow;
} else {
XMC_port->OMR = omrlow;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop;");
}
if(bitMask >>= 1) {
asm("nop; nop; nop; nop; nop;");
} else {
if(ptr >= end) break;
p = *ptr++;
bitMask = 0x80;
}
}
#ifdef NEO_KHZ400 // untested code
} else { // 400 KHz bitstream
for(;;) {
XMC_port->OMR = omrhigh;
asm("nop; nop; nop; nop; nop;");
if(p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop;");
XMC_port->OMR = omrlow;
} else {
XMC_port->OMR = omrlow;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop;");
}
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
if(bitMask >>= 1) {
asm("nop; nop; nop;");
} else {
if(ptr >= end) break;
p = *ptr++;
bitMask = 0x80;
}
}
}
#endif
//----
//----
#elif defined(XMC4700_Relax_Kit) || defined(XMC4800_Relax_Kit)
// XMC4700 and XMC4800 with ARM Cortex M4 are running with 144MHz
// Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs...
uint8_t *ptr, *end, p, bitMask, portNum;
uint32_t pinMask;
ptr = pixels;
end = ptr + numBytes;
p = *ptr++;
bitMask = 0x80;
XMC_GPIO_PORT_t* XMC_port = mapping_port_pin[ pin ].port;
uint8_t XMC_pin = mapping_port_pin[ pin ].pin;
uint32_t omrhigh = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_HIGH << XMC_pin;
uint32_t omrlow = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_LOW << XMC_pin;
#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) {
#endif
for(;;) {
XMC_port->OMR = omrhigh;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
if(p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
XMC_port->OMR = omrlow;
} else {
XMC_port->OMR = omrlow;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
}
if(bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
} else {
if(ptr >= end) break;
p = *ptr++;
bitMask = 0x80;
}
}
#ifdef NEO_KHZ400
} else { // 400 KHz bitstream
// ToDo!
}
#endif
//----
#elif defined(__SAMD51__) // M4
uint8_t *ptr, *end, p, bitMask, portNum, bit;
uint32_t pinMask;
portNum = g_APinDescription[pin].ulPort;
pinMask = 1ul << g_APinDescription[pin].ulPin;
ptr = pixels;
end = ptr + numBytes;
p = *ptr++;
bitMask = 0x80;
volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg),
*clr = &(PORT->Group[portNum].OUTCLR.reg); *clr = &(PORT->Group[portNum].OUTCLR.reg);
...@@ -1745,65 +2530,67 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1745,65 +2530,67 @@ void Adafruit_NeoPixel::show(void) {
// seems to work just well enough. When finished, the SysTick // seems to work just well enough. When finished, the SysTick
// peripheral is set back to its original state. // peripheral is set back to its original state.
uint32_t t0, t1, top, ticks, uint32_t t0, t1, top, ticks, saveLoad = SysTick->LOAD, saveVal = SysTick->VAL;
saveLoad = SysTick->LOAD, saveVal = SysTick->VAL;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS
t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi
t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 KHz bitstream } else { // 400 KHz bitstream
top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS
t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi
t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi
} }
#endif #endif
SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq
SysTick->VAL = top; // Set to start value (counts down) SysTick->VAL = top; // Set to start value (counts down)
(void)SysTick->VAL; // Dummy read helps sync up 1st bit (void)SysTick->VAL; // Dummy read helps sync up 1st bit
for(;;) { for (;;) {
*set = pinMask; // Set output high *set = pinMask; // Set output high
ticks = (p & bitMask) ? t1 : t0; // SysTick threshold, ticks = (p & bitMask) ? t1 : t0; // SysTick threshold,
while(SysTick->VAL > ticks); // wait for it while (SysTick->VAL > ticks)
*clr = pinMask; // Set output low ; // wait for it
if(!(bitMask >>= 1)) { // Next bit for this byte...done? *clr = pinMask; // Set output low
if(ptr >= end) break; // If last byte sent, exit loop if (!(bitMask >>= 1)) { // Next bit for this byte...done?
p = *ptr++; // Fetch next byte if (ptr >= end)
bitMask = 0x80; // Reset bitmask break; // If last byte sent, exit loop
p = *ptr++; // Fetch next byte
bitMask = 0x80; // Reset bitmask
} }
while(SysTick->VAL <= ticks); // Wait for rollover to 'top' while (SysTick->VAL <= ticks)
; // Wait for rollover to 'top'
} }
SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms
SysTick->VAL = saveVal; // Restore SysTick value SysTick->VAL = saveVal; // Restore SysTick value
#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz) #elif defined(ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz)
// Tried this with a timer/counter, couldn't quite get adequate // Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs... // resolution. So yay, you get a load of goofball NOPs...
uint8_t *ptr, *end, p, bitMask; uint8_t *ptr, *end, p, bitMask;
uint32_t pinMask; uint32_t pinMask;
pinMask = BIT(PIN_MAP[pin].gpio_bit); pinMask = BIT(PIN_MAP[pin].gpio_bit);
ptr = pixels; ptr = pixels;
end = ptr + numBytes; end = ptr + numBytes;
p = *ptr++; p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
volatile uint16_t *set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); volatile uint16_t *set = &(PIN_MAP[pin].gpio_device->regs->BSRRL);
volatile uint16_t *clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); volatile uint16_t *clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH);
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
for(;;) { for (;;) {
if(p & bitMask) { // ONE if (p & bitMask) { // ONE
// High 800ns // High 800ns
*set = pinMask; *set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;" asm("nop; nop; nop; nop; nop; nop; nop; nop;"
...@@ -1849,12 +2636,13 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1849,12 +2636,13 @@ void Adafruit_NeoPixel::show(void) {
"nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;"); "nop; nop; nop; nop;");
} }
if(bitMask >>= 1) { if (bitMask >>= 1) {
// Move on to the next pixel // Move on to the next pixel
asm("nop;"); asm("nop;");
} else { } else {
if(ptr >= end) break; if (ptr >= end)
p = *ptr++; break;
p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
} }
} }
...@@ -1865,17 +2653,17 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1865,17 +2653,17 @@ void Adafruit_NeoPixel::show(void) {
#endif #endif
#elif defined(TARGET_LPC1768) #elif defined(TARGET_LPC1768)
uint8_t *ptr, *end, p, bitMask; uint8_t *ptr, *end, p, bitMask;
ptr = pixels; ptr = pixels;
end = ptr + numBytes; end = ptr + numBytes;
p = *ptr++; p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
for(;;) { for (;;) {
if(p & bitMask) { if (p & bitMask) {
// data ONE high // data ONE high
// min: 550 typ: 700 max: 5,500 // min: 550 typ: 700 max: 5,500
gpio_set(pin); gpio_set(pin);
...@@ -1893,12 +2681,13 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1893,12 +2681,13 @@ void Adafruit_NeoPixel::show(void) {
gpio_clear(pin); gpio_clear(pin);
time::delay_ns(450); time::delay_ns(450);
} }
if(bitMask >>= 1) { if (bitMask >>= 1) {
// Move on to the next pixel // Move on to the next pixel
asm("nop;"); asm("nop;");
} else { } else {
if(ptr >= end) break; if (ptr >= end)
p = *ptr++; break;
p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
} }
} }
...@@ -1908,204 +2697,229 @@ void Adafruit_NeoPixel::show(void) { ...@@ -1908,204 +2697,229 @@ void Adafruit_NeoPixel::show(void) {
} }
#endif #endif
#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) #elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
uint8_t *p = pixels, *end = p + numBytes, uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80;
pix = *p++, mask = 0x80; uint32_t cyc;
uint32_t cyc;
uint32_t saveLoad = SysTick->LOAD, saveVal = SysTick->VAL; uint32_t saveLoad = SysTick->LOAD, saveVal = SysTick->VAL;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
uint32_t top = (F_CPU / 800000); // 1.25µs uint32_t top = (F_CPU / 800000); // 1.25µs
uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs
uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs
SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq
SysTick->VAL = 0; // Set to start value SysTick->VAL = 0; // Set to start value
for(;;) { for (;;) {
LL_GPIO_SetOutputPin(gpioPort, gpioPin); LL_GPIO_SetOutputPin(gpioPort, gpioPin);
cyc = (pix & mask) ? t1 : t0; cyc = (pix & mask) ? t1 : t0;
while(SysTick->VAL > cyc); while (SysTick->VAL > cyc)
;
LL_GPIO_ResetOutputPin(gpioPort, gpioPin); LL_GPIO_ResetOutputPin(gpioPort, gpioPin);
if(!(mask >>= 1)) { if (!(mask >>= 1)) {
if(p >= end) break; if (p >= end)
pix = *p++; break;
pix = *p++;
mask = 0x80; mask = 0x80;
} }
while(SysTick->VAL <= cyc); while (SysTick->VAL <= cyc)
;
} }
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 kHz bitstream } else { // 400 kHz bitstream
uint32_t top = (F_CPU / 400000); // 2.5µs uint32_t top = (F_CPU / 400000); // 2.5µs
uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs
uint32_t t1 = top - (F_CPU / 833333); // 1.2µs uint32_t t1 = top - (F_CPU / 833333); // 1.2µs
SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq
SysTick->VAL = 0; // Set to start value SysTick->VAL = 0; // Set to start value
for(;;) { for (;;) {
LL_GPIO_SetOutputPin(gpioPort, gpioPin); LL_GPIO_SetOutputPin(gpioPort, gpioPin);
cyc = (pix & mask) ? t1 : t0; cyc = (pix & mask) ? t1 : t0;
while(SysTick->VAL > cyc); while (SysTick->VAL > cyc)
;
LL_GPIO_ResetOutputPin(gpioPort, gpioPin); LL_GPIO_ResetOutputPin(gpioPort, gpioPin);
if(!(mask >>= 1)) { if (!(mask >>= 1)) {
if(p >= end) break; if (p >= end)
pix = *p++; break;
pix = *p++;
mask = 0x80; mask = 0x80;
} }
while(SysTick->VAL <= cyc); while (SysTick->VAL <= cyc)
;
} }
} }
#endif // NEO_KHZ400 #endif // NEO_KHZ400
SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms
SysTick->VAL = saveVal; // Restore SysTick value SysTick->VAL = saveVal; // Restore SysTick value
#elif defined (NRF51) #elif defined(NRF51)
uint8_t *p = pixels, uint8_t *p = pixels, pix, count, mask;
pix, count, mask; int32_t num = numBytes;
int32_t num = numBytes; unsigned int bitmask = (1 << g_ADigitalPinMap[pin]);
unsigned int bitmask = ( 1 << g_ADigitalPinMap[pin] ); // https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp
// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp
volatile unsigned int *reg = (unsigned int *) (0x50000000UL + 0x508); volatile unsigned int *reg = (unsigned int *)(0x50000000UL + 0x508);
// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h // https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h
// http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1 // http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1
// https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm // https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm
asm volatile( asm volatile(
// "cpsid i" ; disable irq // "cpsid i" ; disable irq
// b .start // b .start
"b L%=_start" "\n\t" "b L%=_start"
// .nextbit: ; C0 "\n\t"
"L%=_nextbit:" "\n\t" //; C0 // .nextbit: ; C0
// str r1, [r3, #0] ; pin := hi C2 "L%=_nextbit:"
"strb %[bitmask], [%[reg], #0]" "\n\t" //; pin := hi C2 "\n\t" //; C0
// tst r6, r0 ; C3 // str r1, [r3, #0] ; pin := hi C2
"tst %[mask], %[pix]" "\n\t"// ; C3 "strb %[bitmask], [%[reg], #0]"
// bne .islate ; C4 "\n\t" //; pin := hi C2
"bne L%=_islate" "\n\t" //; C4 // tst r6, r0 ; C3
// str r1, [r2, #0] ; pin := lo C6 "tst %[mask], %[pix]"
"strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C6 "\n\t" // ; C3
// .islate: // bne .islate ; C4
"L%=_islate:" "\n\t" "bne L%=_islate"
// lsrs r6, r6, #1 ; r6 >>= 1 C7 "\n\t" //; C4
"lsr %[mask], %[mask], #1" "\n\t" //; r6 >>= 1 C7 // str r1, [r2, #0] ; pin := lo C6
// bne .justbit ; C8 "strb %[bitmask], [%[reg], #4]"
"bne L%=_justbit" "\n\t" //; C8 "\n\t" //; pin := lo C6
// .islate:
// ; not just a bit - need new byte "L%=_islate:"
// adds r4, #1 ; r4++ C9 "\n\t"
"add %[p], #1" "\n\t" //; r4++ C9 // lsrs r6, r6, #1 ; r6 >>= 1 C7
// subs r5, #1 ; r5-- C10 "lsr %[mask], %[mask], #1"
"sub %[num], #1" "\n\t" //; r5-- C10 "\n\t" //; r6 >>= 1 C7
// bcc .stop ; if (r5<0) goto .stop C11 // bne .justbit ; C8
"bcc L%=_stop" "\n\t" //; if (r5<0) goto .stop C11 "bne L%=_justbit"
// .start: "\n\t" //; C8
"L%=_start:"
// movs r6, #0x80 ; reset mask C12 // ; not just a bit - need new byte
"movs %[mask], #0x80" "\n\t" //; reset mask C12 // adds r4, #1 ; r4++ C9
// nop ; C13 "add %[p], #1"
"nop" "\n\t" //; C13 "\n\t" //; r4++ C9
// subs r5, #1 ; r5-- C10
// .common: ; C13 "sub %[num], #1"
"L%=_common:" "\n\t" //; C13 "\n\t" //; r5-- C10
// str r1, [r2, #0] ; pin := lo C15 // bcc .stop ; if (r5<0) goto .stop C11
"strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C15 "bcc L%=_stop"
// ; always re-load byte - it just fits with the cycles better this way "\n\t" //; if (r5<0) goto .stop C11
// ldrb r0, [r4, #0] ; r0 := *r4 C17 // .start:
"ldrb %[pix], [%[p], #0]" "\n\t" //; r0 := *r4 C17 "L%=_start:"
// b .nextbit ; C20 // movs r6, #0x80 ; reset mask C12
"b L%=_nextbit" "\n\t" //; C20 "movs %[mask], #0x80"
"\n\t" //; reset mask C12
// .justbit: ; C10 // nop ; C13
"L%=_justbit:" "\n\t" //; C10 "nop"
// ; no nops, branch taken is already 3 cycles "\n\t" //; C13
// b .common ; C13
"b L%=_common" "\n\t" //; C13 // .common: ; C13
"L%=_common:"
// .stop: "\n\t" //; C13
"L%=_stop:" "\n\t" // str r1, [r2, #0] ; pin := lo C15
// str r1, [r2, #0] ; pin := lo "strb %[bitmask], [%[reg], #4]"
"strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo "\n\t" //; pin := lo C15
// cpsie i ; enable irq // ; always re-load byte - it just fits with the cycles better this way
// ldrb r0, [r4, #0] ; r0 := *r4 C17
: [p] "+r" (p), "ldrb %[pix], [%[p], #0]"
[pix] "=&r" (pix), "\n\t" //; r0 := *r4 C17
[count] "=&r" (count), // b .nextbit ; C20
[mask] "=&r" (mask), "b L%=_nextbit"
[num] "+r" (num) "\n\t" //; C20
: [bitmask] "r" (bitmask),
[reg] "r" (reg) // .justbit: ; C10
); "L%=_justbit:"
"\n\t" //; C10
// ; no nops, branch taken is already 3 cycles
// b .common ; C13
"b L%=_common"
"\n\t" //; C13
// .stop:
"L%=_stop:"
"\n\t"
// str r1, [r2, #0] ; pin := lo
"strb %[bitmask], [%[reg], #4]"
"\n\t" //; pin := lo
// cpsie i ; enable irq
: [p] "+r"(p), [pix] "=&r"(pix), [count] "=&r"(count), [mask] "=&r"(mask),
[num] "+r"(num)
: [bitmask] "r"(bitmask), [reg] "r"(reg));
#elif defined(__SAM3X8E__) // Arduino Due #elif defined(__SAM3X8E__) // Arduino Due
#define SCALE VARIANT_MCK / 2UL / 1000000UL #define SCALE VARIANT_MCK / 2UL / 1000000UL
#define INST (2UL * F_CPU / VARIANT_MCK) #define INST (2UL * F_CPU / VARIANT_MCK)
#define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST)) #define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST))
#define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST)) #define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST))
#define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST)) #define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST))
#define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST)) #define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST))
#define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST)) #define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST))
#define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST)) #define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST))
int pinMask, time0, time1, period, t; int pinMask, time0, time1, period, t;
Pio *port; Pio *port;
volatile WoReg *portSet, *portClear, *timeValue, *timeReset; volatile WoReg *portSet, *portClear, *timeValue, *timeReset;
uint8_t *p, *end, pix, mask; uint8_t *p, *end, pix, mask;
pmc_set_writeprotect(false); pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t)TC3_IRQn); pmc_enable_periph_clk((uint32_t)TC3_IRQn);
TC_Configure(TC1, 0, TC_Configure(TC1, 0,
TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1); TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1);
TC_Start(TC1, 0); TC_Start(TC1, 0);
pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into
port = g_APinDescription[pin].pPort; // declarations above. Want to port = g_APinDescription[pin].pPort; // declarations above. Want to
portSet = &(port->PIO_SODR); // burn a few cycles after portSet = &(port->PIO_SODR); // burn a few cycles after
portClear = &(port->PIO_CODR); // starting timer to minimize portClear = &(port->PIO_CODR); // starting timer to minimize
timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'.
timeReset = &(TC1->TC_CHANNEL[0].TC_CCR); timeReset = &(TC1->TC_CHANNEL[0].TC_CCR);
p = pixels; p = pixels;
end = p + numBytes; end = p + numBytes;
pix = *p++; pix = *p++;
mask = 0x80; mask = 0x80;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if(is800KHz) { if (is800KHz) {
#endif #endif
time0 = TIME_800_0; time0 = TIME_800_0;
time1 = TIME_800_1; time1 = TIME_800_1;
period = PERIOD_800; period = PERIOD_800;
#if defined(NEO_KHZ400) #if defined(NEO_KHZ400)
} else { // 400 KHz bitstream } else { // 400 KHz bitstream
time0 = TIME_400_0; time0 = TIME_400_0;
time1 = TIME_400_1; time1 = TIME_400_1;
period = PERIOD_400; period = PERIOD_400;
} }
#endif #endif
for(t = time0;; t = time0) { for (t = time0;; t = time0) {
if(pix & mask) t = time1; if (pix & mask)
while(*timeValue < (unsigned)period); t = time1;
*portSet = pinMask; while (*timeValue < (unsigned)period)
;
*portSet = pinMask;
*timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG; *timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG;
while(*timeValue < (unsigned)t); while (*timeValue < (unsigned)t)
;
*portClear = pinMask; *portClear = pinMask;
if(!(mask >>= 1)) { // This 'inside-out' loop logic utilizes if (!(mask >>= 1)) { // This 'inside-out' loop logic utilizes
if(p >= end) break; // idle time to minimize inter-byte delays. if (p >= end)
break; // idle time to minimize inter-byte delays.
pix = *p++; pix = *p++;
mask = 0x80; mask = 0x80;
} }
} }
while(*timeValue < (unsigned)period); // Wait for last bit while (*timeValue < (unsigned)period)
; // Wait for last bit
TC_Stop(TC1, 0); TC_Stop(TC1, 0);
#endif // end Due #endif // end Due
// END ARM ---------------------------------------------------------------- // END ARM ----------------------------------------------------------------
#elif defined(ESP8266) || defined(ESP32) #elif defined(ESP8266) || defined(ESP32)
// ESP8266 ---------------------------------------------------------------- // ESP8266 ----------------------------------------------------------------
// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution
espShow(pin, pixels, numBytes, is800KHz); espShow(pin, pixels, numBytes, is800KHz);
...@@ -2113,94 +2927,97 @@ void Adafruit_NeoPixel::show(void) { ...@@ -2113,94 +2927,97 @@ void Adafruit_NeoPixel::show(void) {
#elif defined(KENDRYTE_K210) #elif defined(KENDRYTE_K210)
k210Show(pin, pixels, numBytes, is800KHz); k210Show(pin, pixels, numBytes, is800KHz);
#elif defined(__ARDUINO_ARC__)
// Arduino 101 ----------------------------------------------------------- #elif defined(__ARDUINO_ARC__)
#define NOPx7 { __builtin_arc_nop(); \ // Arduino 101 -----------------------------------------------------------
__builtin_arc_nop(); __builtin_arc_nop(); \
__builtin_arc_nop(); __builtin_arc_nop(); \ #define NOPx7 \
__builtin_arc_nop(); __builtin_arc_nop(); } { \
__builtin_arc_nop(); \
__builtin_arc_nop(); \
__builtin_arc_nop(); \
__builtin_arc_nop(); \
__builtin_arc_nop(); \
__builtin_arc_nop(); \
__builtin_arc_nop(); \
}
PinDescription *pindesc = &g_APinDescription[pin]; PinDescription *pindesc = &g_APinDescription[pin];
register uint32_t loop = 8 * numBytes; // one loop to handle all bytes and all bits register uint32_t loop =
8 * numBytes; // one loop to handle all bytes and all bits
register uint8_t *p = pixels; register uint8_t *p = pixels;
register uint32_t currByte = (uint32_t) (*p); register uint32_t currByte = (uint32_t)(*p);
register uint32_t currBit = 0x80 & currByte; register uint32_t currBit = 0x80 & currByte;
register uint32_t bitCounter = 0; register uint32_t bitCounter = 0;
register uint32_t first = 1; register uint32_t first = 1;
// The loop is unusual. Very first iteration puts all the way LOW to the wire - // The loop is unusual. Very first iteration puts all the way LOW to the wire
// constant LOW does not affect NEOPIXEL, so there is no visible effect displayed. // - constant LOW does not affect NEOPIXEL, so there is no visible effect
// During that very first iteration CPU caches instructions in the loop. // displayed. During that very first iteration CPU caches instructions in the
// Because of the caching process, "CPU slows down". NEOPIXEL pulse is very time sensitive // loop. Because of the caching process, "CPU slows down". NEOPIXEL pulse is
// that's why we let the CPU cache first and we start regular pulse from 2nd iteration // very time sensitive that's why we let the CPU cache first and we start
// regular pulse from 2nd iteration
if (pindesc->ulGPIOType == SS_GPIO) { if (pindesc->ulGPIOType == SS_GPIO) {
register uint32_t reg = pindesc->ulGPIOBase + SS_GPIO_SWPORTA_DR; register uint32_t reg = pindesc->ulGPIOBase + SS_GPIO_SWPORTA_DR;
uint32_t reg_val = __builtin_arc_lr((volatile uint32_t)reg); uint32_t reg_val = __builtin_arc_lr((volatile uint32_t)reg);
register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId);
register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId);
loop += 1; // include first, special iteration loop += 1; // include first, special iteration
while(loop--) { while (loop--) {
if(!first) { if (!first) {
currByte <<= 1; currByte <<= 1;
bitCounter++; bitCounter++;
} }
// 1 is >550ns high and >450ns low; 0 is 200..500ns high and >450ns low // 1 is >550ns high and >450ns low; 0 is 200..500ns high and >450ns low
__builtin_arc_sr(first ? reg_bit_low : reg_bit_high, (volatile uint32_t)reg); __builtin_arc_sr(first ? reg_bit_low : reg_bit_high,
if(currBit) { // ~400ns HIGH (740ns overall) (volatile uint32_t)reg);
NOPx7 if (currBit) { // ~400ns HIGH (740ns overall)
NOPx7 NOPx7 NOPx7
} }
// ~340ns HIGH // ~340ns HIGH
NOPx7 NOPx7 __builtin_arc_nop();
__builtin_arc_nop();
// 820ns LOW; per spec, max allowed low here is 5000ns */ // 820ns LOW; per spec, max allowed low here is 5000ns */
__builtin_arc_sr(reg_bit_low, (volatile uint32_t)reg); __builtin_arc_sr(reg_bit_low, (volatile uint32_t)reg);
NOPx7 NOPx7 NOPx7
NOPx7
if(bitCounter >= 8) { if (bitCounter >= 8) {
bitCounter = 0; bitCounter = 0;
currByte = (uint32_t) (*++p); currByte = (uint32_t)(*++p);
} }
currBit = 0x80 & currByte; currBit = 0x80 & currByte;
first = 0; first = 0;
} }
} else if(pindesc->ulGPIOType == SOC_GPIO) { } else if (pindesc->ulGPIOType == SOC_GPIO) {
register uint32_t reg = pindesc->ulGPIOBase + SOC_GPIO_SWPORTA_DR; register uint32_t reg = pindesc->ulGPIOBase + SOC_GPIO_SWPORTA_DR;
uint32_t reg_val = MMIO_REG_VAL(reg); uint32_t reg_val = MMIO_REG_VAL(reg);
register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId);
register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId);
loop += 1; // include first, special iteration loop += 1; // include first, special iteration
while(loop--) { while (loop--) {
if(!first) { if (!first) {
currByte <<= 1; currByte <<= 1;
bitCounter++; bitCounter++;
} }
MMIO_REG_VAL(reg) = first ? reg_bit_low : reg_bit_high; MMIO_REG_VAL(reg) = first ? reg_bit_low : reg_bit_high;
if(currBit) { // ~430ns HIGH (740ns overall) if (currBit) { // ~430ns HIGH (740ns overall)
NOPx7 NOPx7 NOPx7 __builtin_arc_nop();
NOPx7
__builtin_arc_nop();
} }
// ~310ns HIGH // ~310ns HIGH
NOPx7 NOPx7
// 850ns LOW; per spec, max allowed low here is 5000ns */ // 850ns LOW; per spec, max allowed low here is 5000ns */
MMIO_REG_VAL(reg) = reg_bit_low; MMIO_REG_VAL(reg) = reg_bit_low;
NOPx7 NOPx7 NOPx7
NOPx7
if(bitCounter >= 8) { if (bitCounter >= 8) {
bitCounter = 0; bitCounter = 0;
currByte = (uint32_t) (*++p); currByte = (uint32_t)(*++p);
} }
currBit = 0x80 & currByte; currBit = 0x80 & currByte;
...@@ -2212,10 +3029,9 @@ void Adafruit_NeoPixel::show(void) { ...@@ -2212,10 +3029,9 @@ void Adafruit_NeoPixel::show(void) {
#error Architecture not supported #error Architecture not supported
#endif #endif
// END ARCHITECTURE SELECT ------------------------------------------------
// END ARCHITECTURE SELECT ------------------------------------------------ #if !(defined(NRF52) || defined(NRF52_SERIES))
#if !( defined(NRF52) || defined(NRF52_SERIES) )
interrupts(); interrupts();
#endif #endif
...@@ -2227,15 +3043,16 @@ void Adafruit_NeoPixel::show(void) { ...@@ -2227,15 +3043,16 @@ void Adafruit_NeoPixel::show(void) {
if any, is set to INPUT and the new pin is set to OUTPUT. if any, is set to INPUT and the new pin is set to OUTPUT.
@param p Arduino pin number (-1 = no pin). @param p Arduino pin number (-1 = no pin).
*/ */
void Adafruit_NeoPixel::setPin(uint16_t p) { void Adafruit_NeoPixel::setPin(int16_t p) {
if(begun && (pin >= 0)) pinMode(pin, INPUT); if (begun && (pin >= 0))
pinMode(pin, INPUT); // Disable existing out pin
pin = p; pin = p;
if(begun) { if (begun) {
pinMode(p, OUTPUT); pinMode(p, OUTPUT);
digitalWrite(p, LOW); digitalWrite(p, LOW);
} }
#if defined(__AVR__) #if defined(__AVR__)
port = portOutputRegister(digitalPinToPort(p)); port = portOutputRegister(digitalPinToPort(p));
pinMask = digitalPinToBitMask(p); pinMask = digitalPinToBitMask(p);
#endif #endif
#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) #if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
...@@ -2252,23 +3069,23 @@ void Adafruit_NeoPixel::setPin(uint16_t p) { ...@@ -2252,23 +3069,23 @@ void Adafruit_NeoPixel::setPin(uint16_t p) {
@param g Green brightness, 0 = minimum (off), 255 = maximum. @param g Green brightness, 0 = minimum (off), 255 = maximum.
@param b Blue brightness, 0 = minimum (off), 255 = maximum. @param b Blue brightness, 0 = minimum (off), 255 = maximum.
*/ */
void Adafruit_NeoPixel::setPixelColor( void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g,
uint16_t n, uint8_t r, uint8_t g, uint8_t b) { uint8_t b) {
if(n < numLEDs) { if (n < numLEDs) {
if(brightness) { // See notes in setBrightness() if (brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8; r = (r * brightness) >> 8;
g = (g * brightness) >> 8; g = (g * brightness) >> 8;
b = (b * brightness) >> 8; b = (b * brightness) >> 8;
} }
uint8_t *p; uint8_t *p;
if(wOffset == rOffset) { // Is an RGB-type strip if (wOffset == rOffset) { // Is an RGB-type strip
p = &pixels[n * 3]; // 3 bytes per pixel p = &pixels[n * 3]; // 3 bytes per pixel
} else { // Is a WRGB-type strip } else { // Is a WRGB-type strip
p = &pixels[n * 4]; // 4 bytes per pixel p = &pixels[n * 4]; // 4 bytes per pixel
p[wOffset] = 0; // But only R,G,B passed -- set W to 0 p[wOffset] = 0; // But only R,G,B passed -- set W to 0
} }
p[rOffset] = r; // R,G,B always stored p[rOffset] = r; // R,G,B always stored
p[gOffset] = g; p[gOffset] = g;
p[bOffset] = b; p[bOffset] = b;
} }
...@@ -2284,24 +3101,24 @@ void Adafruit_NeoPixel::setPixelColor( ...@@ -2284,24 +3101,24 @@ void Adafruit_NeoPixel::setPixelColor(
@param w White brightness, 0 = minimum (off), 255 = maximum, ignored @param w White brightness, 0 = minimum (off), 255 = maximum, ignored
if using RGB pixels. if using RGB pixels.
*/ */
void Adafruit_NeoPixel::setPixelColor( void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g,
uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { uint8_t b, uint8_t w) {
if(n < numLEDs) { if (n < numLEDs) {
if(brightness) { // See notes in setBrightness() if (brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8; r = (r * brightness) >> 8;
g = (g * brightness) >> 8; g = (g * brightness) >> 8;
b = (b * brightness) >> 8; b = (b * brightness) >> 8;
w = (w * brightness) >> 8; w = (w * brightness) >> 8;
} }
uint8_t *p; uint8_t *p;
if(wOffset == rOffset) { // Is an RGB-type strip if (wOffset == rOffset) { // Is an RGB-type strip
p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) p = &pixels[n * 3]; // 3 bytes per pixel (ignore W)
} else { // Is a WRGB-type strip } else { // Is a WRGB-type strip
p = &pixels[n * 4]; // 4 bytes per pixel p = &pixels[n * 4]; // 4 bytes per pixel
p[wOffset] = w; // Store W p[wOffset] = w; // Store W
} }
p[rOffset] = r; // Store R,G,B p[rOffset] = r; // Store R,G,B
p[gOffset] = g; p[gOffset] = g;
p[bOffset] = b; p[bOffset] = b;
} }
...@@ -2315,17 +3132,14 @@ void Adafruit_NeoPixel::setPixelColor( ...@@ -2315,17 +3132,14 @@ void Adafruit_NeoPixel::setPixelColor(
and least significant byte is blue. and least significant byte is blue.
*/ */
void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
if(n < numLEDs) { if (n < numLEDs) {
uint8_t *p, uint8_t *p, r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c;
r = (uint8_t)(c >> 16), if (brightness) { // See notes in setBrightness()
g = (uint8_t)(c >> 8),
b = (uint8_t)c;
if(brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8; r = (r * brightness) >> 8;
g = (g * brightness) >> 8; g = (g * brightness) >> 8;
b = (b * brightness) >> 8; b = (b * brightness) >> 8;
} }
if(wOffset == rOffset) { if (wOffset == rOffset) {
p = &pixels[n * 3]; p = &pixels[n * 3];
} else { } else {
p = &pixels[n * 4]; p = &pixels[n * 4];
...@@ -2352,21 +3166,22 @@ void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { ...@@ -2352,21 +3166,22 @@ void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) { void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) {
uint16_t i, end; uint16_t i, end;
if(first >= numLEDs) { if (first >= numLEDs) {
return; // If first LED is past end of strip, nothing to do return; // If first LED is past end of strip, nothing to do
} }
// Calculate the index ONE AFTER the last pixel to fill // Calculate the index ONE AFTER the last pixel to fill
if(count == 0) { if (count == 0) {
// Fill to end of strip // Fill to end of strip
end = numLEDs; end = numLEDs;
} else { } else {
// Ensure that the loop won't go past the last pixel // Ensure that the loop won't go past the last pixel
end = first + count; end = first + count;
if(end > numLEDs) end = numLEDs; if (end > numLEDs)
end = numLEDs;
} }
for(i = first; i < end; i++) { for (i = first; i < end; i++) {
this->setPixelColor(i, c); this->setPixelColor(i, c);
} }
} }
...@@ -2429,45 +3244,45 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { ...@@ -2429,45 +3244,45 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
// the constants below are not the multiples of 256 you might expect. // the constants below are not the multiples of 256 you might expect.
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch): // Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
if(hue < 510) { // Red to Green-1 if (hue < 510) { // Red to Green-1
b = 0; b = 0;
if(hue < 255) { // Red to Yellow-1 if (hue < 255) { // Red to Yellow-1
r = 255; r = 255;
g = hue; // g = 0 to 254 g = hue; // g = 0 to 254
} else { // Yellow to Green-1 } else { // Yellow to Green-1
r = 510 - hue; // r = 255 to 1 r = 510 - hue; // r = 255 to 1
g = 255; g = 255;
} }
} else if(hue < 1020) { // Green to Blue-1 } else if (hue < 1020) { // Green to Blue-1
r = 0; r = 0;
if(hue < 765) { // Green to Cyan-1 if (hue < 765) { // Green to Cyan-1
g = 255; g = 255;
b = hue - 510; // b = 0 to 254 b = hue - 510; // b = 0 to 254
} else { // Cyan to Blue-1 } else { // Cyan to Blue-1
g = 1020 - hue; // g = 255 to 1 g = 1020 - hue; // g = 255 to 1
b = 255; b = 255;
} }
} else if(hue < 1530) { // Blue to Red-1 } else if (hue < 1530) { // Blue to Red-1
g = 0; g = 0;
if(hue < 1275) { // Blue to Magenta-1 if (hue < 1275) { // Blue to Magenta-1
r = hue - 1020; // r = 0 to 254 r = hue - 1020; // r = 0 to 254
b = 255; b = 255;
} else { // Magenta to Red-1 } else { // Magenta to Red-1
r = 255; r = 255;
b = 1530 - hue; // b = 255 to 1 b = 1530 - hue; // b = 255 to 1
} }
} else { // Last 0.5 Red (quicker than % operator) } else { // Last 0.5 Red (quicker than % operator)
r = 255; r = 255;
g = b = 0; g = b = 0;
} }
// Apply saturation and value to R,G,B, pack into 32-bit result: // Apply saturation and value to R,G,B, pack into 32-bit result:
uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
uint16_t s1 = 1 + sat; // 1 to 256; same reason uint16_t s1 = 1 + sat; // 1 to 256; same reason
uint8_t s2 = 255 - sat; // 255 to 0 uint8_t s2 = 255 - sat; // 255 to 0
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
(((((g * s1) >> 8) + s2) * v1) & 0xff00) | (((((g * s1) >> 8) + s2) * v1) & 0xff00) |
( ((((b * s1) >> 8) + s2) * v1) >> 8); (((((b * s1) >> 8) + s2) * v1) >> 8);
} }
/*! /*!
...@@ -2482,44 +3297,41 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { ...@@ -2482,44 +3297,41 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
This gets more pronounced at lower brightness levels. This gets more pronounced at lower brightness levels.
*/ */
uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const { uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const {
if(n >= numLEDs) return 0; // Out of bounds, return no color. if (n >= numLEDs)
return 0; // Out of bounds, return no color.
uint8_t *p; uint8_t *p;
if(wOffset == rOffset) { // Is RGB-type device if (wOffset == rOffset) { // Is RGB-type device
p = &pixels[n * 3]; p = &pixels[n * 3];
if(brightness) { if (brightness) {
// Stored color was decimated by setBrightness(). Returned value // Stored color was decimated by setBrightness(). Returned value
// attempts to scale back to an approximation of the original 24-bit // attempts to scale back to an approximation of the original 24-bit
// value used when setting the pixel color, but there will always be // value used when setting the pixel color, but there will always be
// some error -- those bits are simply gone. Issue is most // some error -- those bits are simply gone. Issue is most
// pronounced at low brightness levels. // pronounced at low brightness levels.
return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
(((uint32_t)(p[gOffset] << 8) / brightness) << 8) | (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
( (uint32_t)(p[bOffset] << 8) / brightness ); ((uint32_t)(p[bOffset] << 8) / brightness);
} else { } else {
// No brightness adjustment has been made -- return 'raw' color // No brightness adjustment has been made -- return 'raw' color
return ((uint32_t)p[rOffset] << 16) | return ((uint32_t)p[rOffset] << 16) | ((uint32_t)p[gOffset] << 8) |
((uint32_t)p[gOffset] << 8) | (uint32_t)p[bOffset];
(uint32_t)p[bOffset];
} }
} else { // Is RGBW-type device } else { // Is RGBW-type device
p = &pixels[n * 4]; p = &pixels[n * 4];
if(brightness) { // Return scaled color if (brightness) { // Return scaled color
return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) | return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) |
(((uint32_t)(p[rOffset] << 8) / brightness) << 16) | (((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
(((uint32_t)(p[gOffset] << 8) / brightness) << 8) | (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
( (uint32_t)(p[bOffset] << 8) / brightness ); ((uint32_t)(p[bOffset] << 8) / brightness);
} else { // Return raw color } else { // Return raw color
return ((uint32_t)p[wOffset] << 24) | return ((uint32_t)p[wOffset] << 24) | ((uint32_t)p[rOffset] << 16) |
((uint32_t)p[rOffset] << 16) | ((uint32_t)p[gOffset] << 8) | (uint32_t)p[bOffset];
((uint32_t)p[gOffset] << 8) |
(uint32_t)p[bOffset];
} }
} }
} }
/*! /*!
@brief Adjust output brightness. Does not immediately affect what's @brief Adjust output brightness. Does not immediately affect what's
currently displayed on the LEDs. The next call to show() will currently displayed on the LEDs. The next call to show() will
...@@ -2543,7 +3355,7 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) { ...@@ -2543,7 +3355,7 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) {
// (color values are interpreted literally; no scaling), 1 = min // (color values are interpreted literally; no scaling), 1 = min
// brightness (off), 255 = just below max brightness. // brightness (off), 255 = just below max brightness.
uint8_t newBrightness = b + 1; uint8_t newBrightness = b + 1;
if(newBrightness != brightness) { // Compare against prior value if (newBrightness != brightness) { // Compare against prior value
// Brightness has changed -- re-scale existing data in RAM, // Brightness has changed -- re-scale existing data in RAM,
// This process is potentially "lossy," especially when increasing // This process is potentially "lossy," especially when increasing
// brightness. The tight timing in the WS2811/WS2812 code means there // brightness. The tight timing in the WS2811/WS2812 code means there
...@@ -2554,15 +3366,17 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) { ...@@ -2554,15 +3366,17 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) {
// the limited number of steps (quantization) in the old data will be // the limited number of steps (quantization) in the old data will be
// quite visible in the re-scaled version. For a non-destructive // quite visible in the re-scaled version. For a non-destructive
// change, you'll need to re-render the full strip data. C'est la vie. // change, you'll need to re-render the full strip data. C'est la vie.
uint8_t c, uint8_t c, *ptr = pixels,
*ptr = pixels, oldBrightness = brightness - 1; // De-wrap old brightness value
oldBrightness = brightness - 1; // De-wrap old brightness value
uint16_t scale; uint16_t scale;
if(oldBrightness == 0) scale = 0; // Avoid /0 if (oldBrightness == 0)
else if(b == 255) scale = 65535 / oldBrightness; scale = 0; // Avoid /0
else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; else if (b == 255)
for(uint16_t i=0; i<numBytes; i++) { scale = 65535 / oldBrightness;
c = *ptr; else
scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
for (uint16_t i = 0; i < numBytes; i++) {
c = *ptr;
*ptr++ = (c * scale) >> 8; *ptr++ = (c * scale) >> 8;
} }
brightness = newBrightness; brightness = newBrightness;
...@@ -2573,16 +3387,12 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) { ...@@ -2573,16 +3387,12 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) {
@brief Retrieve the last-set brightness value for the strip. @brief Retrieve the last-set brightness value for the strip.
@return Brightness value: 0 = minimum (off), 255 = maximum. @return Brightness value: 0 = minimum (off), 255 = maximum.
*/ */
uint8_t Adafruit_NeoPixel::getBrightness(void) const { uint8_t Adafruit_NeoPixel::getBrightness(void) const { return brightness - 1; }
return brightness - 1;
}
/*! /*!
@brief Fill the whole NeoPixel strip with 0 / black / off. @brief Fill the whole NeoPixel strip with 0 / black / off.
*/ */
void Adafruit_NeoPixel::clear(void) { void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); }
memset(pixels, 0, numBytes);
}
// A 32-bit variant of gamma8() that applies the same function // A 32-bit variant of gamma8() that applies the same function
// to all components of a packed RGB or WRGB value. // to all components of a packed RGB or WRGB value.
...@@ -2596,6 +3406,35 @@ uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) { ...@@ -2596,6 +3406,35 @@ uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) {
// someone's storing information in the unused most significant byte // someone's storing information in the unused most significant byte
// of an RGB value, but this seems exceedingly rare and if it's // of an RGB value, but this seems exceedingly rare and if it's
// encountered in reality they can mask values going in or coming out. // encountered in reality they can mask values going in or coming out.
for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]); for (uint8_t i = 0; i < 4; i++)
y[i] = gamma8(y[i]);
return x; // Packed 32-bit return return x; // Packed 32-bit return
} }
/*!
@brief Fill NeoPixel strip with one or more cycles of hues.
Everyone loves the rainbow swirl so much, now it's canon!
@param first_hue Hue of first pixel, 0-65535, representing one full
cycle of the color wheel. Each subsequent pixel will
be offset to complete one or more cycles over the
length of the strip.
@param reps Number of cycles of the color wheel over the length
of the strip. Default is 1. Negative values can be
used to reverse the hue order.
@param saturation Saturation (optional), 0-255 = gray to pure hue,
default = 255.
@param brightness Brightness/value (optional), 0-255 = off to max,
default = 255. This is distinct and in combination
with any configured global strip brightness.
@param gammify If true (default), apply gamma correction to colors
for better appearance.
*/
void Adafruit_NeoPixel::rainbow(uint16_t first_hue, int8_t reps,
uint8_t saturation, uint8_t brightness, bool gammify) {
for (uint16_t i=0; i<numLEDs; i++) {
uint16_t hue = first_hue + (i * reps * 65536) / numLEDs;
uint32_t color = ColorHSV(hue, saturation, brightness);
if (gammify) color = gamma32(color);
setPixelColor(i, color);
}
}
...@@ -37,16 +37,28 @@ ...@@ -37,16 +37,28 @@
#define ADAFRUIT_NEOPIXEL_H #define ADAFRUIT_NEOPIXEL_H
#ifdef ARDUINO #ifdef ARDUINO
#if (ARDUINO >= 100) #if (ARDUINO >= 100)
#include <Arduino.h> #include <Arduino.h>
#else #else
#include <WProgram.h> #include <WProgram.h>
#include <pins_arduino.h> #include <pins_arduino.h>
#endif #endif
#ifdef USE_TINYUSB // For Serial when selecting TinyUSB
#include <Adafruit_TinyUSB.h>
#endif
#endif #endif
#ifdef TARGET_LPC1768 #ifdef TARGET_LPC1768
#include <Arduino.h> #include <Arduino.h>
#endif
#if defined(ARDUINO_ARCH_RP2040)
#include <stdlib.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "rp2040_pio.h"
#endif #endif
// The order of primary colors in the NeoPixel data stream can vary among // The order of primary colors in the NeoPixel data stream can vary among
...@@ -76,42 +88,42 @@ ...@@ -76,42 +88,42 @@
// RGB NeoPixel permutations; white and red offsets are always same // RGB NeoPixel permutations; white and red offsets are always same
// Offset: W R G B // Offset: W R G B
#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B #define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B
#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G #define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G
#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B #define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B
#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R #define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R
#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G #define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G
#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R #define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R
// RGBW NeoPixel permutations; all 4 offsets are distinct // RGBW NeoPixel permutations; all 4 offsets are distinct
// Offset: W R G B // Offset: W R G B
#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B #define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) ///< Transmit as W,R,G,B
#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G #define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) ///< Transmit as W,R,B,G
#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B #define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) ///< Transmit as W,G,R,B
#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R #define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) ///< Transmit as W,G,B,R
#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G #define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) ///< Transmit as W,B,R,G
#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R #define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) ///< Transmit as W,B,G,R
#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B #define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) ///< Transmit as R,W,G,B
#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G #define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) ///< Transmit as R,W,B,G
#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B #define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) ///< Transmit as R,G,W,B
#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W #define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B,W
#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G #define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) ///< Transmit as R,B,W,G
#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W #define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G,W
#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B #define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) ///< Transmit as G,W,R,B
#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R #define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) ///< Transmit as G,W,B,R
#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B #define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) ///< Transmit as G,R,W,B
#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W #define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B,W
#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R #define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,W,R
#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W #define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R,W
#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G #define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) ///< Transmit as B,W,R,G
#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R #define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) ///< Transmit as B,W,G,R
#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G #define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) ///< Transmit as B,R,W,G
#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W #define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G,W
#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R #define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,W,R
#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W #define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R,W
// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device. // Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is // All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
...@@ -134,7 +146,7 @@ ...@@ -134,7 +146,7 @@
#ifdef NEO_KHZ400 #ifdef NEO_KHZ400
typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
#else #else
typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
#endif #endif
// These two tables are declared outside the Adafruit_NeoPixel class // These two tables are declared outside the Adafruit_NeoPixel class
...@@ -149,22 +161,24 @@ for x in range(256): ...@@ -149,22 +161,24 @@ for x in range(256):
if x&15 == 15: print if x&15 == 15: print
*/ */
static const uint8_t PROGMEM _NeoPixelSineTable[256] = { static const uint8_t PROGMEM _NeoPixelSineTable[256] = {
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170,
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211,
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240,
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254,
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251,
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232,
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198,
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155,
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109,
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65,
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29,
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6,
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0,
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11,
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37,
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124}; 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121,
124};
/* Similar to above, but for an 8-bit gamma-correction table. /* Similar to above, but for an 8-bit gamma-correction table.
Copy & paste this snippet into a Python REPL to regenerate: Copy & paste this snippet into a Python REPL to regenerate:
...@@ -175,49 +189,49 @@ for x in range(256): ...@@ -175,49 +189,49 @@ for x in range(256):
if x&15 == 15: print if x&15 == 15: print
*/ */
static const uint8_t PROGMEM _NeoPixelGammaTable[256] = { static const uint8_t PROGMEM _NeoPixelGammaTable[256] = {
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, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17,
20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48,
58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81,
97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102,
122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148, 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125,
150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180, 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152,
182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182,
218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255}; 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215,
218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252,
255};
/*! /*!
@brief Class that stores state and functions for interacting with @brief Class that stores state and functions for interacting with
Adafruit NeoPixels and compatible devices. Adafruit NeoPixels and compatible devices.
*/ */
class Adafruit_NeoPixel { class Adafruit_NeoPixel {
public: public:
// Constructor: number of LEDs, pin number, LED type // Constructor: number of LEDs, pin number, LED type
Adafruit_NeoPixel(uint16_t n, uint16_t pin=6, Adafruit_NeoPixel(uint16_t n, int16_t pin = 6,
neoPixelType type=NEO_GRB + NEO_KHZ800); neoPixelType type = NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel(void); Adafruit_NeoPixel(void);
~Adafruit_NeoPixel(); ~Adafruit_NeoPixel();
void begin(void); void begin(void);
void show(void); void show(void);
void setPin(uint16_t p); void setPin(int16_t p);
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
uint8_t w); void setPixelColor(uint16_t n, uint32_t c);
void setPixelColor(uint16_t n, uint32_t c); void fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0);
void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0); void setBrightness(uint8_t);
void setBrightness(uint8_t); void clear(void);
void clear(void); void updateLength(uint16_t n);
void updateLength(uint16_t n); void updateType(neoPixelType t);
void updateType(neoPixelType t);
/*! /*!
@brief Check whether a call to show() will start sending data @brief Check whether a call to show() will start sending data
immediately or will 'block' for a required interval. NeoPixels immediately or will 'block' for a required interval. NeoPixels
...@@ -232,10 +246,26 @@ class Adafruit_NeoPixel { ...@@ -232,10 +246,26 @@ class Adafruit_NeoPixel {
if show() would block (meaning some idle time is available). if show() would block (meaning some idle time is available).
*/ */
bool canShow(void) { bool canShow(void) {
if (endTime > micros()) { // It's normal and possible for endTime to exceed micros() if the
endTime = micros(); // 32-bit clock counter has rolled over (about every 70 minutes).
// Since both are uint32_t, a negative delta correctly maps back to
// positive space, and it would seem like the subtraction below would
// suffice. But a problem arises if code invokes show() very
// infrequently...the micros() counter may roll over MULTIPLE times in
// that interval, the delta calculation is no longer correct and the
// next update may stall for a very long time. The check below resets
// the latch counter if a rollover has occurred. This can cause an
// extra delay of up to 300 microseconds in the rare case where a
// show() call happens precisely around the rollover, but that's
// neither likely nor especially harmful, vs. other code that might
// stall for 30+ minutes, or having to document and frequently remind
// and/or provide tech support explaining an unintuitive need for
// show() calls at least once an hour.
uint32_t now = micros();
if (endTime > now) {
endTime = now;
} }
return (micros() - endTime) >= 300L; return (now - endTime) >= 300L;
} }
/*! /*!
@brief Get a pointer directly to the NeoPixel data buffer in RAM. @brief Get a pointer directly to the NeoPixel data buffer in RAM.
...@@ -251,19 +281,19 @@ class Adafruit_NeoPixel { ...@@ -251,19 +281,19 @@ class Adafruit_NeoPixel {
writes past the ends of the buffer. Great power, great writes past the ends of the buffer. Great power, great
responsibility and all that. responsibility and all that.
*/ */
uint8_t *getPixels(void) const { return pixels; }; uint8_t *getPixels(void) const { return pixels; };
uint8_t getBrightness(void) const; uint8_t getBrightness(void) const;
/*! /*!
@brief Retrieve the pin number used for NeoPixel data output. @brief Retrieve the pin number used for NeoPixel data output.
@return Arduino pin number (-1 if not set). @return Arduino pin number (-1 if not set).
*/ */
int16_t getPin(void) const { return pin; }; int16_t getPin(void) const { return pin; };
/*! /*!
@brief Return the number of pixels in an Adafruit_NeoPixel strip object. @brief Return the number of pixels in an Adafruit_NeoPixel strip object.
@return Pixel count (0 if not set). @return Pixel count (0 if not set).
*/ */
uint16_t numPixels(void) const { return numLEDs; } uint16_t numPixels(void) const { return numLEDs; }
uint32_t getPixelColor(uint16_t n) const; uint32_t getPixelColor(uint16_t n) const;
/*! /*!
@brief An 8-bit integer sine wave function, not directly compatible @brief An 8-bit integer sine wave function, not directly compatible
with standard trigonometric units like radians or degrees. with standard trigonometric units like radians or degrees.
...@@ -276,7 +306,7 @@ class Adafruit_NeoPixel { ...@@ -276,7 +306,7 @@ class Adafruit_NeoPixel {
a signed int8_t, but you'll most likely want unsigned as this a signed int8_t, but you'll most likely want unsigned as this
output is often used for pixel brightness in animation effects. output is often used for pixel brightness in animation effects.
*/ */
static uint8_t sine8(uint8_t x) { static uint8_t sine8(uint8_t x) {
return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
} }
/*! /*!
...@@ -290,7 +320,7 @@ class Adafruit_NeoPixel { ...@@ -290,7 +320,7 @@ class Adafruit_NeoPixel {
NeoPixels in average tasks. If you need finer control you'll NeoPixels in average tasks. If you need finer control you'll
need to provide your own gamma-correction function instead. need to provide your own gamma-correction function instead.
*/ */
static uint8_t gamma8(uint8_t x) { static uint8_t gamma8(uint8_t x) {
return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out
} }
/*! /*!
...@@ -304,8 +334,8 @@ class Adafruit_NeoPixel { ...@@ -304,8 +334,8 @@ class Adafruit_NeoPixel {
function. Packed RGB format is predictable, regardless of function. Packed RGB format is predictable, regardless of
LED strand color order. LED strand color order.
*/ */
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
} }
/*! /*!
@brief Convert separate red, green, blue and white values into a @brief Convert separate red, green, blue and white values into a
...@@ -319,10 +349,10 @@ class Adafruit_NeoPixel { ...@@ -319,10 +349,10 @@ class Adafruit_NeoPixel {
function. Packed WRGB format is predictable, regardless of function. Packed WRGB format is predictable, regardless of
LED strand color order. LED strand color order.
*/ */
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
} }
static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255); static uint32_t ColorHSV(uint16_t hue, uint8_t sat = 255, uint8_t val = 255);
/*! /*!
@brief A gamma-correction function for 32-bit packed RGB or WRGB @brief A gamma-correction function for 32-bit packed RGB or WRGB
colors. Makes color transitions appear more perceptially colors. Makes color transitions appear more perceptially
...@@ -335,31 +365,45 @@ class Adafruit_NeoPixel { ...@@ -335,31 +365,45 @@ class Adafruit_NeoPixel {
control you'll need to provide your own gamma-correction control you'll need to provide your own gamma-correction
function instead. function instead.
*/ */
static uint32_t gamma32(uint32_t x); static uint32_t gamma32(uint32_t x);
protected: void rainbow(uint16_t first_hue = 0, int8_t reps = 1,
uint8_t saturation = 255, uint8_t brightness = 255,
bool gammify = true);
#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled... private:
bool is800KHz; ///< true if 800 KHz pixels #if defined(ARDUINO_ARCH_RP2040)
void rp2040Init(uint8_t pin, bool is800KHz);
void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
#endif #endif
bool begun; ///< true if begin() previously called
uint16_t numLEDs; ///< Number of RGB LEDs in strip protected:
uint16_t numBytes; ///< Size of 'pixels' buffer below #ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled...
int16_t pin; ///< Output pin number (-1 if not yet set) bool is800KHz; ///< true if 800 KHz pixels
uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) #endif
uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) bool begun; ///< true if begin() previously called
uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel uint16_t numLEDs; ///< Number of RGB LEDs in strip
uint8_t gOffset; ///< Index of green byte uint16_t numBytes; ///< Size of 'pixels' buffer below
uint8_t bOffset; ///< Index of blue byte int16_t pin; ///< Output pin number (-1 if not yet set)
uint8_t wOffset; ///< Index of white (==rOffset if no white) uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
uint32_t endTime; ///< Latch timing reference uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
uint8_t gOffset; ///< Index of green byte
uint8_t bOffset; ///< Index of blue byte
uint8_t wOffset; ///< Index of white (==rOffset if no white)
uint32_t endTime; ///< Latch timing reference
#ifdef __AVR__ #ifdef __AVR__
volatile uint8_t *port; ///< Output PORT register volatile uint8_t *port; ///< Output PORT register
uint8_t pinMask; ///< Output PORT bitmask uint8_t pinMask; ///< Output PORT bitmask
#endif #endif
#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) #if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
GPIO_TypeDef *gpioPort; ///< Output GPIO PORT GPIO_TypeDef *gpioPort; ///< Output GPIO PORT
uint32_t gpioPin; ///< Output GPIO PIN uint32_t gpioPin; ///< Output GPIO PIN
#endif
#if defined(ARDUINO_ARCH_RP2040)
PIO pio = pio0;
int sm = 0;
bool init = true;
#endif #endif
}; };
......
...@@ -56,6 +56,10 @@ Compatibility notes: Port A is not supported on any AVR processors at this time ...@@ -56,6 +56,10 @@ Compatibility notes: Port A is not supported on any AVR processors at this time
- ESP8266 any speed - ESP8266 any speed
- ESP32 any speed - ESP32 any speed
- Nordic nRF52 (Adafruit Feather nRF52), nRF51 (micro:bit) - Nordic nRF52 (Adafruit Feather nRF52), nRF51 (micro:bit)
- Infineon XMC1100 BootKit @ 32 MHz
- Infineon XMC1100 2Go @ 32 MHz
- Infineon XMC1300 BootKit @ 32 MHz
- Infineon XMC4700 RelaxKit, XMC4800 RelaxKit, XMC4800 IoT Amazon FreeRTOS Kit @ 144 MHz
Check forks for other architectures not listed here! Check forks for other architectures not listed here!
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment