update libraries E-I

This commit is contained in:
rob tillaart 2020-11-27 11:16:22 +01:00
parent a19ec7f025
commit d13cd04dac
113 changed files with 4239 additions and 1599 deletions

View File

@ -1,17 +1,20 @@
//
// FILE: FRAM.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: Class for FRAM memory
// URL:
// VERSION: 0.2.1
// DATE: 2018-01-24
// PURPOSE: Arduino library for I2C FRAM
// URL: https://github.com/RobTillaart/FRAM_I2C
//
// HISTORY:
// 0.1.0 initial version
// 0.1.1 added suppport for Fujitsu 64Kbit MB85RC64T (kudos ysoyipek)
// 0.1.0 2018-01-24 initial version
// 0.1.1 2019-07-31 added suppport for Fujitsu 64Kbit MB85RC64T (kudos ysoyipek)
// 0.2.0 2020-04-30 refactor, add writeProtectPin code
// 0.2.1 2020-06-10 fix library.json
#include "FRAM.h"
#define FRAM_SLAVE_ID_ 0x7C
const uint8_t FRAM_SLAVE_ID_= 0x7C;
/////////////////////////////////////////////////////
//
@ -20,7 +23,7 @@
FRAM::FRAM()
{}
int FRAM::begin(int address = 0X50)
int FRAM::begin(uint8_t address, int8_t writeProtectPin)
{
if (address < 0x50 || address > 0x57)
{
@ -30,18 +33,11 @@ int FRAM::begin(int address = 0X50)
_address = address;
Wire.begin();
uint16_t mid = getManufacturerID();
uint16_t pid = getProductID();
_size = 0; // UNKNOWN
if (mid == 0x000A) // fujitsu
if (writeProtectPin > -1)
{
// note pid's are from fujitsu SIZE TYPE
if (pid == 0x0358) _size = 8; // 8KB MB85RC64T
if (pid == 0x0510) _size = 32; // 32KB MB85RC256V
if (pid == 0x0658) _size = 64; // 64KB MB85RC512T
if (pid == 0x0758) _size = 128; // 128KB MB85RC1MT
_writeProtectPin = writeProtectPin;
pinMode(_writeProtectPin, OUTPUT);
}
return FRAM_OK;
}
@ -104,7 +100,7 @@ uint32_t FRAM::read32(uint16_t memaddr)
void FRAM::read(uint16_t memaddr, uint8_t * obj, uint16_t size)
{
const int blocksize = 24;
const uint8_t blocksize = 24;
uint8_t * p = obj;
while (size >= blocksize)
{
@ -113,44 +109,70 @@ void FRAM::read(uint16_t memaddr, uint8_t * obj, uint16_t size)
p += blocksize;
size -= blocksize;
}
// remainder
if (size > 0)
{
readBlock(memaddr, p, size);
}
}
bool FRAM::setWriteProtect(bool b)
{
if (_writeProtectPin == -1) return false;
digitalWrite(_writeProtectPin, b ? HIGH : LOW);
return true;
}
uint16_t FRAM::getManufacturerID()
{
uint16_t value = 0;
Wire.beginTransmission(FRAM_SLAVE_ID_);
Wire.write(_address << 1);
Wire.endTransmission(false);
int x = Wire.requestFrom(FRAM_SLAVE_ID_, 2);
if (x != 2) return -1;
value = Wire.read() << 4;
value |= Wire.read() >> 4;
return value;
return getMetaData(0);
}
uint16_t FRAM::getProductID()
{
uint16_t value = 0;
Wire.beginTransmission(FRAM_SLAVE_ID_);
Wire.write(_address << 1);
Wire.endTransmission(false);
int x = Wire.requestFrom(FRAM_SLAVE_ID_, 3);
if (x != 3) return -1;
Wire.read();
value = (Wire.read() & 0x0F) << 8;
value |= Wire.read();
return value;
return getMetaData(1);
}
uint16_t FRAM::getSize()
{
uint16_t val = getMetaData(2); // density bits
if (val > 0) return 1 << val;
return 0;
}
///////////////////////////////////////////////////////////
//
// PRIVATE
//
void FRAM::writeBlock(uint16_t memaddr, uint8_t * obj, uint16_t size)
// metadata is packed as [....MMMM][MMMMDDDD][PPPPPPPP]
// M = manufacturerID
// D = density => memsize = 2^D KB
// P = product ID (together with D)
uint16_t FRAM::getMetaData(uint8_t field)
{
if (field > 2) return 0;
Wire.beginTransmission(FRAM_SLAVE_ID_);
Wire.write(_address << 1);
Wire.endTransmission(false);
int x = Wire.requestFrom(FRAM_SLAVE_ID_, (uint8_t)3);
if (x != 3) return -1;
uint32_t value = 0;
value = Wire.read();
value |= Wire.read();
value |= Wire.read();
// MANUFACTURER
if (field == 0) return (value >> 12) & 0xFF;
// PRODUCT ID
if (field == 1) return value & 0x0FFF;
// DENSITY
if (field == 2) return (value >> 8) & 0x0F;
return 0;
}
void FRAM::writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
{
// TODO constrain size < 30 ??
Wire.beginTransmission(_address);
@ -164,7 +186,7 @@ void FRAM::writeBlock(uint16_t memaddr, uint8_t * obj, uint16_t size)
Wire.endTransmission();
}
void FRAM::readBlock(uint16_t memaddr, uint8_t * obj, uint16_t size)
void FRAM::readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size)
{
Wire.beginTransmission(_address);
Wire.write(memaddr >> 8);
@ -178,4 +200,4 @@ void FRAM::readBlock(uint16_t memaddr, uint8_t * obj, uint16_t size)
}
}
// END OF FILE
// -- END OF FILE --

View File

@ -1,21 +1,20 @@
#pragma once
//
// FILE: FRAM.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: Class for FRAM memory
// URL:
// VERSION: 0.2.1
// DATE: 2018-01-24
// PURPOSE: Arduino library for I2C FRAM
// URL: https://github.com/RobTillaart/FRAM_I2C
//
// HISTORY:
// see FRAM.cpp file
//
#ifndef FRAM_H
#define FRAM_H
#include "Arduino.h"
#include "Wire.h"
#define FRAM_LIB_VERSION (F("0.1.1"))
#define FRAM_LIB_VERSION (F("0.2.1"))
#define FRAM_OK 0
#define FRAM_ERROR_ADDR -10
@ -25,7 +24,8 @@ class FRAM
public:
FRAM();
int begin(const int address); // defaults to 0x50
// writeProtectPin is optional
int begin(const uint8_t address = 0x50, int8_t writeProtectPin = -1);
void write8(uint16_t memaddr, uint8_t value);
void write16(uint16_t memaddr, uint16_t value);
@ -37,18 +37,19 @@ public:
uint32_t read32(uint16_t memaddr);
void read(uint16_t memaddr, uint8_t * obj, uint16_t size);
bool setWriteProtect(bool b);
uint16_t getManufacturerID();
uint16_t getProductID();
uint16_t getSize() { return _size; };
uint16_t getSize();
private:
int8_t _address;
uint16_t _size; // unknown
uint8_t _address;
int8_t _writeProtectPin = -1; // default no pin ==> no write protect.
void writeBlock(uint16_t memaddr, uint8_t * obj, uint16_t size);
void readBlock(uint16_t memaddr, uint8_t * obj, uint16_t size);
uint16_t getMetaData(uint8_t id);
void writeBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
void readBlock(uint16_t memaddr, uint8_t * obj, uint8_t size);
};
#endif
// END OF FILE
// -- END OF FILE --

21
libraries/FRAM/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

23
libraries/FRAM/README.md Normal file
View File

@ -0,0 +1,23 @@
# FRAM_I2C
Arduino library for I2C FRAM
# Description
FRAM is a library to read and write (over I2C) to an FRAM module.
FRAM is much faster than EEPROM and almost as fast as Arduino UNO RAM.
Another imaportant feature is that FRAM keeps its content after a reboot (non-volatile)
Types of FRAM it should work with
| SIZE | TYPE |
|:---:|:---:|
| 8KB | MB85RC64T |
| 32KB | MB85RC256V |
| 64KB | MB85RC512T |
| 128KB | MB85RC1MT |
# Operational
See examples

View File

@ -3,7 +3,7 @@
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test for FRAM library for Arduino
// URL:
// URL: https://github.com/RobTillaart/FRAM_I2C
//
// Released to the public domain
//
@ -171,7 +171,6 @@ void testReadWriteLarge()
Serial.println();
}
void testWriteText()
{
char str[10][20] =

View File

@ -3,9 +3,7 @@
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test for FRAM library for Arduino
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/FRAM_I2C
//
#include "FRAM.h"
@ -15,6 +13,8 @@ FRAM fram;
uint32_t start;
uint32_t stop;
int ar[600];
void setup()
{
Serial.begin(115200);
@ -55,30 +55,31 @@ void testReadWriteLarge()
Serial.println();
Serial.println(__FUNCTION__);
uint8_t ar[100];
for (int i = 0; i < 100; i++) ar[i] = i;
for (int i = 0; i < 600; i++) ar[i] = i;
start = millis();
fram.write(1000, ar, 100);
fram.write(1000, (uint8_t*)ar, 1200);
stop = millis();
Serial.print("WRITE 100 bytes TIME:\t");
Serial.print("WRITE 1200 bytes TIME:\t");
Serial.print(stop - start);
Serial.println(" ms");
for (int i = 0; i < 100; i++) ar[i] = 0;
for (int i = 0; i < 600; i++) ar[i] = 0;
start = millis();
fram.read(1000, ar, 100);
fram.read(1000, (uint8_t*)ar, 1200);
stop = millis();
Serial.print("READ 100 bytes TIME:\t");
Serial.print("READ 1200 bytes TIME:\t");
Serial.print(stop - start);
Serial.println(" ms");
for (int i = 0; i < 100; i++)
for (int i = 0; i < 600; i++)
{
if (ar[i] != i)
{
Serial.print("FAIL: \t");
Serial.print(ar[i]);
Serial.print('\t');
Serial.println(i);
}
}

View File

@ -1,22 +1,25 @@
#######################################
# Syntax Coloring Map for FRAM
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
FRAM KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
write8 KEYWORD2
write16 KEYWORD2
write32 KEYWORD2
write KEYWORD2
read8 KEYWORD2
read16 KEYWORD2
read32 KEYWORD2
read KEYWORD2
getManufacturerID KEYWORD2
getProductID KEYWORD2
getSize KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
unknown LITERAL1
FRAM_OK LITERAL1
FRAM_ERROR_ADDR LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "FRAM",
"keywords": "FRAM,storage",
"name": "FRAM_I2C",
"keywords": "FRAM, storage",
"description": "Library for FRAM for Arduino.",
"authors":
[
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/FRAM_I2C.git"
},
"version":"0.1.1",
"version":"0.2.1",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/FRAM"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=FRAM
version=0.1.1
name=FRAM_I2C
version=0.2.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library for FRAM for Arduino.
sentence=Arduino library for I2C FRAM.
paragraph=
category=Data Storage
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
url=https://github.com/RobTillaart/FRAM_I2C.git
architectures=*
includes=Wire.h,FRAM.h
depends=Wire.h

View File

@ -1,14 +1,19 @@
//
// FILE: FastMap.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// VERSION: 0.3.1
// PURPOSE: class with fast map function - library for Arduino
// URL: http://forum.arduino.cc/index.php?topic=276194
// URL: https://github.com/RobTillaart/FastMap
//
// HISTORY:
// 0.3.1 2020-08-31 updated documentation
// 0.3.0 2020-07-04 added fastMapDouble + test sketch.
// 0.2.1 2020-06-10 fix library.json; rename license
// 0.2.0 2020-03-21 #pragma once; readme.md; license.md
//
// 0.1.8 2017-07-27 revert double to float (issue 33)
// 0.1.7 2017-04-28 cleaned up, get examples working again
// 0.1.06 2015-03-08 replaced float by float (support ARM)
// 0.1.06 2015-03-08 replaced float by double (support ARM)
// 0.1.05 2014-11-02 stripped of bit mask experimental code
// 0.1.04 add back() - the inverse map
// tested with bit mask for constrain code (Perfomance was killed)
@ -20,10 +25,6 @@
#include "FastMap.h"
/////////////////////////////////////////////////////
//
// PUBLIC
//
FastMap::FastMap()
{
init(0, 1, 0, 1);
@ -62,4 +63,42 @@ float FastMap::upperConstrainedMap(float value)
return this->map(value);
}
// END OF FILE
FastMapDouble::FastMapDouble()
{
init(0, 1, 0, 1);
}
void FastMapDouble::init(double in_min, double in_max, double out_min, double out_max)
{
_in_min = in_min;
_in_max = in_max;
_out_min = out_min;
_out_max = out_max;
_factor = (out_max - out_min)/(in_max - in_min);
_base = out_min - in_min * _factor;
_backfactor = 1/_factor;
_backbase = in_min - out_min * _backfactor;
}
double FastMapDouble::constrainedMap(double value)
{
if (value <= _in_min) return _out_min;
if (value >= _in_max) return _out_max;
return this->map(value);
}
double FastMapDouble::lowerConstrainedMap(double value)
{
if (value <= _in_min) return _out_min;
return this->map(value);
}
double FastMapDouble::upperConstrainedMap(double value)
{
if (value >= _in_max) return _out_max;
return this->map(value);
}
// END OF FILE

View File

@ -1,24 +1,18 @@
#pragma once
//
// FILE: FastMap.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// VERSION: 0.3.1
// PURPOSE: class with fast map function - library for Arduino
// URL: http://forum.arduino.cc/index.php?topic=276194
// URL: https://github.com/RobTillaart/FastMap
//
// HISTORY:
// see FastMap.cpp file
//
#ifndef FastMap_h
#define FastMap_h
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
#define FASTMAP_LIB_VERSION (F("0.1.8"))
#define FASTMAP_LIB_VERSION (F("0.3.1"))
class FastMap
{
@ -40,6 +34,26 @@ private:
float _backfactor, _backbase;
};
#endif
// END OF FILE
class FastMapDouble
{
public:
FastMapDouble();
void init(const double in_min, const double in_max, const double out_min, const double out_max);
double inline map (const double value) { return _base + value * _factor; }
double inline back (const double value) { return _backbase + value * _backfactor; }
double constrainedMap(const double value);
double lowerConstrainedMap(const double value);
double upperConstrainedMap(const double value);
private:
double _in_min, _in_max, _out_min, _out_max;
double _factor, _base;
double _backfactor, _backbase;
};
// -- END OF FILE --

21
libraries/FastMap/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2010-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,13 +1,10 @@
//
// FILE: constrainedMap.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// VERSION: 0.1.1
// PURPOSE: demo of FastMap class ==> merge map and constrain functions
// DATE: 2014-11-02
// URL:
//
// Released to the public domain
//
// URL: https://github.com/RobTillaart/FastMap
#include "FastMap.h"

View File

@ -4,10 +4,7 @@
// VERSION: 0.1.02
// PURPOSE: demo of FastMap class ==> a faster map function
// DATE: 2014-11-02
// URL:
//
// Released to the public domain
//
// URL: https://github.com/RobTillaart/FastMap
#include "FastMap.h"

View File

@ -4,10 +4,7 @@
// VERSION: 0.1.02
// PURPOSE: demo of FastMap class ==> a faster map function
// DATE: 2014-11-02
// URL:
//
// Released to the public domain
//
// URL: https://github.com/RobTillaart/FastMap
#include "FastMap.h"

View File

@ -3,10 +3,8 @@
// VERSION: 0.1.1
// PURPOSE: demo of FastMap class ==> a faster map function
// DATE: 2014-11-02
// URL: http://forum.arduino.cc/index.php?topic=276194
//
// Released to the public domain
//
// URL: https://github.com/RobTillaart/FastMap
// http://forum.arduino.cc/index.php?topic=276194
#include "FastMap.h"

View File

@ -1,15 +1,10 @@
//
// FILE: fastMapDemo4.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.1.0
// PURPOSE: demo of FastMap class ==> a faster map function
// DATE: 2014-11-02
// URL:
//
// Released to the public domain
//
// works only with 0.1.04
// URL: https://github.com/RobTillaart/FastMap
#include "FastMap.h"

View File

@ -0,0 +1,66 @@
//
// FILE: fastMapDouble.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo of FastMapDouble class
// DATE: 2020-07-04
// URL: https://github.com/RobTillaart/FastMap
// Note: the mapping used in the example cannot be done
// with the normal map function.
#include "FastMap.h"
#include "printHelpers.h" // https://github.com/RobTillaart/printHelpers
uint32_t start, stop;
volatile double x;
FastMapDouble mapper;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("lib version: ");
Serial.println(FASTMAP_LIB_VERSION);
Serial.println();
// Get a non optimizable value;
volatile double pie = PI;
// FASTMAP
// map 0 .. 100% to lightspeed in km/hr
mapper.init(0, 100, 0, 1.0792528488E+12);
start = micros();
for (int i = 0; i < 10000; i++)
{
x = mapper.map(pie);
}
stop = micros();
Serial.print("10000 calls map:\t");
Serial.print(pie, 5);
Serial.print(" -> ");
Serial.println(sci(x, 5));
Serial.print(" time:\t");
Serial.println(stop - start);
Serial.println();
Serial.println("PERC\tSpeed in Km/h");
for (float p = 80; p < 100; p += 0.25)
{
x = mapper.map(p);
Serial.print(p, 2);
Serial.print('\t');
Serial.print(sci(x, 4));
Serial.println();
}
Serial.println("\nDone...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,15 @@
# Syntax Coloring Map For FastMap
# Datatypes (KEYWORD1)
FastMap KEYWORD1
FastMapDouble KEYWORD1
# Methods and Functions (KEYWORD2)
map KEYWORD2
back KEYWORD2
constrainedMap KEYWORD2
lowerConstrainedMap KEYWORD2
upperConstrainedMap KEYWORD2
# Constants (LITERAL1)

View File

@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/FastMap"
},
"version":"0.1.8",
"version":"0.3.1",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/FastMap"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=FastMap
version=0.1.8
version=0.3.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library with fast map function for Arduino.
paragraph=
category=Data Processing
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/FastMap
architectures=*
includes=FastMap.h
depends=

View File

@ -1,2 +1,62 @@
# FastMap
https://github.com/RobTillaart/FastMap
Fast mapping and constraining
## Description
FastMap is an object that precalculates (internal) floats to make a mapping function especially for floats.
The Fastmap also provides a **back()** function to reverse the mapping.
This only works well with floats, so use with care.
An important difference with the traditional **map()** function is that both **init()** and **map()**
accepts floats as parameters, allowing mapping that would be hard to achieve with the normal **map()**
function.
## Performance notes
(based upon tests https://github.com/RobTillaart/FastMap/issues/4 )
- On AVR (UNO and MEGA) no gain is made mapping integers with fastMap, 130% slower = substantial
- On AVR the gain for float is limited, 10% faster
- On ESP32 the gain for integers and float is both in the order of 25%
To see the actual gain in your project on your hardware you should test and compare.
FastMap is faster when mapping floats as it uses less float operations than the standard map formula does.
The perfomance results from precalculating values in the **init()** function so actual mapping needs only
one multiply and add, where the standard **map()** function uses four adds, a multiplication and a division.
The precalculation in **init()** should be taken in account and if every **map()** call needs an **init()**
there will be no gain, on contrary.
The implementation uses floats (typical 32 bits) which might result in more memory usage and loss of precision
for mapping of larger values, especially 32 and 64 bit integers. This is caused by the limits of the mantissa
(~23 bits) of the standard 4 byte float.
## Interface
- **void init(in_min, in_max, out_min, out_max);** defines the linear mapping parameters.
The **init()** function calculates all needed values for the **map()**, the **back()** call and the **constrainXX()** functions.
The **init()** function can be called again with new values when needed to do other mapping,
although it will give less overhead if you create an fastMap object per conversion needed.
Note: **init()** does not check for a divide by zero (out_max == out_min) or (in_max == in_min)
- **float map(float value)** maps the parameter.
- **float back(float value)** does the inverse mapping
FastMap supports three versions of constraining the map function, based upon the parameters of **init()**
- **float constrainedMap(float value);** returns a value between outMin .. outMax
- **float lowerConstrainedMap(float value);** returns a value between outMin .. inf (No upper limit)
- **float upperConstrainedMap(float value);** returns a value between -inf .. outMax
To change the constrain values call **init()** with new limits, or call **constrain()**
Note there are **NO** constrain-versions for **back(value)** function.
## FastMapDouble
Version 3.0 adds **fastMapDouble** which has the same interface.
This class is meant to support 8 bytes doubles in their native accuracy and precision.
To display doubles one might need the **sci()** function of my **printHelpers** class.
## Usage
See examples.

View File

@ -1,58 +1,113 @@
//
// FILE: FastShiftIn.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.3
// PURPOSE: shiftin
// VERSION: 0.2.1
// PURPOSE: Fast ShiftIn for 74HC165 register, AVR optimized
// DATE: 2013-09-29
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/FastShiftIn
//
#include "FastShiftIn.h"
FastShiftIn::FastShiftIn(const uint8_t datapin, const uint8_t clockpin, const uint8_t bitOrder)
{
_bitorder = bitOrder;
_value = FASTSHIFTIN_NOVALUE;
_bitorder = bitOrder;
pinMode(datapin, INPUT);
pinMode(clockpin, INPUT);
pinMode(datapin, INPUT);
pinMode(clockpin, OUTPUT);
// uint8_t _datatimer = digitalPinToTimer(datapin);
// if (_datatimer != NOT_ON_TIMER) turnOffPWM(_datatimer); TODO
uint8_t _dataport = digitalPinToPort(datapin);
_datain = portOutputRegister(_dataport);
_databit = digitalPinToBitMask(datapin);
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
// uint8_t _clocktimer = digitalPinToTimer(clockpin);
// if (_clocktimer != NOT_ON_TIMER) turnOffPWM(_clocktimer);
uint8_t _clockport = digitalPinToPort(clockpin);
_clockin = portOutputRegister(_clockport);
_clockbit = digitalPinToBitMask(clockpin);
// uint8_t _datatimer = digitalPinToTimer(datapin);
// if (_datatimer != NOT_ON_TIMER) turnOffPWM(_datatimer); TODO
uint8_t _dataport = digitalPinToPort(datapin);
_datain = portOutputRegister(_dataport);
_databit = digitalPinToBitMask(datapin);
// uint8_t _clocktimer = digitalPinToTimer(clockpin);
// if (_clocktimer != NOT_ON_TIMER) turnOffPWM(_clocktimer);
uint8_t _clockport = digitalPinToPort(clockpin);
_clockin = portOutputRegister(_clockport);
_clockbit = digitalPinToBitMask(clockpin);
#else // reference implementation
// reuse these vars as pin to save some space
_databit = datapin;
_clockbit = clockpin;
#endif
}
int FastShiftIn::read()
{
_value = 0;
for (uint8_t i = 0, m = 1, n = 128; i < 8; i++, m <<=1, n >>= 1)
{
uint8_t oldSREG = SREG;
cli();
*_clockin |= _clockbit;
if ((*_datain & _databit) > 0)
{
if (_bitorder == LSBFIRST)
_value |= m;
else
_value |= n;
}
*_clockin &= ~_clockbit;
SREG = oldSREG;
}
return _value;
if (_bitorder == LSBFIRST)
{
return readLSBFIRST();
}
return readMSBFIRST();
}
// -- END OF FILE --
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
int FastShiftIn::readLSBFIRST()
{
uint8_t value = 0;
uint8_t cbmask1 = _clockbit;
uint8_t cbmask2 = ~_clockbit;
uint8_t dbmask = _databit;
for (uint8_t m = 1; m > 0; m <<= 1)
{
uint8_t oldSREG = SREG;
cli();
*_clockin |= cbmask1;
if ((*_datain & dbmask) > 0)
{
value |= m;
}
*_clockin &= cbmask2;
SREG = oldSREG;
}
_value = value;
return _value;
}
int FastShiftIn::readMSBFIRST()
{
uint8_t value = 0;
uint8_t cbmask1 = _clockbit;
uint8_t cbmask2 = ~cbmask1;
uint8_t dbmask = _databit;
for (uint8_t n = 128; n > 0; n >>= 1)
{
uint8_t oldSREG = SREG;
cli();
*_clockin |= cbmask1;
if ((*_datain & dbmask) > 0)
{
value |= n;
}
*_clockin &= cbmask2;
SREG = oldSREG;
}
_value = value;
return _value;
}
#else // reference implementation - note this has no cli()
int FastShiftIn::readLSBFIRST()
{
return shiftIn(_databit, _clockbit, LSBFIRST);
}
int FastShiftIn::readMSBFIRST()
{
return shiftIn(_databit, _clockbit, MSBFIRST);
}
#endif
// -- END OF FILE --

View File

@ -1,43 +1,37 @@
#pragma once
//
// FILE: FastShiftIn.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.3
// PURPOSE: shiftin
// VERSION: 0.2.1
// PURPOSE: Fast ShiftIn for 74HC165 register, AVR optimized
// DATE: 2013-09-29
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/FastShiftIn
//
#ifndef FastShiftIn_h
#define FastShiftIn_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define FASTSHIFTIN_LIB_VERSION "0.1.3"
#define FASTSHIFTIN_NOVALUE -1
#define FASTSHIFTIN_LIB_VERSION (F("0.2.1"))
class FastShiftIn
{
public:
FastShiftIn(const uint8_t, const uint8_t, const uint8_t);
int read(void);
// bitorder = { LSBFIRST, MSBFIRST };
FastShiftIn(const uint8_t datapin, const uint8_t clockpin, const uint8_t bitOrder = LSBFIRST);
int read(void);
// overrule bitorder (most optimized).
int readLSBFIRST(void);
int readMSBFIRST(void);
private:
uint8_t _bitorder;
int _value;
uint8_t _bitorder;
int _value;
uint8_t _databit;
volatile uint8_t *_datain;
uint8_t _databit;
volatile uint8_t *_datain;
uint8_t _clockbit;
volatile uint8_t *_clockin;
uint8_t _clockbit;
volatile uint8_t *_clockin;
};
#endif
// -- END OF FILE --
// -- END OF FILE --

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,77 @@
//
// FILE: fastShiftIn_readLSBFIRST.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test sketch
// URL: https://github.com/RobTillaart/FastShiftIn
//
#include "FastShiftIn.h"
FastShiftIn FSI(12, 13);
volatile int x = 0;
void setup()
{
Serial.begin(115200);
Serial.print("example fastShiftIn: ");
Serial.println(FASTSHIFTIN_LIB_VERSION);
digitalWrite(12, HIGH);
Serial.println("\n 8 bits HIGH - readLSBFIRST\n");
Serial.println("\nPerformance - time in us");
uint32_t start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readLSBFIRST();
}
uint32_t duration1 = micros() - start;
Serial.print("FastShiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readLSBFIRST();
x = FSI.readLSBFIRST();
}
uint32_t duration2 = micros() - start;
Serial.print("FastShiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
start = micros();
for (int i = 0; i < 1000; i++)
{
x = shiftIn(12, 13, LSBFIRST);
}
duration1 = micros() - start;
Serial.print("Standard shiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = shiftIn(12, 13, LSBFIRST);
x = shiftIn(12, 13, LSBFIRST);
}
duration2 = micros() - start;
Serial.print("Standard shiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
Serial.println("done...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,77 @@
//
// FILE: fastShiftIn_readMSBFIRST.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test sketch
// URL: https://github.com/RobTillaart/FastShiftIn
//
#include "FastShiftIn.h"
FastShiftIn FSI(12, 13);
volatile int x = 0;
void setup()
{
Serial.begin(115200);
Serial.print("example fastShiftIn: ");
Serial.println(FASTSHIFTIN_LIB_VERSION);
digitalWrite(12, HIGH);
Serial.println("\n 8 bits HIGH\n");
Serial.println("\nPerformance - time in us");
uint32_t start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readMSBFIRST();
}
uint32_t duration1 = micros() - start;
Serial.print("FastShiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readMSBFIRST();
x = FSI.readMSBFIRST();
}
uint32_t duration2 = micros() - start;
Serial.print("FastShiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
start = micros();
for (int i = 0; i < 1000; i++)
{
x = shiftIn(12, 13, MSBFIRST);
}
duration1 = micros() - start;
Serial.print("Standard shiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = shiftIn(12, 13, MSBFIRST);
x = shiftIn(12, 13, MSBFIRST);
}
duration2 = micros() - start;
Serial.print("Standard shiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
Serial.println("done...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,147 @@
//
// FILE: fastShiftIn_test.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test sketch
// URL: https://github.com/RobTillaart/FastShiftIn
//
#include "FastShiftIn.h"
FastShiftIn FSI(12, 13);
volatile int x = 0;
uint32_t start, duration1, duration2;
void setup()
{
Serial.begin(115200);
Serial.print("example fastShiftIn: ");
Serial.println(FASTSHIFTIN_LIB_VERSION);
digitalWrite(12, HIGH);
Serial.println("\n 8 bits HIGH\n");
Serial.println("\nPerformance - time in us : read()");
test_read();
Serial.println("\nPerformance - time in us : readLSBFIRST()");
test_readLSBFIRST();
Serial.println("\nPerformance - time in us : readMSBFIRST()");
test_readMSBFIRST();
Serial.println("\nPerformance - time in us : reference shiftIn()");
test_reference();
Serial.println("done...");
}
void test_read()
{
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.read();
}
duration1 = micros() - start;
Serial.print("FastShiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.read();
x = FSI.read();
}
duration2 = micros() - start;
Serial.print("FastShiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
delay(100);
}
void test_readLSBFIRST()
{
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readLSBFIRST();
}
duration1 = micros() - start;
Serial.print("FastShiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readLSBFIRST();
x = FSI.readLSBFIRST();
}
duration2 = micros() - start;
Serial.print("FastShiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
delay(100);
}
void test_readMSBFIRST()
{
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readMSBFIRST();
}
duration1 = micros() - start;
Serial.print("FastShiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = FSI.readMSBFIRST();
x = FSI.readMSBFIRST();
}
duration2 = micros() - start;
Serial.print("FastShiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
delay(100);
}
void test_reference()
{
start = micros();
for (int i = 0; i < 1000; i++)
{
x = shiftIn(12, 13, LSBFIRST);
}
duration1 = micros() - start;
Serial.print("Standard shiftIn1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
x = shiftIn(12, 13, LSBFIRST);
x = shiftIn(12, 13, LSBFIRST);
}
duration2 = micros() - start;
Serial.print("Standard shiftIn2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
}
void loop()
{
}

View File

@ -1,7 +1,7 @@
{
"name": "FastShiftIn",
"keywords": "Shift,shiftin,in,serial,fast",
"description": "Class for up to 3x faster shift in.",
"keywords": "Shift, shiftIn, in, serial, fast, 74HC165",
"description": "Arduino library for (AVR) optimized shiftIn - e.g. for 74HC165",
"authors":
[
{
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/FastShiftIn.git"
},
"version":"0.1.3",
"version":"0.2.1",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/FastShiftIn"
}
"platforms": "AVR"
}

View File

@ -1,9 +1,11 @@
name=FastShiftIn
version=0.1.3
version=0.2.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Class for up to 3x faster shift in.
paragraph=
sentence=Arduino library for (AVR) optimized shiftIn - e.g. for 74HC165
paragraph=
category=Signal Input/Output
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/FastShiftIn
architectures=avr
includes=FastShiftIn.h
depends=

View File

@ -0,0 +1,119 @@
==============================
2020-04-14 - tested IDE 1.8.12
factor 5+ faster
-----------------------------
example fastShiftIn: 0.2.0
8 bits HIGH
Performance - time in us : read()
FastShiftIn1: 20.43
FastShiftIn2: 39.72
Delta: 19.29
Performance - time in us : readLSBFIRST()
FastShiftIn1: 19.80
FastShiftIn2: 38.84
Delta: 19.04
Performance - time in us : readMSBFIRST()
FastShiftIn1: 19.80
FastShiftIn2: 38.84
Delta: 19.04
Performance - time in us : reference shiftIn()
Standard shiftIn1: 108.61
Standard shiftIn2: 216.43
Delta: 107.82
done...
==============================
tested IDE 1.8.12
2020-04-14
example fastShiftIn: 0.1.5
8 bits HIGH
Performance - time in us
FastShiftIn1: 25.15
FastShiftIn2: 49.15
Delta: 24.00
Standard shiftIn1: 108.51
Standard shiftIn2: 216.43
Delta: 107.92
done...
==============================
tested IDE 1.8.1
20-08-2017
example fastShiftIn: 0.1.4
8 bits HIGH
Performance - time in us
FastShiftIn1: 25.64
FastShiftIn2: 50.16
Delta: 24.52
Standard shiftIn1: 110.53
Standard shiftIn2: 220.48
Delta: 109.95
done...
==============================
2017-4-27
example fastShiftIn: 0.1.3
8 bits HIGH
Performance - time in us
FastShiftIn1: 31.31
FastShiftIn2: 61.48
Delta: 30.18
Standard shiftIn1: 110.54
Standard shiftIn2: 220.48
Delta: 109.94
done...
==============================
example fastShiftIn: 0.1.01
8 bits HIGH
Performance - time in us
FastShiftIn1: 44.83
FastShiftIn2: 88.52
Delta: 43.70
Standard shiftIn1: 110.54
Standard shiftIn2: 220.49
Delta: 109.95
done...

View File

@ -1,6 +1,41 @@
# FastShiftIn
FastShiftIn is a class that speeds up the shifting by using predetermined posts and masks.
Its performance is about twice as fast as the normal Arduino shiftIn.
Arduino library for (AVR) optimized shiftIn - e.g. for 74HC165
A library for FastShiftOut also exist.
## Description
FastShiftIn is a class that has optimized code (AVR only) to shift in data faster
than the normal shiftIn() function.
It speeds up the shift using low level ports and masks. These are predetermined
in the constructor of the FastShiftIn object.
If not an **ARDUINO_ARCH_AVR** or **ARDUINO_ARCH_MEGAAVR** the class falls back
to the default shiftIn() implementation.
## Performance
The performance of **read()** is substantially faster than the default Arduino
**shiftIn()**, but not as fast as HW SPI.
Exact how big the performance gain is can be seen with the example sketch.
It does a comparison and shows how the class is to be used.
## Interface
The interface exists of the following functions:
- **int read(void);**
- **int readLSBFIRST(void);** most optimized
- **int readMSBFIRST(void);** most optimized
## Notes
- The optimizations are AVR only for now, other platforms may follow.
- The 74HC165 needs 0.1uF caps and the data and clock lines may need
pull up resistors, especially if wires are exceeding 10 cm (4").
## Operation
See examples
There is a thread on the forum here - http://forum.arduino.cc/index.php?topic=184002.0 -
This thread also discusses a FastShiftOut

View File

@ -0,0 +1,119 @@
//
// FILE: FastShiftOut_test.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test sketch
// URL: https://github.com/RobTillaart/FastSHiftOut
//
#include "FastShiftOut.h"
FastShiftOut FSO(12, 13, LSBFIRST);
uint32_t start, duration1, duration2;
void setup()
{
Serial.begin(115200);
Serial.print("example fastShiftOut: ");
Serial.println(FASTSHIFTOUT_LIB_VERSION);
Serial.println("\nPerformance - time in us");
test1();
test2();
test3();
Serial.println("\ndone ...\n");
}
void test1()
{
start = micros();
for (int i = 0; i < 1000; i++)
{
FSO.write(0x55);
}
duration1 = micros() - start;
Serial.print("FastShiftOut1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
FSO.write(0x55);
FSO.write(0x55);
}
duration2 = micros() - start;
Serial.print("FastShiftOut2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
delay(100);
}
void test2()
{
start = micros();
for (int i = 0; i < 1000; i++)
{
shiftOut(12, 13, 0x55, LSBFIRST);
}
duration1 = micros() - start;
Serial.print("Standard shiftOut1: ");
Serial.println(duration1 * 0.001);
start = micros();
for (int i = 0; i < 1000; i++)
{
shiftOut(12, 13, 0x55, LSBFIRST);
shiftOut(12, 13, 0x55, LSBFIRST);
}
duration2 = micros() - start;
Serial.print("Standard shiftOut2: ");
Serial.println(duration2 * 0.001);
Serial.print(" Delta: ");
Serial.println((duration2 - duration1) * 0.001);
Serial.println();
delay(100);
}
void test3()
{
Serial.println("\nTest print interface");
start = micros();
for (int i = 0; i < 100; i++)
{
FSO.println("Hello world");
}
duration1 = micros() - start;
Serial.print("println(\"Hello world\"): \t");
Serial.println(duration1 * 0.01);
start = micros();
for (int i = 0; i < 100; i++)
{
FSO.println(1357);
}
duration1 = micros() - start;
Serial.print("println(1357): \t\t\t");
Serial.println(duration1 * 0.01);
start = micros();
for (int i = 0; i < 100; i++)
{
FSO.println(3.14159265, 4);
}
duration1 = micros() - start;
Serial.print("println(3.14159265, 4): \t");
Serial.println(duration1 * 0.01);
delay(100);
}
void loop()
{
}
// -- END OF FILE --

View File

@ -1,112 +1,110 @@
//
// FILE: FastShiftOut.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.5
// VERSION: 0.2.1
// PURPOSE: shiftout that implements the Print interface
// DATE: 2013-08-22
// URL:
//
// Released to the public domain
//
// HISTORY
// 0.1.5 - changed masking in inner loop
// URL: https://github.com/RobTillaart/FastShiftOut
//
#include "FastShiftOut.h"
//
// Constructor
// prepares the digitalWrite()
FastShiftOut::FastShiftOut(const uint8_t datapin, const uint8_t clockpin, const uint8_t bitOrder)
{
_bitorder = bitOrder;
_value = -1;
pinMode(datapin, OUTPUT);
pinMode(clockpin, OUTPUT);
_bitorder = bitOrder;
pinMode(datapin, OUTPUT);
pinMode(clockpin, OUTPUT);
// uint8_t _datatimer = digitalPinToTimer(datapin);
// if (_datatimer != NOT_ON_TIMER) turnOffPWM(_datatimer); TODO
uint8_t _dataport = digitalPinToPort(datapin);
_dataout = portOutputRegister(_dataport);
_databit = digitalPinToBitMask(datapin);
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
// uint8_t _clocktimer = digitalPinToTimer(clockpin);
// if (_clocktimer != NOT_ON_TIMER) turnOffPWM(_clocktimer);
uint8_t _clockport = digitalPinToPort(clockpin);
_clockout = portOutputRegister(_clockport);
_clockbit = digitalPinToBitMask(clockpin);
// uint8_t _datatimer = digitalPinToTimer(datapin);
// if (_datatimer != NOT_ON_TIMER) turnOffPWM(_datatimer); TODO
uint8_t _dataport = digitalPinToPort(datapin);
_dataout = portOutputRegister(_dataport);
_databit = digitalPinToBitMask(datapin);
// uint8_t _clocktimer = digitalPinToTimer(clockpin);
// if (_clocktimer != NOT_ON_TIMER) turnOffPWM(_clocktimer);
uint8_t _clockport = digitalPinToPort(clockpin);
_clockout = portOutputRegister(_clockport);
_clockbit = digitalPinToBitMask(clockpin);
#else // reference implementation
// reuse these vars as pin to save some space
_databit = datapin;
_clockbit = clockpin;
#endif
}
//
// write() must implement the virtual write of Print class
//
// approx 30 us/byte
size_t FastShiftOut::write(const uint8_t data)
{
_value = data;
for (uint8_t i = 0, m = 1, n = 128; i < 8; i++, m <<= 1, n >>= 1)
{
uint8_t v;
if (_bitorder == LSBFIRST) v = (_value & m);
else v = (_value & n);
if (_bitorder == LSBFIRST)
{
return writeLSBFIRST(data);
}
return writeMSBFIRST(data);
}
uint8_t oldSREG = SREG;
cli();
if (v == 0) *_dataout &= ~_databit;
else *_dataout |= _databit;
*_clockout |= _clockbit;
*_clockout &= ~_clockbit;
SREG = oldSREG;
}
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
size_t FastShiftOut::writeLSBFIRST(const uint8_t data)
{
uint8_t cbmask1 = _clockbit;
uint8_t cbmask2 = ~_clockbit;
uint8_t dbmask1 = _databit;
uint8_t dbmask2 = ~_databit;
for (uint8_t i = 0, m = 1; i < 8; i++)
{
uint8_t oldSREG = SREG;
cli();
if ((data & m) == 0) *_dataout &= dbmask2;
else *_dataout |= dbmask1;
*_clockout |= cbmask1;
*_clockout &= cbmask2;
SREG = oldSREG;
m <<= 1;
}
return 1;
}
size_t FastShiftOut::writeMSBFIRST(const uint8_t data)
{
uint8_t cbmask1 = _clockbit;
uint8_t cbmask2 = ~_clockbit;
uint8_t dbmask1 = _databit;
uint8_t dbmask2 = ~_databit;
for (uint8_t i = 0, n = 128; i < 8; i++)
{
uint8_t oldSREG = SREG;
cli();
if ((data & n) == 0) *_dataout &= dbmask2;
else *_dataout |= dbmask1;
*_clockout |= cbmask1;
*_clockout &= cbmask2;
SREG = oldSREG;
n >>= 1;
}
return 1;
}
#else // reference implementation // note this has no cli()
size_t FastShiftOut::writeLSBFIRST(const uint8_t data)
{
shiftOut(_databit, _clockbit, LSBFIRST, data);
return 1;
}
//
// this version is twice as fast,
// but it is in CLI() mode
// approx 32us / byte
// size_t FastShiftOut::write(uint8_t data)
// {
// _value = data;
// // prep masks
// uint8_t dm1 = *_dataout | _databit;
// uint8_t dm0 = *_dataout & ~_databit;
// uint8_t cm1 = *_clockout | _clockbit;
// uint8_t cm0 = *_clockout & ~_clockbit;
// uint8_t oldSREG = SREG;
// cli();
// if (_bitorder == LSBFIRST)
// {
// for (uint8_t m = 0x01; m != 0x80; m <<= 1)
// {
// if (_value & m) *_dataout = dm1;
// else *_dataout = dm0;
// *_clockout = cm1;
// *_clockout = cm0;
// }
// }
// else
// {
// for (uint8_t m = 0x80; m > 0; m >>= 1)
// {
// if (_value & m) *_dataout = dm1;
// else *_dataout = dm0;
// *_clockout = cm1;
// *_clockout = cm0;
// }
// }
// SREG = oldSREG;
// return 1;
// }
//
// reads back the last value written.
//
int FastShiftOut::read()
size_t FastShiftOut::writeMSBFIRST(const uint8_t data)
{
return _value;
shiftOut(_databit, _clockbit, MSBFIRST, data);
return 1;
}
#endif
// -- END OF FILE --

View File

@ -1,44 +1,37 @@
#pragma once
//
// FILE: FastShiftOut.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.5
// VERSION: 0.2.1
// PURPOSE: shiftout that implements the Print interface
// DATE: 2013-08-22
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/FastShiftOut
//
#ifndef FastShiftOut_h
#define FastShiftOut_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define FASTSHIFTOUT_LIB_VERSION (F("0.1.5"))
#include "Print.h"
#define FASTSHIFTOUT_LIB_VERSION (F("0.2.1"))
class FastShiftOut : public Print
{
public:
FastShiftOut(const uint8_t, const uint8_t, const uint8_t);
size_t write(const uint8_t);
int read(void);
// bitorder = { LSBFIRST, MSBFIRST };
FastShiftOut(const uint8_t datapin, const uint8_t clockpin, const uint8_t bitOrder = LSBFIRST);
size_t write(const uint8_t data);
// overrule bitorder (most optimized).
size_t writeLSBFIRST(const uint8_t data);
size_t writeMSBFIRST(const uint8_t data);
private:
uint8_t _bitorder;
int _value;
uint8_t _bitorder;
uint8_t _databit;
volatile uint8_t *_dataout;
uint8_t _databit;
volatile uint8_t *_dataout;
uint8_t _clockbit;
volatile uint8_t *_clockout;
uint8_t _clockbit;
volatile uint8_t *_clockout;
};
#endif
// -- END OF FILE --
// -- END OF FILE --

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,179 @@
==============================
tests IDE 1.8.12 - 20-08-2017
example fastShiftOut: 0.2.0
Performance - time in us
FastShiftOut1: 21.95
FastShiftOut2: 43.00
Delta: 21.04
Standard shiftOut1: 88.27
Standard shiftOut2: 175.94
Delta: 87.68
Test print interface
println("Hello world"): 313.16
println(1357): 304.68
println(3.14159265, 4): 705.60
done ...
==============================
tests IDE 1.8.12 - 20-08-2017
example fastShiftOut: 0.1.6
Performance - time in us
FastShiftOut1: 27.68
FastShiftOut2: 54.44
Delta: 26.76
Standard shiftOut1: 88.27
Standard shiftOut2: 175.94
Delta: 87.67
Test print interface
println("Hello world"): 387.92
println(1357): 339.44
println(3.14159265, 4): 751.96
done ...
==============================
tests IDE 1.8.1
20-08-2017
example fastShiftOut: 0.1.6
Performance - time in us
FastShiftOut1: 28.69
FastShiftOut2: 56.46
Delta: 27.76
Standard shiftOut1: 90.79
Standard shiftOut2: 181.00
Delta: 90.21
Test print interface
println("Hello world"): 403.20
println(1357): 346.64
println(3.14159265, 4): 757.72
done ...
20-08-2017
example fastShiftOut: 0.1.5
Performance - time in us
FastShiftOut1: 29.76
FastShiftOut2: 58.59
Delta: 28.83
Standard shiftOut1: 90.80
Standard shiftOut2: 181.00
Delta: 90.20
Test print interface
println("Hello world"): 417.08
println(1357): 353.00
println(3.14159265, 4): 766.28
done ...
example fastShiftOut: 0.1.04
Performance - time in us
FastShiftOut1: 43.66
FastShiftOut2: 86.38
Delta: 42.72
Standard shiftOut1: 90.79
Standard shiftOut2: 181.00
Delta: 90.21
Test print interface
println("Hello world"): 597.68
println(1357): 436.44
println(3.14159265, 4): 877.48
done ...
==================================
example fastShiftOut: 0.1.03
Performance - time in us
FastShiftOut1: 63.9
FastShiftOut2: 123.0
Standard shiftOut1: 115.7
Standard shiftOut2: 230.4
Test print interface
println("Hello world"): 822.2
println(1357): 561.2
println(3.14159265, 4): 1068.6
done
example fastShiftOut: 0.1.03
Performance - time in us
FastShiftOut1: 31.9
FastShiftOut2: 60.8
Standard shiftOut1: 115.7
Standard shiftOut2: 230.4
Test print interface
println("Hello world"): 412.9
println(1357): 372.0
println(3.14159265, 4): 815.9
done
1.04 - fast print version + normal fastSHiftOut
example fastShiftOut: 0.1.03
Performance - time in us
FastShiftOut1: 64.0
FastShiftOut2: 123.4
Standard shiftOut1: 115.8
Standard shiftOut2: 230.5
Test print interface
println("Hello world"): 816.6
println(1357): 405.9
println(3.14159265, 4): 579.8
done
=======================================
example fastShiftIn: 0.1.00
Performance - time in us
FastShiftIn1: 42.8 // 8 bits LOW
FastShiftIn2: 80.8 // 8 bits LOW
Standard shiftIn1: 114248.4
Standard shiftIn2: 228004.8
example fastShiftIn: 0.1.00
Performance - time in us
FastShiftIn1: 67.7 // 8 bits HIGH
FastShiftIn2: 130.6 // 8 bits HIGH
Standard shiftIn1: 114442.4
Standard shiftIn2: 228440.4

View File

@ -0,0 +1,48 @@
# FastShiftOut
Arduino library for (AVR) optimized shiftOut - e.g. 74HC595
A library for FastShiftOut also exist.
## Description
FastShiftOut is a class that has optimized code for AVR to shift out data faster
than the normal shiftOut() function.
It speeds up the shift using low level ports and masks. These are predetermined
in the constructor of the FastShiftOut object.
If not an **ARDUINO_ARCH_AVR** or **ARDUINO_ARCH_MEGAAVR** the class falls back
to the default shiftOut() implementation.
## Performance
The performance of **write()** is substantially faster than the default Arduino
**shiftIn()**, but not as fast as HW SPI.
Exact how big the performance gain is can be seen with the example sketch.
It does a comparison and shows how the class is to be used.
## Interface
The interface exists of the following functions:
- **size_t write(const uint8_t data);**
- **size_t writeLSBFIRST(const uint8_t data);** most optimized
- **size_t writeMSBFIRST(const uint8_t data);** most optimized
Furthermore an FastShiftOut object implements the Print interface
so one can also call
- **FSO.print(any type);** or
- **FSO.println(any type);**
to send e.g. a float with 4 digits over the line.
## Notes
- The optimizations are AVR only for now, other platforms may follow.
- The 74HC595 needs 0.1uF caps and the data and clock lines may need
pull up resistors, especially if wires are exceeding 10 cm (4").
## Operation
See examples

View File

@ -1,7 +1,7 @@
{
"name": "FastShiftOut",
"keywords": "Shift,shiftout,out,serial,fast",
"description": "Class for up to 3x faster shift out. Implements print() interface.",
"keywords": "Shift, shiftout, out, serial, fast",
"description": "Arduino library for (AVR) optimized shiftOut - e.g. 74HC595",
"authors":
[
{
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/FastShiftOut.git"
},
"version":"0.1.5",
"version":"0.2.1",
"frameworks": "arduino",
"platforms": "atmelavr",
"export": {
"include": "libraries/FastShiftOut"
}
"platforms": "atmelavr"
}

View File

@ -1,9 +1,11 @@
name=FastShiftOut
version=0.1.5
version=0.2.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Class for up to 3x faster shift out.
sentence=Arduino library for (AVR) optimized shiftOut - e.g. 74HC595
paragraph=Implements print() interface.
category=Signal Input/Output
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/FastSHiftOut
architectures=*
includes=FastShiftOut.h
depends=

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,12 +1,10 @@
//
// FILE: FractionFindSum.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.1.1
// PURPOSE: demo
// DATE: 13-feb-2015
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/Fraction
//
#include "fraction.h"
@ -50,7 +48,7 @@ void findSum(Fraction f)
Serial.print(g);
Serial.print(" + ");
}
if (f == 0)
if (f == Fraction(0))
{
break;
}
@ -59,8 +57,10 @@ void findSum(Fraction f)
Serial.println(z);
Serial.println();
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,108 @@
//
// FILE: FractionMediant.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: Find fraction by binary search with mediant.
// DATE: 2020-04-21
//
// this method is not that fast but it shows a nice application for
// the mediant.
#include "fraction.h"
uint32_t start, stop;
void setup()
{
Serial.begin(115200);
Serial.print(__FILE__);
Serial.println();
Serial.println();
float f = PI;
start = micros();
Fraction x = fractionize(f);
stop = micros();
Serial.println(stop - start);
Serial.println(x);
Serial.println(x.toDouble(), 10);
Serial.println();
f = EULER;
start = micros();
Fraction y = fractionize(f);
stop = micros();
Serial.println(stop - start);
Serial.println(y);
Serial.println(y.toDouble(), 10);
Serial.println();
Serial.println("done...\n");
}
void loop()
{
float f = random(1000000) * 0.000001;
// reference
start = micros();
Fraction y(f);
stop = micros();
Serial.println();
Serial.print(stop - start);
Serial.print("\t");
Serial.print(y);
Serial.print("\t");
Serial.print(f, 10);
Serial.print("\t");
Serial.print(y.toDouble(), 10);
Serial.print("\t");
Serial.println(f - y.toDouble(), 10);
// mediant method.
start = micros();
y = fractionize(f);
stop = micros();
Serial.print(stop - start);
Serial.print("\t");
Serial.print(y);
Serial.print("\t");
Serial.print(f, 10);
Serial.print("\t");
Serial.print(y.toDouble(), 10);
Serial.print("\t");
Serial.println(f - y.toDouble(), 10);
}
Fraction fractionize(float f)
{
float acc = 1e-6;
float d1 = 0;
float d2 = 0;
Fraction a(0, 1);
Fraction b(9999, 1);
Fraction q(f);
for (int i = 0; i < 500; i++)
{
Fraction c = Fraction::mediant(a, b); // NOTE middle(a,b) is slower and worse!
if ( c.toDouble() < f) a = c;
else b = c;
d1 = abs(f - a.toDouble());
d2 = abs(f - b.toDouble());
if (d1 < acc && d2 < acc) break;
}
if (d1 < d2) return a;
return b;
}
// -- END OF FILE --

View File

@ -1,12 +1,10 @@
//
// FILE: fractionExerciser.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.1.1
// PURPOSE: demo sketch for fraction math
// DATE:
// URL:
//
// Released to the public domain
// DATE: 2015-03-29
// URL: https://github.com/RobTillaart/Fraction
//
#include "fraction.h"

View File

@ -1,12 +1,10 @@
//
// FILE: fractionTest01.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.1.1
// PURPOSE: test sketch for fraction math
// DATE:
// URL:
//
// Released to the public domain
// DATE: 2015-01-25
// URL: https://github.com/RobTillaart/Fraction
//
#include "fraction.h"
@ -18,6 +16,7 @@ Fraction b(1, 4);
Fraction n(0, 5);
Fraction p(5);
Fraction pi(PI);
Fraction e(EULER);
void setup()
{
@ -33,6 +32,9 @@ void setup()
Serial.println(p);
Serial.println(q);
Serial.println(pi);
Serial.println(e);
Serial.println(Fraction::middle(pi, e));
Serial.println(Fraction::mediant(pi, e));
Serial.println();
testPlus();

View File

@ -1,16 +1,17 @@
//
// FILE: fraction.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// PURPOSE: library for fractions for Arduino
// URL: https://github.com/RobTillaart/Arduino
// VERSION: 0.1.10
// PURPOSE: Arduino library to implement a Fraction datatype
// URL: https://github.com/RobTillaart/Fraction
//
// Released to the public domain
//
// TODO
// - divide by zero errors
// - test extensively
//
// 0.1.10 2020-06-10 fix library.json
// 0.1.9 refactor
// 0.1.8 - refactor made constructors explicit; fix issue #33 double --> float
// 0.1.07 - major refactoring by Chris-A
// 0.1.06 - added proper(), mediant(), angle();
@ -24,8 +25,10 @@
#include "fraction.h"
//////////////////////////////////////
//
// CONSTRUCTORS
//
Fraction::Fraction(double d)
{
Fraction::split(float(d));
@ -45,16 +48,29 @@ void Fraction::split(float f)
// EULER = 2721/1001; // 1.1e-7
// EULER = 1264/465; // 2.2e-6
// get robust for small values.
// get robust for small values. (effectively zero)
if (abs(f) < 0.00001)
{
n = 0;
d = 1;
return;
}
if (int32_t(f) == f)
{
n = int32_t(f);
d = 1;
return;
}
// Normalize to 0.0 ... 1.0
bool negative = f < 0;
if (negative) f = -f;
// TODO investigate different strategy:
// intpart = int32_t(f); // strip of the integer part.
// f = f - intpart; // determine remainder
// determine n, d
// n += intpart * d; // add integer part * denominator to fraction.
bool reciproke = f > 1;
if (reciproke) f = 1/f;
@ -79,10 +95,16 @@ Fraction::Fraction(int32_t p, int32_t q) : n(p), d(q)
simplify();
}
//////////////////////////////////////
//
// PRINTING
//
size_t Fraction::printTo(Print& p) const
{
size_t s = 0;
// TODO split of sign first
//
// vs 22/7 => 3_1/7
// if (n >= d)
// {
@ -96,12 +118,21 @@ size_t Fraction::printTo(Print& p) const
return s;
};
//////////////////////////////////////
//
// EQUALITIES
//
bool Fraction::operator == (const Fraction &c)
{
return (n * c.d) == (d * c.n);
}
// bool Fraction::operator == (const float &f)
// {
// Fraction c(f);
// return (n * c.d) == (d * c.n);
// }
bool Fraction::operator != (const Fraction &c)
{
return (n * c.d) != (d * c.n);
@ -127,13 +158,19 @@ bool Fraction::operator <= (const Fraction &c)
return (n * c.d) <= (d * c.n);
}
//////////////////////////////////////
//
// NEGATE
//
Fraction Fraction::operator - ()
{
return Fraction(-n, d);
}
//////////////////////////////////////
//
// BASIC MATH
//
Fraction Fraction::operator + (const Fraction &c)
{
if (d == c.d)
@ -215,7 +252,6 @@ float Fraction::toDouble()
return (1.0 * n) / d;
}
// wikipedia
// fraction is proper if abs(fraction) < 1
bool Fraction::isProper()
{
@ -229,15 +265,18 @@ float Fraction::toAngle()
}
////////////////////////////////////////////////////////////
//////////////////////////////////////
//
// STATIC
//
// Mediant - http://www.cut-the-knot.org/Curriculum/Arithmetic/FCExercise.shtml
// void Fraction::mediant(Fraction c)
// {
// n += c.n;
// d += c.d;
// simplify();
// n += c.n;
// d += c.d;
// simplify();
// }
//
// the mediant is a fraction that is always between 2 fractions
// at least if within precission.
@ -246,6 +285,13 @@ Fraction Fraction::mediant(const Fraction &a, const Fraction &b)
return Fraction(a.n + b.n, a.d + b.d);
}
// the middle is a fraction that is between 2 fractions
// at least if within precission.
Fraction Fraction::middle(const Fraction &a, const Fraction &b)
{
return Fraction(a.n*b.d + b.n*a.d, 2 * a.d * b.d);
}
// approximate a fraction with defined denominator
// sort of setDenominator(uint16_t den);
Fraction Fraction::setDenominator(const Fraction &a, uint16_t b)
@ -256,9 +302,11 @@ Fraction Fraction::setDenominator(const Fraction &a, uint16_t b)
}
////////////////////////////////////////////////////////////////
//////////////////////////////////////
//
// PRIVATE
// http://en.wikipedia.org/wiki/Binary_GCD_algorithm
//
int32_t Fraction::gcd(int32_t a , int32_t b)
{
while ( a != 0 )
@ -282,8 +330,8 @@ void Fraction::simplify()
int32_t p = abs(n);
int32_t q = abs(d);
int32_t x = gcd(p,q);
p = p/x;
q = q/x;
p = p / x;
q = q / x;
// denominator max 4 digits keeps mul and div simple
// in preventing overflow
@ -293,14 +341,15 @@ void Fraction::simplify()
p = (p + 5)/10;
q = (q + 5)/10;
x = gcd(p, q);
p = p/x;
q = q/x;
p = p / x;
q = q / x;
}
n = (neg)?-p:p;
n = (neg) ? -p : p;
d = q;
}
//////////////////////////////////////////////////////////////////////////////
//
// fractionize() - finds the fraction representation of a float
// PRE: 0 <= f < 1.0
//
@ -312,106 +361,14 @@ void Fraction::simplify()
//
// MINIMALISTIC
// (100x) micros()=51484
/*
float Fraction::fractionize(float f) // simple, small, 2nd fastest
{
n = round(f * 10000); // why not 1000000 ?
d = 10000;
simplify();
return 0; // abs(f - this.toDouble());
}
*/
// LINEAR SEARCH
// (100x) micros()=18873064
// slow not stable
/*
float Fraction::fractionize(float f)
{
long nn = 1, dd = 1;
float r = 1 / f;
float delta = f * dd - nn;
while (abs(delta) > 0.00001 && (dd < 10000))
{
dd++;
if (delta < 0)
{
nn++;
dd = nn * r;
}
delta = f * dd - nn;
}
n = nn;
d = dd;
return delta;
}
*/
/*
// LINEAR SEARCH (mirror optimized)
// (100x) micros()=
// slow but stable version
float Fraction::fractionize(float f)
{
long nn = 1, dd = 1;
bool inverse = false;
if (f > 0.5)
{
f = 1-f;
inverse = true;
}
float r = 1 / f;
float delta = f * dd - nn;
while (abs(delta) > 0.00001 && (dd < 10000))
{
dd++;
if (delta < 0)
{
nn++;
dd = nn * r;
}
delta = f * dd - nn;
}
n = inverse?(dd - nn):nn;
d = dd;
return delta;
}
*/
// ADD BY DIGIT
// - does not find "magic fractions" e.g. pi = 355/113
// (100x) micros()=392620
/*
float Fraction::fractionize(float f) // divide and conquer, simple, small, 2nd fastest
{
Fraction t((long)0);
for (long dd = 10; dd < 1000001; dd *= 10)
{
f *= 10;
int ff = f;
t += Fraction(ff, dd);
f -= ff;
}
n = t.n;
d = t.d;
return f;
}
*/
// Dr. Peterson
// - http://mathforum.org/library/drmath/view/51886.html
// (100x) micros()=96048
// showed errors for very small values around 0
void Fraction::fractionize(float val)
{ // find nearest fraction
float Precision = 0.000001;
{
// find nearest fraction
float Precision = 0.0000001;
Fraction low(0, 1); // "A" = 0/1
Fraction high(1, 1); // "B" = 1/1
for (int i = 0; i < 100; ++i)
@ -456,46 +413,4 @@ void Fraction::fractionize(float val)
d = high.d;
}
// BINARY SEARCH
// - http://www.gamedev.net/topic/354209-how-do-i-convert-a-decimal-to-a-fraction-in-c/
// (100x) micros()=1292452
// slower
/*
float Fraction::fractionize(float value) // size ok, too slow.
{
int max_denominator = 10000;
int low_n = 0;
int low_d = 1;
int high_n = 1;
int high_d = 1;
int mid_n;
int mid_d;
do
{
mid_n = low_n + high_n;
mid_d = low_d + high_d;
if ( mid_n < value * mid_d )
{
low_n = mid_n;
low_d = mid_d;
n = high_n;
d = high_d;
}
else
{
high_n = mid_n;
high_d = mid_d;
n = low_n;
d = low_d;
}
} while ( mid_d <= max_denominator );
return 0;
}
*/
//
// END OF FILE
//
// -- END OF FILE --

View File

@ -1,19 +1,15 @@
#pragma once
//
// FILE: fraction.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// PURPOSE: demo library for fractions for Arduino
// URL:
// VERSION: 0.1.10
// PURPOSE: Arduino library to implement a Fraction datatype
// URL: https://github.com/RobTillaart/Fraction
//
// Released to the public domain
//
#ifndef Fraction_h
#define Fraction_h
#include "Arduino.h"
#define FRACTIONLIBVERSION "0.1.8"
#define FRACTIONLIBVERSION "0.1.10"
class Fraction: public Printable
{
@ -33,6 +29,7 @@ public:
// equalities
bool operator == (const Fraction&);
// bool operator == (const float&);
bool operator != (const Fraction&);
bool operator > (const Fraction&);
bool operator >= (const Fraction&);
@ -58,7 +55,12 @@ public:
bool isProper(); // abs(f) < 1
float toAngle();
int32_t nominator() { return n; };
int32_t denominator() { return d; };
static Fraction mediant(const Fraction&, const Fraction&);
static Fraction middle(const Fraction&, const Fraction&);
// approximate a fraction with defined denominator
static Fraction setDenominator(const Fraction&, uint16_t);
@ -73,7 +75,4 @@ protected:
int32_t d;
};
#endif
//
// -- END OF FILE --
//

View File

@ -0,0 +1,18 @@
# Syntax Coloring Map For Fraction
# Datatypes (KEYWORD1)
Fraction KEYWORD1
# Methods and Functions (KEYWORD2)
toDouble KEYWORD2
toFloat KEYWORD2
isProper KEYWORD2
toAngle KEYWORD2
nominator KEYWORD2
denominator KEYWORD2
mediant KEYWORD2
middle KEYWORD2
setDenominator KEYWORD2
# Constants (LITERAL1)

View File

@ -1,7 +1,7 @@
{
"name": "Fraction",
"keywords": "Fraction,quotient,math,nominator,denominator",
"description": "Library for using fractions.",
"keywords": "Fraction, quotient, math, nominator, denominator",
"description": "Arduino library to implement a Fraction datatype. Nominator and denominator are limited to 4 digits. Experimental.",
"authors":
[
{
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/Fraction.git"
},
"version":"0.1.8",
"version":"0.1.10",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/Fraction"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=Fraction
version=0.1.8
version=0.1.10
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library for using fractions.
paragraph=
sentence=Arduino library to implement a Fraction datatype
paragraph=Nominator and denominator are limited to 4 digits. Experimental.
category=Data Processing
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/Fraction
architectures=*
includes=fraction.h
depends=

View File

@ -1,12 +1,30 @@
# Fraction
Arduino library to implement a Fraction datatype (experimental)
## Description
The fraction library implements fractional numbers a.k.a. Q,
(integers are Z and floats/doubles are R),
and the conversion to floats.
Fraction is an experimental fraction library for the Arduino.
The code is working with a number of limitations among others:
- denominator is max 4 digits to keep code for multiply and divide simple
- therefore not all fractions are exact
- Fractions are not exact (even floats are not exact)
- the range of numbers supported is limited.
- code is experimental still.
That said, the library is useful e.g. to display float numbers as a fraction.
From programming point of view the fractionize function, converting a double
into a fraction is a nice programming problem, fast with a minimal error.
In short, use fractions with care otherwise your sketch might get broken ;)
In short, use fractions with care otherwise your sketch might get broken ;)
## Operations
See examples
## Use with care
The library is reasonably tested, and if problems arise please let me know.

View File

@ -1,300 +1,59 @@
#pragma once
//
// FILE: functionGenerator.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.04
// PURPOSE: functionGenerator functions (use with care)
// URL:
//
// HISTORY:
// 0.1.00 - 2015-01-01 - initial version
// 0.1.01 - 2015-01-01 - initial class version
// 0.1.02 - 2015-01-01 - refactor and research
// 0.1.03 - 2015-01-02 - added stair, more refactoring
// 0.1.04 - 2015-01-03 - added integer versions - to be used with 8 bit DAC
// VERSION: 0.2.0
// PURPOSE: wave form generating functions (use with care)
// URL: https://github.com/RobTillaart/FunctionGenerator
//
#ifndef functiongenerator_h
#define functiongenerator_h
#include "Arduino.h"
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
#define FUNCTIONGENERATOR_LIB_VERSION "0.1.04"
#define FUNCTIONGENERATOR_LIB_VERSION "0.2.0"
class funcgen
{
public:
funcgen(double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
begin(period, amplitude, phase, yShift);
}
void begin(double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
_period = period;
_freq1 = 1 / period;
_freq2 = 2 * _freq1;
_freq4 = 4 * _freq1;
_freq0 = TWO_PI * _freq1;
_amplitude = amplitude;
_phase = phase;
_yShift = yShift;
}
double sawtooth(double t)
{
double rv;
t += _phase;
if (t >= 0.0)
{
if (t >= _period) t = fmod(t, _period);
rv = _amplitude * (-1.0 + t *_freq2);
}
else
{
t = -t;
if (t >= _period) t = fmod(t, _period);
rv = _amplitude * ( 1.0 - t * _freq2);
}
rv += _yShift;
return rv;
}
double triangle(double t)
{
double rv;
t += _phase;
if (t < 0.0)
{
t = -t;
}
if (t >= _period) t = fmod(t, _period);
if ( t * 2 < _period)
{
rv = _amplitude * (-1.0 + t * _freq4);
}
else
{
rv = _amplitude * (3.0 - t * _freq4);
}
rv += _yShift;
return rv;
}
double square(double t)
{
double rv;
t += _phase;
if (t >= 0)
{
if (t >= _period) t = fmod(t, _period);
if ((t + t) < _period) rv = _amplitude;
else rv = -_amplitude;
}
else
{
t = -t;
if (t >= _period) t = fmod(t, _period);
if ( t * 2 < _period) rv = -_amplitude;
else rv = _amplitude;
}
rv += _yShift;
return rv;
}
double sinus(double t)
{
double rv;
t += _phase;
rv = _amplitude * sin(t * _freq0);
rv += _yShift;
return rv;
}
double stair(double t, uint16_t steps = 8)
{
t += _phase;
if (t >= 0)
{
if (t >= _period) t = fmod(t, _period);
int level = steps * t / _period;
return _yShift + _amplitude * (-1.0 + 2.0 * level / (steps - 1));
}
t = -t;
if (t >= _period) t = fmod(t, _period);
int level = steps * t / _period;
return _yShift + _amplitude * (1.0 - 2.0 * level / (steps - 1));
}
funcgen(float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0);
// configuration
void setPeriod(float period = 1.0);
float getPeriod() { return _period; };
void setFrequency(float freq = 1.0) { setPeriod(1/freq); };
float getFrequency() { return _freq1; };
void setAmplitude(float ampl = 1.0) { _amplitude = ampl; };
float getAmplitude() { return _amplitude; };
void setPhase(float phase = 0.0) { _phase = phase; };
float getPhase() { return _phase; };
void setYShift(float yShift = 0.0) { _yShift = yShift; };
float getYShift() { return _yShift; };
// constant amplitude
float line();
// constant zero for calibration
float zero();
// standard wave forms
float sawtooth(float t);
float triangle(float t);
float square(float t);
float sinus(float t);
float stair(float t, uint16_t steps = 8);
float random();
private:
double _period;
double _freq0;
double _freq1;
double _freq2;
double _freq4;
double _amplitude;
double _phase;
double _yShift;
float _period;
float _freq0;
float _freq1;
float _freq2;
float _freq4;
float _amplitude;
float _phase;
float _yShift;
// Marsaglia 'constants'
uint32_t _m_w = 1;
uint32_t _m_z = 2;
uint32_t _random();
};
//
// INTEGER VERSIONS FOR 8 BIT DAC
//
// 8 bits version
// t = 0..9999 period 10000 in millis, returns 0..255
/*
uint8_t ifgsaw(uint16_t t, uint16_t period = 1000)
{
return 255L * t / period;
}
uint8_t ifgtri(uint16_t t, uint16_t period = 1000)
{
if (t * 2 < period) return 510L * t / period;
return 255L - 510L * t / period;
}
uint8_t ifgsqr(uint16_t t, uint16_t period = 1000)
{
if (t * 2 < period) return 510L * t / period;
return 255L - 510L * t / period;
}
uint8_t ifgsin(uint16_t t, uint16_t period = 1000)
{
return sin(355L * t / period / 113); // LUT
}
uint8_t ifgstr(uint16_t t, uint16_t period = 1000, uint16_t steps = 8)
{
int level = 1L * steps * t / period;
return 255L * level / (steps - 1);
}
*/
//
// SIMPLE DOUBLE ONES
//
// t = 0..period
// period = 0.001 ... 10000 ?
/*
double fgsaw(double t, double period = 1.0)
{
if (t >= 0) return -1.0 + 2 * t / period;
return 1.0 + 2 * t / period;
}
double fgtri(double t, double period = 1.0)
{
if (t < 0) t = -t;
if (t * 2 < period) return -1.0 + 4 * t / period;
return 3.0 - 4 * t / period;
}
double fgsqr(double t, double period = 1.0)
{
if (t >= 0)
{
if ( 2 * t < period) return 1.0;
return -1.0;
}
t = -t;
if (2 * t < period) return -1.0;
return 1.0;
}
double fgsin(double t, double period = 1.0)
{
return sin(TWO_PI * t / period);
}
double fgstr(double t, double period = 1.0, uint16_t steps = 8)
{
if (t >= 0)
{
int level = steps * t / period;
return -1.0 + 2.0 * level / (steps - 1);
}
t = -t;
int level = steps * t / period;
return 1.0 - 2.0 * level / (steps - 1);
}
*/
//
// FULL DOUBLES ONES
//
double fgsaw(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
return yShift + amplitude * (-1.0 + 2 * t / period);
}
t = -t;
if (t >= period) t = fmod(t, period);
return yShift + amplitude * ( 1.0 - 2 * t / period);
}
double fgtri(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, double dutyCycle = 0.50)
{
t += phase;
if (t < 0) t = -t;
if (t >= period) t = fmod(t, period);
// 50 % dutyCycle = faster
// if (t * 2 < period) return yShift + amplitude * (-1.0 + 4 * t / period);
// return yShift + amplitude * (3.0 - 4 * t / period);
if (t < dutyCycle * period) return yShift + amplitude * (-1.0 + 2 * t / (dutyCycle * period));
// return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) - 2 * t / ((1 - dutyCycle) * period));
return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) * ( 1 - t / period));
}
double fgsqr(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, double dutyCycle = 0.50)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
if (t < dutyCycle * period) return yShift + amplitude;
return yShift - amplitude;
}
t = -t;
if (t >= period) t = fmod(t, period);
if (t < dutyCycle * period) return yShift - amplitude;
return yShift + amplitude;
}
double fgsin(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
{
t += phase;
double rv = yShift + amplitude * sin(TWO_PI * t / period);
return rv;
}
double fgstr(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0, uint16_t steps = 8)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
int level = steps * t / period;
return yShift + amplitude * (-1.0 + 2.0 * level / (steps - 1));
}
t = -t;
if (t >= period) t = fmod(t, period);
int level = steps * t / period;
return yShift + amplitude * (1.0 - 2.0 * level / (steps - 1));
}
#endif
//
// END OF FILE
//

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,44 +1,54 @@
//
// FILE: functionGenerator.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.02
// VERSION: 0.2.0
// PURPOSE: demo function generators
// DATE: 2015-01-03
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/FunctionGenerator
//
#include "functionGenerator.h"
funcgen gen;
uint32_t lastTime = 0;
char choice = '0';
void setup()
{
Serial.begin(115200);
Serial.begin(500000);
Serial.println(__FILE__);
Serial.println("Start ");
gen.setFrequency(4);
gen.setAmplitude(50);
}
void loop()
{
uint32_t now = millis();
int choice = analogRead(A0) / 200; // signal selection potMeter
int period = 1000; // in milliseconds
float val;
if (now != lastTime)
if (Serial.available())
{
lastTime = now;
int p = now % period;
choice = Serial.read();
}
// wave selection by potMeter
// int choice = analogRead(A0) / 200;
float val;
// wait for next millisecond;
if (millis() - lastTime > 0)
{
lastTime = millis();
float t = lastTime * 0.001;
switch (choice)
{
case 0: val = fgsqr(p, period); break;
case 1: val = fgsaw(p, period); break;
case 2: val = fgtri(p, period); break;
case 3: val = fgstr(p, period); break;
default: val = fgsin(p, period); break;
case '0': val = gen.square(t); break;
case '1': val = gen.sawtooth(t); break;
case '2': val = gen.triangle(t); break;
case '3': val = gen.stair(t); break;
case '4': val = gen.sinus(t); break;
case '5': val = gen.line(); break;
case '6': val = gen.random(); break;
default: val = gen.zero(); break;
}
Serial.println(val);
}

View File

@ -0,0 +1,47 @@
//
// FILE: functionGeneratorDuoPlot.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo function generators
// DATE: 2020-06-10
// URL: https://github.com/RobTillaart/FunctionGenerator
//
// use a Serial plotter to show the data
#include "functionGenerator.h"
funcgen gen1;
funcgen gen2;
void setup()
{
Serial.begin(115200);
// Serial.print("Start functionGeneratorPerformance - LIB VERSION: ");
// Serial.println(FUNCTIONGENERATOR_LIB_VERSION);
gen1.setFrequency(13);
gen1.setAmplitude(50);
gen1.setPhase(0);
gen1.setYShift(0);
gen2.setFrequency(17);
gen2.setAmplitude(25);
gen2.setPhase(0.25);
gen2.setYShift(25);
}
void loop()
{
float t = millis() * 0.001;
float x = gen1.sinus(t);
float y = gen2.sinus(t);
Serial.print(x);
Serial.print("\t");
Serial.print(y);
Serial.print("\t");
Serial.print(x + y);
Serial.println();
}
// END OF FILE

View File

@ -1,12 +1,10 @@
//
// FILE: functionGeneratorPerformance.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// VERSION: 0.2.0
// PURPOSE: demo function generators
// DATE: 2015-01-01
// URL:
//
// Released to the public domain
// URL: https://github.com/RobTillaart/FunctionGenerator
//
#include "functionGenerator.h"
@ -14,46 +12,58 @@
uint32_t start;
uint32_t stop;
volatile double t;
volatile double y;
volatile float t;
volatile float y;
funcgen gen;
void setup()
{
Serial.begin(115200);
Serial.println("Start functionGeneratorPerformance - LIB VERSION: ");
Serial.print("Start functionGeneratorPerformance - LIB VERSION: ");
Serial.println(FUNCTIONGENERATOR_LIB_VERSION);
Serial.println("func \t usec\t max calls/sec");
Serial.println("func \t\tusec\tmax calls/sec");
y = analogRead(A0) / 1024;
test_fgsqr();
test_square();
delay(10);
test_fgsaw();
test_sawtooth();
delay(10);
test_fgtri();
test_triangle();
delay(10);
test_fgsin();
test_sinus();
delay(10);
test_fgstr();
test_stair();
delay(10);
test_random();
delay(10);
test_line();
delay(10);
test_zero();
delay(10);
Serial.println();
Serial.println("t \t sqr\t saw\t tri\t sin\t str");
Serial.println("t \t sqr\t saw\t tri\t sin\t str\t rnd\t line\t zero");
for (int i = -400; i < 400; i += 2)
{
// func(double t, double period = 1.0, double amplitude = 1.0, double phase = 0.0, double yShift = 0.0)
double t = i * 0.01;
float t = i * 0.01;
Serial.print(t);
Serial.print("\t");
Serial.print(fgsqr(t));
Serial.print(gen.square(t));
Serial.print("\t");
Serial.print(fgsaw(t)); //, 2.0, 1.0, 0.5));
Serial.print(gen.sawtooth(t));
Serial.print("\t");
Serial.print(fgtri(t)); //, 1.0, 0.5, 1.0));
Serial.print(gen.triangle(t));
Serial.print("\t");
Serial.print(fgsin(t)); // , TWO_PI, 1.0, PI / 4)); // period 2PI, phase = 45°
// Serial.print(fgsin(t, 1.0, 0.5, -0.5, 0.5));
Serial.print(gen.sinus(t));
Serial.print("\t");
Serial.print(fgstr(t));
Serial.print(gen.stair(t));
Serial.print("\t");
Serial.print(gen.random());
Serial.print("\t");
Serial.print(gen.line());
Serial.print("\t");
Serial.print(gen.zero());
Serial.println();
}
Serial.println("\ndone...");
@ -62,71 +72,121 @@ void setup()
/******************************************************************/
void test_fgsqr()
void test_square()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgsqr(i);
t = gen.square(i);
}
stop = micros();
Serial.print("fsqr:\t");
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_fgsaw()
void test_sawtooth()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgsaw(i);
t = gen.sawtooth(i);
}
stop = micros();
Serial.print("fsaw:\t");
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_fgtri()
void test_triangle()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgtri(i);
t = gen.triangle(i);
}
stop = micros();
Serial.print("ftri:\t");
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_fgsin()
void test_sinus()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgsin(i);
t = gen.sinus(i);
}
stop = micros();
Serial.print("fsin:\t");
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_fgstr()
void test_stair()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = fgstr(i);
t = gen.stair(i);
}
stop = micros();
Serial.print("fstr:\t");
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_random()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = gen.random();
}
stop = micros();
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_line()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = gen.line();
}
stop = micros();
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
}
void test_zero()
{
start = micros();
for (int i = 0; i < 10000; i++)
{
t = gen.zero();
}
stop = micros();
Serial.print(__FUNCTION__);
Serial.print(":\t");
Serial.print((stop - start) / 10000.0);
Serial.print("\t");
Serial.println(1000000.0 / ((stop - start) / 10000.0));
@ -136,4 +196,4 @@ void loop()
{
}
// END OF FILE
// END OF FILE

View File

@ -0,0 +1,46 @@
//
// FILE: functionGeneratorPlotter.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo function generators
// DATE: 2020-06-10
// URL: https://github.com/RobTillaart/FunctionGenerator
//
// use a Serial plotter to show the data
#include "functionGenerator.h"
funcgen gen;
void setup()
{
Serial.begin(115200);
// Serial.print("Start functionGeneratorPerformance - LIB VERSION: ");
// Serial.println(FUNCTIONGENERATOR_LIB_VERSION);
gen.setAmplitude(50);
}
void loop()
{
float t = millis() * 0.001;
Serial.print(gen.square(t));
Serial.print("\t");
Serial.print(gen.sawtooth(t));
Serial.print("\t");
Serial.print(gen.triangle(t));
Serial.print("\t");
Serial.print(gen.sinus(t));
Serial.print("\t");
Serial.print(gen.stair(t));
Serial.print("\t");
Serial.print(gen.random());
Serial.print("\t");
Serial.print(gen.line());
Serial.print("\t");
Serial.print(gen.zero());
Serial.println();
}
// END OF FILE

View File

@ -0,0 +1,300 @@
//
// FILE: functionGenerator.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: wave form generating functions (use with care)
// URL: https://github.com/RobTillaart/FunctionGenerator
//
// HISTORY:
// 0.1.00 2015-01-01 initial version
// 0.1.01 2015-01-01 initial class version
// 0.1.02 2015-01-01 refactor and research
// 0.1.03 2015-01-02 added stair, more refactoring
// 0.1.04 2015-01-03 added integer versions - to be used with 8 bit DAC
// 0.1.5 2017-07-29 Fix issue #33 (dbl -> float)
// 0.2.0 2020-06-10 main refactoring and cleanup
#include "functionGenerator.h"
funcgen::funcgen(float period, float amplitude, float phase, float yShift)
{
setPeriod(period);
setAmplitude(amplitude);
setPhase(phase);
setYShift(yShift);
}
void funcgen::setPeriod(float period)
{
_period = period;
_freq1 = 1 / period;
_freq2 = 2 * _freq1;
_freq4 = 4 * _freq1;
_freq0 = TWO_PI * _freq1;
}
float funcgen::line()
{
return _yShift + _amplitude;
}
float funcgen::zero()
{
return 0;
}
float funcgen::sawtooth(float t)
{
float rv;
t += _phase;
if (t >= 0.0)
{
if (t >= _period) t = fmod(t, _period);
rv = _amplitude * (-1.0 + t *_freq2);
}
else
{
t = -t;
if (t >= _period) t = fmod(t, _period);
rv = _amplitude * ( 1.0 - t * _freq2);
}
rv += _yShift;
return rv;
}
float funcgen::triangle(float t)
{
float rv;
t += _phase;
if (t < 0.0)
{
t = -t;
}
if (t >= _period) t = fmod(t, _period);
if ( t * 2 < _period)
{
rv = _amplitude * (-1.0 + t * _freq4);
}
else
{
rv = _amplitude * (3.0 - t * _freq4);
}
rv += _yShift;
return rv;
}
float funcgen::square(float t)
{
float rv;
t += _phase;
if (t >= 0)
{
if (t >= _period) t = fmod(t, _period);
if ((t + t) < _period) rv = _amplitude;
else rv = -_amplitude;
}
else
{
t = -t;
if (t >= _period) t = fmod(t, _period);
if ( t * 2 < _period) rv = -_amplitude;
else rv = _amplitude;
}
rv += _yShift;
return rv;
}
float funcgen::sinus(float t)
{
float rv;
t += _phase;
rv = _amplitude * sin(t * _freq0);
rv += _yShift;
return rv;
}
float funcgen::stair(float t, uint16_t steps)
{
t += _phase;
if (t >= 0)
{
if (t >= _period) t = fmod(t, _period);
int level = steps * t / _period;
return _yShift + _amplitude * (-1.0 + 2.0 * level / (steps - 1));
}
t = -t;
if (t >= _period) t = fmod(t, _period);
int level = steps * t / _period;
return _yShift + _amplitude * (1.0 - 2.0 * level / (steps - 1));
}
float funcgen::random()
{
// TODO smart reseed needed
float rv = _yShift + _amplitude * _random() * 0.2328306436E-9; // div 0xFFFFFFFF
return rv;
}
// An example of a simple pseudo-random number generator is the
// Multiply-with-carry method invented by George Marsaglia.
// two initializers (not null)
uint32_t funcgen::_random()
{
_m_z = 36969L * (_m_z & 65535L) + (_m_z >> 16);
_m_w = 18000L * (_m_w & 65535L) + (_m_w >> 16);
return (_m_z << 16) + _m_w; /* 32-bit result */
}
//
// INTEGER VERSIONS FOR 8 BIT DAC
//
// 8 bits version
// t = 0..9999 period 10000 in millis, returns 0..255
/*
uint8_t ifgsaw(uint16_t t, uint16_t period = 1000)
{
return 255L * t / period;
}
uint8_t ifgtri(uint16_t t, uint16_t period = 1000)
{
if (t * 2 < period) return 510L * t / period;
return 255L - 510L * t / period;
}
uint8_t ifgsqr(uint16_t t, uint16_t period = 1000)
{
if (t * 2 < period) return 510L * t / period;
return 255L - 510L * t / period;
}
uint8_t ifgsin(uint16_t t, uint16_t period = 1000)
{
return sin(355L * t / period / 113); // LUT
}
uint8_t ifgstr(uint16_t t, uint16_t period = 1000, uint16_t steps = 8)
{
int level = 1L * steps * t / period;
return 255L * level / (steps - 1);
}
*/
//
// SIMPLE float ONES
//
// t = 0..period
// period = 0.001 ... 10000 ?
/*
float fgsaw(float t, float period = 1.0)
{
if (t >= 0) return -1.0 + 2 * t / period;
return 1.0 + 2 * t / period;
}
float fgtri(float t, float period = 1.0)
{
if (t < 0) t = -t;
if (t * 2 < period) return -1.0 + 4 * t / period;
return 3.0 - 4 * t / period;
}
float fgsqr(float t, float period = 1.0)
{
if (t >= 0)
{
if ( 2 * t < period) return 1.0;
return -1.0;
}
t = -t;
if (2 * t < period) return -1.0;
return 1.0;
}
float fgsin(float t, float period = 1.0)
{
return sin(TWO_PI * t / period);
}
float fgstr(float t, float period = 1.0, uint16_t steps = 8)
{
if (t >= 0)
{
int level = steps * t / period;
return -1.0 + 2.0 * level / (steps - 1);
}
t = -t;
int level = steps * t / period;
return 1.0 - 2.0 * level / (steps - 1);
}
*/
//
// FULL floatS ONES
//
float fgsaw(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
return yShift + amplitude * (-1.0 + 2 * t / period);
}
t = -t;
if (t >= period) t = fmod(t, period);
return yShift + amplitude * ( 1.0 - 2 * t / period);
}
float fgtri(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0, float dutyCycle = 0.50)
{
t += phase;
if (t < 0) t = -t;
if (t >= period) t = fmod(t, period);
// 50 % dutyCycle = faster
// if (t * 2 < period) return yShift + amplitude * (-1.0 + 4 * t / period);
// return yShift + amplitude * (3.0 - 4 * t / period);
if (t < dutyCycle * period) return yShift + amplitude * (-1.0 + 2 * t / (dutyCycle * period));
// return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) - 2 * t / ((1 - dutyCycle) * period));
return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) * ( 1 - t / period));
}
float fgsqr(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0, float dutyCycle = 0.50)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
if (t < dutyCycle * period) return yShift + amplitude;
return yShift - amplitude;
}
t = -t;
if (t >= period) t = fmod(t, period);
if (t < dutyCycle * period) return yShift - amplitude;
return yShift + amplitude;
}
float fgsin(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0)
{
t += phase;
float rv = yShift + amplitude * sin(TWO_PI * t / period);
return rv;
}
float fgstr(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0, uint16_t steps = 8)
{
t += phase;
if (t >= 0)
{
if (t >= period) t = fmod(t, period);
int level = steps * t / period;
return yShift + amplitude * (-1.0 + 2.0 * level / (steps - 1));
}
t = -t;
if (t >= period) t = fmod(t, period);
int level = steps * t / period;
return yShift + amplitude * (1.0 - 2.0 * level / (steps - 1));
}
// END OF FILE

View File

@ -0,0 +1,28 @@
# Syntax Coloring Map for funcgen
# Datatypes (KEYWORD1)
funcgen KEYWORD1
# Methods and Functions (KEYWORD2)
setPeriod KEYWORD2
getPeriod KEYWORD2
setFrequency KEYWORD2
getFrequency KEYWORD2
setAmplitude KEYWORD2
getAmplitude KEYWORD2
setPhase KEYWORD2
getPhase KEYWORD2
setYShift KEYWORD2
getYShift KEYWORD2
sawtooth KEYWORD2
triangle KEYWORD2
square KEYWORD2
sinus KEYWORD2
stair KEYWORD2
random KEYWORD2
line KEYWORD2
zero KEYWORD2
# Constants (LITERAL1)

View File

@ -1,7 +1,7 @@
{
"name": "FunctionGenerator",
"keywords": "Function,wave,generator,sawtooth,sinus,square,stair,triangle",
"description": "Experimental library for waveform generation (SW).",
"description": "Arduino library to generate wave forms (nummeric) for a DAC",
"authors":
[
{
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/FunctionGenerator"
},
"version":"0.1.4",
"version":"0.2.0",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/FunctionGenerator"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=FunctionGenerator
version=0.1.4
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Experimental library for waveform generation (SW).
sentence=Arduino library to generate wave forms (nummeric) for a DAC
paragraph=a.k.a. FunctionGenerator
category=Data Processing
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/FunctionGenerator
architectures=*
includes=functionGenerator.h
depends=

View File

@ -1,2 +1,44 @@
# FunctionGenerator
Arduino library to generate wave forms (nummeric) for a DAC
## Description
**Constructor**
* funcgen(float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0);
All parameters can be set in the constructor but also later in configuration.
**Configuration**
* void setPeriod(float period = 1.0); // seconds
* float getPeriod();
* void setFrequency(float freq = 1.0); // Hertz
* float getFrequency();
* void setAmplitude(float ampl = 1.0); // -
* float getAmplitude();
* void setPhase(float phase = 0.0); // phase of period
* float getPhase();
* void setYShift(float yShift = 0.0); // shift in amplitude, zero point.
* float getYShift();
**Wave forms**
t is time in seconds
* float sawtooth(float t);
* float triangle(float t);
* float square(float t);
* float sinus(float t);
* float stair(float t, uint16_t steps = 8);
* float random();
* float line();
* float zero();
Line() and zero() are functions that can be used
to drive a constant voltage from a DAC and can be
used to calibrate the generator / DAC combination.
## Operational
See examples.
TODO example with DAC. 8 12 16 bit.
TODO example with potmeters for 4 parameters
experimental

View File

@ -1,7 +1,7 @@
//
// FILE: HT16K33.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.3.0
// DATE: 2019-02-07
// PURPOSE: Arduino Library for HT16K33 4x7segment display
// URL: https://github.com/RobTillaart/HT16K33
@ -15,6 +15,10 @@
// 0.1.5 2019-11-30 refactor,
// 0.2.0 2020-06-13 ESP32 support; fix brightness bug;
// 0.2.1 2020-07-15 fix #160 - decimal point
// 0.2.2 2020-10-04 added displayDate() thanks to bepitama
// 0.2.3 2020-10-09 issue #4 add negative values for displayInt()
// 0.2.4 2020-10-10 refactor #5 setDigits() iso suppressLeadingZeroPlaces()
// 0.3.0 2020-10-12 negative float, cache control, extend displayRaw()
#include "HT16K33.h"
@ -97,19 +101,24 @@ void HT16K33::reset()
{
displayOn();
displayClear();
suppressLeadingZeroPlaces(3);
setDigits(1);
clearCache();
brightness(8);
}
void HT16K33::clearCache()
{
for (uint8_t i = 0; i < 5; i++)
{
_displayCache[i] = HT16K33_NONE;
}
brightness(8);
}
void HT16K33::displayOn()
{
writeCmd(HT16K33_ON);
writeCmd(HT16K33_DISPLAYON);
brightness(_bright); // TODO needed?
brightness(_bright);
}
void HT16K33::displayOff()
@ -120,7 +129,6 @@ void HT16K33::displayOff()
void HT16K33::blink(uint8_t val)
{
// TODO cache blink state too?
if (val > 0x03) val = 0x00;
writeCmd(HT16K33_BLINKOFF | (val << 1) );
}
@ -133,10 +141,16 @@ void HT16K33::brightness(uint8_t val)
writeCmd(HT16K33_BRIGHTNESS | _bright);
}
void HT16K33::setDigits(uint8_t val)
{
_digits = val > 4 ? 4 : val;
}
void HT16K33::suppressLeadingZeroPlaces(uint8_t val)
{
_leadingZeroPlaces = val > 4 ? 4 : val;
_digits = val > 4 ? 0 : 4 - val;
}
//////////////////////////////////////////
//
// display functions
@ -148,18 +162,37 @@ void HT16K33::displayClear()
displayColon(false);
}
// 0000..9999
// TODO negative numbers
// -999..9999
// DIV10 & DIV100 optimize?
void HT16K33::displayInt(int n)
{
uint8_t x[4], h, l;
bool neg = (n < 0);
if (neg) n = -n;
h = n / 100;
l = n - h * 100;
x[0] = h / 10;
x[1] = h - x[0] * 10;
x[2] = l / 10;
x[3] = l - x[2] * 10;
if (neg)
{
if (_digits >= 3)
{
x[0] = HT16K33_MINUS;
}
else
{
int i = 0;
for (i = 0; i < (4 - _digits); i++)
{
if (x[i] != 0) break;
x[i] = HT16K33_SPACE;
}
x[i-1] = HT16K33_MINUS;
}
}
display(x);
}
@ -176,6 +209,18 @@ void HT16K33::displayHex(uint16_t n)
display(x);
}
// 00.00 .. 99.99
void HT16K33::displayDate(uint8_t left, uint8_t right)
{
uint8_t x[4];
x[0] = left / 10;
x[1] = left - x[0] * 10;
x[2] = right / 10;
x[3] = right - x[2] * 10;
display(x, 1);
displayColon(false);
}
// 00:00 .. 99:99
void HT16K33::displayTime(uint8_t left, uint8_t right)
{
@ -189,13 +234,16 @@ void HT16K33::displayTime(uint8_t left, uint8_t right)
}
// only 0.000 .. 9999.
// TODO -999..-0.00
// TODO x.yEz
void HT16K33::displayFloat(float f)
{
// uint8_t neg = 0;
// if (f < 0) { neg = -1; f = -f; }
if (f > 9999 || f < 0 ) return;
if (f > 9999 || f < -999 )
{
// display overflow ?
return;
}
bool neg = (f < 0);
if (neg) f = -f;
int w = f;
int pt = 3;
@ -220,6 +268,14 @@ void HT16K33::displayFloat(float f)
x[1] = h - x[0] * 10;
x[2] = l / 10;
x[3] = l - x[2] * 10;
if (neg) // corrections for neg => all shift one position
{
x[3] = x[2];
x[2] = x[1];
x[1] = x[0];
x[0] = HT16K33_MINUS;
pt++;
}
display(x, pt);
}
@ -236,12 +292,13 @@ void HT16K33::displayTest(uint8_t del)
}
}
void HT16K33::displayRaw(uint8_t *arr)
void HT16K33::displayRaw(uint8_t *arr, bool colon)
{
writePos(0, arr[0]);
writePos(1, arr[1]);
writePos(3, arr[2]);
writePos(4, arr[3]);
writePos(2, colon ? 255 : 0);
}
void HT16K33::displayVULeft(uint8_t val)
@ -286,29 +343,24 @@ void HT16K33::displayVURight(uint8_t val)
void HT16K33::display(uint8_t *arr)
{
if (_leadingZeroPlaces)
for (uint8_t i = 0; i < (4 - _digits); i++)
{
for (uint8_t i = 0; i < _leadingZeroPlaces; i++)
{
if (arr[i] != 0) break;
arr[i] = HT16K33_SPACE;
}
if (arr[i] != 0) break;
arr[i] = HT16K33_SPACE;
}
writePos(0, charmap[arr[0]]);
writePos(1, charmap[arr[1]]);
writePos(3, charmap[arr[2]]);
writePos(4, charmap[arr[3]]);
// debug to Serial
// dumpSerial(arr, 0);
}
void HT16K33::display(uint8_t *arr, uint8_t pnt)
{
// to debug without display
// Serial.print(arr[0]);
// Serial.print(arr[1]);
// Serial.print(arr[2]);
// Serial.print(arr[3]);
// Serial.print(" ");
// Serial.println(pnt);
// debug to Serial
// dumpSerial(arr, pnt);
writePos(0, charmap[arr[0]], pnt == 0);
writePos(1, charmap[arr[1]], pnt == 1);
@ -321,6 +373,19 @@ void HT16K33::displayColon(uint8_t on)
writePos(2, on ? 2 : 0);
}
void HT16K33::dumpSerial(uint8_t *arr, uint8_t pnt)
{
// to debug without display
for (int i = 0; i < 4; i++)
{
if (arr[i] == HT16K33_SPACE) Serial.print(" ");
else if (arr[i] == HT16K33_MINUS) Serial.print("-");
else Serial.print(arr[i]);
}
Serial.print(" ");
Serial.println(pnt);
}
//////////////////////////////////////////////////////////
//
// PRIVATE
@ -334,17 +399,18 @@ void HT16K33::writeCmd(uint8_t cmd)
void HT16K33::writePos(uint8_t pos, uint8_t mask)
{
if (_displayCache[pos] == mask) return;
if (_cache && (_displayCache[pos] == mask)) return;
Wire.beginTransmission(_addr);
Wire.write(pos * 2);
Wire.write(mask);
Wire.endTransmission();
_displayCache[pos] = mask;
_displayCache[pos] = _cache ? mask : HT16K33_NONE;
}
void HT16K33::writePos(uint8_t pos, uint8_t mask, bool pnt)
{
if (pnt) mask |= 0x80;
else mask &= 0x7F;
writePos(pos, mask);
}

View File

@ -2,7 +2,7 @@
//
// FILE: HT16K33.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.3.0
// DATE: 2019-02-07
// PURPOSE: Arduino Library for HT16K33 4x7segment display
// http://www.adafruit.com/products/1002
@ -12,7 +12,7 @@
#include "Arduino.h"
#include "Wire.h"
#define HT16K33_LIB_VERSION "0.2.1"
#define HT16K33_LIB_VERSION "0.3.0"
class HT16K33
{
@ -25,30 +25,48 @@ public:
void begin();
void reset();
// default _cache is true as it is ~3x faster but if one has noise
// on the I2C and wants to force refresh one can disable caching
// for one or more calls.
void clearCache();
void cacheOn() { _cache = true; };
void cacheOff() { _cache = false; };
void displayOn();
void displayOff();
void brightness(uint8_t val); // 0 .. 15
void blink(uint8_t val); // 0 .. 3 0 = off
// 0,1,2,3,4 digits - will replace suppressLeadingZeroPlaces
void setDigits(uint8_t val);
// 0 = off, 1,2,3,4 digits space iso 0
void suppressLeadingZeroPlaces(uint8_t val);
void suppressLeadingZeroPlaces(uint8_t val); // will be obsolete
void displayClear();
void displayInt(int n); // 0000 .. 9999
void displayInt(int n); // -999 .. 9999
void displayHex(uint16_t n); // 0000 .. FFFF
// Date could be {month.day} or {day.hour} . as separator
// Time could be hh:mm or mm:ss or ss:uu (hundreds : as separator
//
void displayDate(uint8_t left, uint8_t right); // 00.00 .. 99.99
void displayTime(uint8_t left, uint8_t right); // 00:00 .. 99:99
void displayFloat(float f); // 0.000 .. 9999
void displayFloat(float f); // -999 .. 0.000 .. 9999
void display(uint8_t *arr); // array with 4 elements
void display(uint8_t *arr, uint8_t pt); // pt = digit with . (0..3)
void displayColon(uint8_t on); // 0 = off
void displayRaw(uint8_t *arr, bool colon = false); // max control
void displayTest(uint8_t del); // debug
void displayRaw(uint8_t *arr); // max control
void displayVULeft(uint8_t val); // 0..8
void displayVURight(uint8_t val); // 0..8
// DEBUG
void displayTest(uint8_t del);
void dumpSerial(uint8_t *arr, uint8_t pnt);
private:
void writeCmd(uint8_t cmd);
@ -57,7 +75,8 @@ private:
uint8_t _addr;
uint8_t _displayCache[5]; // for performance
uint8_t _leadingZeroPlaces;
bool _cache = true;
uint8_t _digits = 0;
uint8_t _bright;
};

View File

@ -7,10 +7,22 @@ Arduino Library for HT16K33 I2C 4x7segment display
This library is for the Adafruit 4x7segment display with HT16K33 driver,
http://www.adafruit.com/products/1002
This library is functionally less capable than Adafruits especially
it has no support for negative numbers.
However it is much faster for writing an int / hex to the display.
This library is functionally less capable than Adafruits.
However as it caches the values written to the display per position
it is faster for writing on average. The actual gain depends on the
application and of course the values.
## Perfomance
Version 0.3.0 allows one to switch the caching on/off to enforce
writing all positions e.g. in case of noisy I2C bus.
The example **demo_cache.ino** measures the performance gain of caching
for different I2C bus speeds.
```
Test on UNO, I2C.setClock(100000); // version 0.1.2
0-9999 integers 3.960 sec
0-65535 hexadecimal 23.685 sec
@ -19,33 +31,56 @@ Test on UNO, I2C.setClock(800000); // version 0.1.2
0-9999 integers 1.223 sec
0-65535 hexadecimal 6.350 sec
Substantial part of this performance gain is due to an internal
cache of the digits displayed, so other tests and real live
usage may result in different gain.
Test on UNO, I2C.setClock(100000); // version 0.3.0
0-9999 integers 4.092 sec // ~3% slower due to robustness & cache control
0-65535 hexadecimal 24.336 sec
```
## Release notes
## Multiple display
* 0.1.3
**suppressLeadingZeroPlaces(val)** replaces leading zero with space.
Works only correctly for integers (for now).
If you have two (or multiple) displays to display long integers
you can suppress 4 zero's in the left display and 3 zero's in the right display.
The library supports only one display. Using multiple displays (cascading) e.g. to
display more than 4 digits must done by the user - see **demo_dual1.ino**.
With dual display it is important to **setDigits()** for the displays correctly to
get leading/railing zero's correctly.
* 0.1.4
**displayRaw(ar[4])** added, to be able to display any pattern possible
**displayVULeft(val)** and **displayVURight(val)** added, shows 0..8 vertical bars,
uses **displayRaw()**.
## Interface
* 0.1.5
refactor
added minus sign in the char map.
#### setup behaviour
- **HT16K33(address)** address is 0x70..0x77 depending on the jumpers A0..A2. **0x70** is default
- **void begin(sda, scl)** for ESP32, select I2C pins, initialize I2C and calls **reset()**
- **void begin()** initialize I2C and calls **reset()**
- **void reset()** resets display
- **void clearCache()** forced clearing of the cache, to be used to switch the cache off just for one write.
- **void cacheOn()** enable caching, this is default behavior
- **void cacheOff()** disable caching, will force writing to every position
- **void displayOn()** enable display
- **void displayOff()** disable display, fast way to darken display e.g. for energy consumption
- **void brightness(val)** values (dim) 0..15 (bright)
- **void blink(val)** values 0..3 0 = off
- **void setDigits(val)** values 0..4, minimal number of digits shown, mandatory for large numbers on dual display.
- **void suppressLeadingZeroPlaces(val)** obsolete, replaced by setDigits
* 0.2.0
fix brightness bug
ESP32 support
#### datatypes
- **void displayClear()** empty display
- **void displayInt(n)** values -999 .. 9999
- **void displayHex(n)** values 0000 .. FFFF
- **void displayDate(left, right)** values 00.00..99.99 Date could be {month.day} or {day.hour} . as separator
- **void displayTime(left, right)** values 00:00..99:99 Time could be hh:mm or mm:ss or ss:uu (hundreds) : as separator
- **void displayFloat(f)** values -999..0.000..9999, no overflow indication!
* 0.2.1
fix decimal point in **printFloat()**
#### special
- **void displayVULeft(val)** display used as sort VU meter, values 0..8
- **void displayVURight(val)** display used as sort VU meter, values 0..8
#### lower level workers
- **void display(uint8_t \*arr)** array of 4 bytes to control one 7seg display
- **void display(uint8_t \*arr, uint8_t pt)** idem + pt = position of the digit with point (0..3)
- **void displayColon(on)** 0 = off, all values other are on.
- **void displayRaw(uint8_t \*arr, colon)** array of 4 bytes to control one 7seg display + colon flag
#### debugging
- **void displayTest(uint8_t del)** debugging / test function
- **void dumpSerial(uint8_t \*arr, uint8_t pt)** debugging equivalent of display.
## Operation

View File

@ -1,5 +1,5 @@
//
// FILE: 4x7segmentI2C.ino
// FILE: demo1.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
@ -10,9 +10,7 @@
HT16K33 seg(0x70);
uint32_t start;
uint32_t stop;
uint32_t start, stop;
void setup()
{
@ -43,14 +41,17 @@ void loop()
Serial.println("dim()");
for (int i = 0; i < 16; i++)
{
seg.displayHex(0xABC0 + i);
seg.brightness(i);
delay(500);
}
for (int i = 15; i > 0; i--)
for (int i = 15; i >= 0; i--)
{
seg.displayHex(0xABC0 + i);
seg.brightness(i);
delay(500);
}
delay(1000);
seg.brightness(2);
Serial.println("displayClear()");
@ -67,6 +68,26 @@ void loop()
delay(500);
}
Serial.println("displayDate()");
uint8_t dpm[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 32, 30, 31 };
for (uint8_t mo = 0; mo < 12; mo++)
{
for (uint8_t da = 1; da <= dpm[mo]; da++)
{
seg.displayDate(mo + 1, da);
delay(200);
}
}
seg.displayTime(13, 25);
for (int i = 50; i < 60; i++)
{
seg.displayTime(13, i);
delay(500);
seg.displayColon(true);
delay(500);
}
Serial.println("float");
seg.displayClear();
delay(500);
@ -103,6 +124,7 @@ void loop()
Serial.println("blink()");
for (uint8_t i = 0; i < 3; i++)
{
seg.displayHex(0xABC0 + i);
seg.blink(i);
delay(4000);
}
@ -110,7 +132,7 @@ void loop()
Serial.print("INT TEST:\t");
start = millis();
for (uint16_t counter = 0; counter < 9999; counter++)
for (int16_t counter = -999; counter < 9999; counter++)
{
seg.displayInt(counter);
}
@ -126,6 +148,27 @@ void loop()
}
stop = millis();
Serial.println(stop - start);
// Serial.print("SUPRESS ZERO TEST:\t");
// for (uint8_t nlz = 0; nlz < 5; nlz++)
// {
// Serial.print(nlz);
// seg.suppressLeadingZeroPlaces(nlz);
// seg.displayInt(0);
// delay(1000);
// }
// seg.suppressLeadingZeroPlaces(0);
Serial.print("SET DIGITS TEST:\t");
for (uint8_t dig = 0; dig < 5; dig++)
{
Serial.print(dig);
seg.setDigits(dig);
seg.displayInt(0);
delay(1000);
}
seg.setDigits(1);
}
// -- END OF FILE --

View File

@ -0,0 +1,136 @@
//
// FILE: demo_VU.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
// connect potmeter or so to A0 and A1 for the VU tests
#include "HT16K33.h"
HT16K33 seg(0x70);
uint32_t start, stop;
uint8_t ar[4];
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
seg.begin();
Wire.setClock(100000);
seg.displayOn();
seg.setDigits(4);
Serial.println("displayTest()");
seg.displayTest(10);
seg.displayOff();
delay(1000);
seg.displayOn();
seg.displayColon(false);
}
void loop()
{
test_VULeft();
delay(1000);
test_VURight();
delay(1000);
test_VUStereo();
delay(1000);
}
void test_VULeft()
{
for (uint8_t run = 0; run < 50; run++)
{
int val = abs((sin(run * PI/ 50)) * 8);
seg.displayVULeft(val);
delay(100);
}
}
void test_VURight()
{
for (uint8_t run = 0; run < 50; run++)
{
int val = analogRead(A0);
val = val / 120; // 0..8
seg.displayVURight(val);
delay(100);
}
}
void test_VUStereo()
{
for (uint8_t run = 0; run < 50; run++)
{
uint8_t left = analogRead(A0) / 240; // 0..4
uint8_t right = analogRead(A1) / 240; // 0..4
displayVUStereo(left, right);
delay(100);
}
}
void displayVUStereo(uint8_t left, uint8_t right)
{
switch (left)
{
case 0:
ar[0] = 0x00;
ar[1] = 0x00;
break;
case 1:
ar[0] = 0x00;
ar[1] = 0x06;
break;
case 2:
ar[0] = 0x00;
ar[1] = 0x36;
break;
case 3:
ar[0] = 0x06;
ar[1] = 0x36;
break;
case 4:
default:
ar[0] = 0x36;
ar[1] = 0x36;
break;
}
switch (right)
{
case 0:
ar[2] = 0x00;
ar[3] = 0x00;
break;
case 1:
ar[2] = 0x30;
ar[3] = 0x00;
break;
case 2:
ar[2] = 0x36;
ar[3] = 0x00;
break;
case 3:
ar[2] = 0x36;
ar[3] = 0x30;
break;
case 4:
default:
ar[2] = 0x36;
ar[3] = 0x36;
break;
}
seg.displayRaw(ar);
// sort of heartbeat
static bool hb = false;
seg.displayColon(hb);
hb = !hb;
}
// -- END OF FILE --

View File

@ -0,0 +1,73 @@
//
// FILE: demo_cache.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// PURPOSE: demo
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
#include "HT16K33.h"
HT16K33 seg(0x70);
uint32_t start, stop, d1, d2;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
seg.begin();
Wire.setClock(100000);
seg.displayOn();
seg.brightness(2);
seg.displayClear();
seg.blink(0);
}
void loop()
{
// Note: UNO fails for speed above 850K
// UNO 750K and 800K are using same clock divider
Serial.println("\nSPEED \tNOCACHE \tms/call \tCACHE\t\tms/call \tRATIO");
for (uint32_t sp = 100000; sp < 900000; sp += 50000)
{
test_cache(sp);
}
Serial.println();
}
void test_cache(uint32_t speed)
{
Wire.setClock(speed);
Serial.print(speed);
Serial.print("\t");
seg.cacheOff();
start = millis();
for (int16_t counter = -999; counter < 10000; counter+=10)
{
seg.displayInt(counter);
}
d1 = millis() - start;
Serial.print(d1);
Serial.print("\t\t");
Serial.print(d1 / 1100.0, 3);
Serial.print("\t\t");
delay(100);
seg.cacheOn();
start = millis();
for (int16_t counter = -999; counter < 10000; counter+=10)
{
seg.displayInt(counter);
}
d2 = millis() - start;
Serial.print(d2);
Serial.print("\t\t");
Serial.print(d2 / 1100.0, 3);
Serial.print("\t\t");
Serial.println(1.0 * d1 / d2, 3);
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,78 @@
//
// FILE: demo_displayInt.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
#include "HT16K33.h"
HT16K33 seg(0x70);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
seg.begin();
Wire.setClock(100000);
seg.displayOn();
seg.brightness(2);
seg.displayClear();
seg.blink(0);
}
void loop()
{
Serial.print("INT TEST 0:\t");
for (int16_t d = 4; d >= 0; d--)
{
seg.setDigits(d);
seg.displayInt(0);
delay(1000);
seg.setDigits(d);
seg.displayInt(8);
delay(1000);
seg.setDigits(d);
seg.displayInt(-8);
delay(1000);
}
for (int16_t d = 0; d <= 4; d++)
{
seg.setDigits(d);
seg.displayInt(0);
delay(1000);
seg.setDigits(d);
seg.displayInt(8);
delay(1000);
seg.setDigits(d);
seg.displayInt(-8);
delay(1000);
}
Serial.println();
delay(1000);
Serial.print("INT TEST 1:\t");
seg.setDigits(1);
for (int16_t counter = -200; counter < 1001; counter += 7)
{
seg.displayInt(counter);
delay(100);
}
Serial.println();
delay(1000);
Serial.print("INT TEST 2:\t");
seg.setDigits(4);
for (int16_t counter = -200; counter < 1001; counter += 7)
{
seg.displayInt(counter);
delay(100);
}
Serial.println();
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,82 @@
//
// FILE: demo_displayRaw.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
#include "HT16K33.h"
HT16K33 seg(0x70);
uint32_t start, stop, d1, d2;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
seg.begin();
Wire.setClock(100000);
seg.displayOn();
seg.brightness(2);
seg.displayClear();
seg.blink(0);
seg.cacheOff();
start = millis();
seg.displayTest(0);
Serial.println(millis() - start);
seg.cacheOn();
start = millis();
seg.displayTest(0);
Serial.println(millis() - start);
}
void loop()
{
uint8_t x[4] = { 255, 255, 255, 255 };
seg.displayClear();
delay(1000);
seg.displayRaw(x);
delay(1000);
seg.displayRaw(x, true);
delay(1000);
seg.displayRaw(x, false);
delay(1000);
test_elsa(); // limited text possible
delay(1000);
test_random();
delay(1000);
}
void test_elsa()
{
uint8_t ar[4];
ar[0] = 0x79;
ar[1] = 0x38;
ar[2] = 0x6D;
ar[3] = 0x77;
seg.displayRaw(ar);
}
void test_random()
{
uint8_t ar[4];
for (uint8_t run = 0; run < 50; run++)
{
for (uint8_t i = 0; i < 4; i++)
{
ar[i] = random(256);
}
seg.displayRaw(ar, random(2));
delay(100);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,42 @@
//
// FILE: demo_displayTime.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: demo
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
// sketch will work correctly up to 99:99
#include "HT16K33.h"
HT16K33 seg(0x70);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
seg.begin();
Wire.setClock(100000);
seg.displayOn();
seg.setDigits(4);
}
void loop()
{
static uint32_t last = 0;
uint32_t now = millis();
if (now != last)
{
last = now;
uint32_t s = now / 1000;
uint32_t t = (now - s * 1000) / 10;
s = s % 100;
seg.displayTime(s, t);
seg.displayColon(1);
}
}
// -- END OF FILE --

View File

@ -1,15 +1,15 @@
//
// FILE: demo_dual1.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// VERSION: 0.2.0
// PURPOSE: demo 2 I2C 4x7segment displays one uint32_t unsigned long
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
#include "HT16K33.h"
HT16K33 left(0x70);
HT16K33 right(0x71);
HT16K33 left(0x71);
HT16K33 right(0x70);
uint32_t counter = 0;
@ -20,28 +20,35 @@ void setup()
left.begin();
right.begin();
Wire.setClock(100000);
left.displayOn();
right.displayOn();
left.suppressLeadingZeroPlaces(4); // show no digit if not needed
right.suppressLeadingZeroPlaces(3); // show at least 1 digit if zero (0)
Serial.println("dual displayTest");
}
void loop()
{
uint16_t lval = counter / 10000;
uint16_t rval = counter % 10000;
right.suppressLeadingZeroPlaces(lval == 0 ? 3 : 0);
left.displayInt(lval);
right.displayInt(rval);
display_ulong(counter);
delay(1);
delay(1);
counter++;
}
// -- END OF FILE --
void display_ulong(uint32_t value)
{
uint16_t lval = value / 10000;
uint16_t rval = value % 10000;
// left show no digit if not needed
left.setDigits(0);
// right show at least 1 digit if value < 10000, otherwise leading zero's needed
right.setDigits(lval > 0 ? 4 : 0);
left.displayInt(lval);
right.displayInt(rval);
}
// -- END OF FILE --

View File

@ -1,7 +1,7 @@
//
// FILE: test_printfloat.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// VERSION: 0.2.0
// PURPOSE: test decimal point for floats.
// URL: http://www.adafruit.com/products/1002
// URL: https://github.com/RobTillaart/HT16K33
@ -30,6 +30,12 @@ void loop()
void test_printfloat()
{
for (int i = -2000; i < 2000; i++)
{
float f = i * 0.001;
seg.displayFloat(f);
delay(10);
}
for (int i = 9990; i < 10005; i++)
{
float f = i * 0.001;

View File

@ -7,15 +7,22 @@ HT16K33 KEYWORD1
HT16K33 KEYWORD2
begin KEYWORD2
reset KEYWORD2
clearCache KEYWORD2
cacheOn KEYWORD2
cacheOff KEYWORD2
displayOn KEYWORD2
displayOff KEYWORD2
brightness KEYWORD2
blink KEYWORD2
setDigits KEYWORD2
suppressLeadingZeroPlaces KEYWORD2
displayClear KEYWORD2
displayInt KEYWORD2
displayHex KEYWORD2
displayDate KEYWORD2
displayTime KEYWORD2
displayFloat KEYWORD2
display KEYWORD2

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/HT16K33.git"
},
"version":"0.2.1",
"version":"0.3.0",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -1,5 +1,5 @@
name=HT16K33
version=0.2.1
version=0.3.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino Library for HT16K33

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2012-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,12 +1,10 @@
//
// FILE: Histogram.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.6
// VERSION: 0.2.0
// PURPOSE: Histogram library for Arduino
// DATE: 2012-11-10
//
// Released to the public domain
//
// HISTORY:
// 0.1.0 - 2012-11-10 initial version
// 0.1.1 - 2012-11-10 added PMF() and CDF()
@ -15,9 +13,7 @@
// 0.1.4 - 2015-03-06 stricter interface
// 0.1.5 - 2017-07-16 refactor, support for > 256 buckets; prevent alloc errors
// 0.1.6 - 2017-07-27 revert double to float (issue #33)
//
// Released to the public domain
//
// 0.2.0 2020-06-12 #pragma once, removed pre 1.0 support
#include "histogram.h"
@ -140,4 +136,4 @@ int16_t Histogram::find(const float val)
// return i;
}
// END OF FILE
// -- END OF FILE --

View File

@ -1,26 +1,15 @@
#ifndef Histogram_h
#define Histogram_h
#pragma once
//
// FILE: Histogram.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.5
// VERSION: 0.2.0
// PURPOSE: Histogram library for Arduino
// DATE: 2012-11-10
//
// Released to the public domain
//
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define HISTOGRAM_LIB_VERSION "0.1.6"
#define HISTOGRAM_LIB_VERSION "0.2.0"
class Histogram
{
@ -53,5 +42,4 @@ protected:
uint32_t _cnt;
};
#endif
// END OF FILE
// -- END OF FILE --

View File

@ -1,15 +1,9 @@
#######################################
# Syntax Coloring Map For Histogram
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Histogram KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
clear KEYWORD2
add KEYWORD2
sub KEYWORD2
@ -22,7 +16,5 @@ CDF KEYWORD2
VAL KEYWORD2
find KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
HISTOGRAM_LIB_VERSION LITERAL1

View File

@ -1,7 +1,7 @@
{
"name": "Histogram",
"keywords": "Histogram,VAL,CDF,PMF,frequency",
"description": "Library for creating histogram math.",
"description": "Arduino library for creating histograms math.",
"authors":
[
{
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/Histogram.git"
},
"version":"0.1.6",
"version":"0.2.0",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/Histogram"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=Histogram
version=0.1.6
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library for creating histogram math.
sentence=Arduino library for creating histograms math.
paragraph=
category=Data Processing
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/Histogram
architectures=*
includes=histogram.h
depends=

View File

@ -1,5 +1,7 @@
# Histogram Library
Arduino library for creating histograms math.
## Description
One of the main applications for the Arduino board is reading and logging of sensor data.
@ -28,7 +30,6 @@ The interface consists of:
* float PMF(float val); // Probability Mass Function
* float CDF(float val); // Cumulative Distribution Function
* float VAL(float prob); // Value
(:sourceend:)
When the class is initialized an array of the boundaries to define the borders of the
buckets is passed to the constructor. This array should be declared global as the
@ -42,8 +43,8 @@ and the buckets counter is increased.
The sub() function is used to decrease the count of a bucket and it can cause the count
to become below zero. ALthough seldom used but still depending on the application it can
be useful. E.g. when you want to compare two value generating streams, you let one stream
add() and the other sub(). If the histogram is similar they should cancel each other out
(more or less), and the count of all the buckets should be around 0. [not tried].
add() and the other sub(). If the histogram of both streams is similar they should cancel
each other out (more or less), and the value of all buckets should be around 0. [not tried].
Frequency() may be removed to reduce footprint as it can be calculated quite easily with
the formula (1.0* bucket(i))/count().

View File

@ -1,8 +1,9 @@
//
// FILE: I2C_eeprom.cpp
// AUTHOR: Rob Tillaart
// VERSION: 1.2.7
// PURPOSE: I2C_eeprom library for Arduino with EEPROM 24LC256 et al.
// VERSION: 1.3.0
// PURPOSE: Arduino Library for external I2C EEPROM 24LC256 et al.
// URL: https://github.com/RobTillaart/I2C_EEPROM.git
//
// HISTORY:
// 0.1.00 - 2011-01-21 initial version
@ -24,24 +25,15 @@
// 1.2.01 - 2014-05-21 Refactoring
// 1.2.02 - 2015-03-06 stricter interface
// 1.2.03 - 2015-05-15 bugfix in _pageBlock & example (thanks ifreislich )
// 1.2.4 - 2017-04-19 remove timeout - issue #63
// 1.2.5 - 2017-04-20 refactor the removed timeout (Thanks to Koepel)
// 1.2.6 - 2019-02-01 fix issue #121
// 1.2.7 - 2019-09-03 fix issue #113 and #128
//
// Released to the public domain
// 1.2.4 2017-04-19 remove timeout - issue #63
// 1.2.5 2017-04-20 refactor the removed timeout (Thanks to Koepel)
// 1.2.6 2019-02-01 fix issue #121
// 1.2.7 2019-09-03 fix issue #113 and #128
// 1.3.0 2020-06-19 refactor; removed pre 1.0 support; added ESP32 support.
//
#include <I2C_eeprom.h>
#if defined(ARDUINO) && ARDUINO >= 100
#define WIRE_WRITE Wire.write
#define WIRE_READ Wire.read
#else
#define WIRE_WRITE Wire.send
#define WIRE_READ Wire.receive
#endif
I2C_eeprom::I2C_eeprom(const uint8_t deviceAddress)
{
@ -71,113 +63,108 @@ I2C_eeprom::I2C_eeprom(const uint8_t deviceAddress, const unsigned int deviceSiz
}
}
#if defined (ESP8266) || defined(ESP32)
void I2C_eeprom::begin(uint8_t sda, uint8_t scl)
{
Wire.begin(sda, scl);
_lastWrite = 0;
}
#endif
void I2C_eeprom::begin()
{
Wire.begin();
_lastWrite = 0;
// TWBR is not available on Arduino Due
#ifdef TWBR
TWBR = 72;
// 0=1000 1=888 2=800 8=500
// 12=400KHz 24=250 32=200 72=100 152=50
// F_CPU/16+(2*TWBR) // TWBR is a uint8_t
#endif
Wire.begin();
_lastWrite = 0;
}
int I2C_eeprom::writeByte(const uint16_t memoryAddress, const uint8_t data)
{
int rv = _WriteBlock(memoryAddress, &data, 1);
return rv;
int rv = _WriteBlock(memoryAddress, &data, 1);
return rv;
}
int I2C_eeprom::setBlock(const uint16_t memoryAddress, const uint8_t data, const uint16_t length)
{
uint8_t buffer[I2C_TWIBUFFERSIZE];
for (uint8_t i = 0; i < I2C_TWIBUFFERSIZE; i++) buffer[i] = data;
int rv = _pageBlock(memoryAddress, buffer, length, false);
return rv;
uint8_t buffer[I2C_TWIBUFFERSIZE];
for (uint8_t i = 0; i < I2C_TWIBUFFERSIZE; i++)
{
buffer[i] = data;
}
int rv = _pageBlock(memoryAddress, buffer, length, false);
return rv;
}
int I2C_eeprom::writeBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint16_t length)
{
int rv = _pageBlock(memoryAddress, buffer, length, true);
return rv;
int rv = _pageBlock(memoryAddress, buffer, length, true);
return rv;
}
uint8_t I2C_eeprom::readByte(const uint16_t memoryAddress)
{
uint8_t rdata;
_ReadBlock(memoryAddress, &rdata, 1);
return rdata;
uint8_t rdata;
_ReadBlock(memoryAddress, &rdata, 1);
return rdata;
}
uint16_t I2C_eeprom::readBlock(const uint16_t memoryAddress, uint8_t* buffer, const uint16_t length)
{
uint16_t addr = memoryAddress;
uint16_t len = length;
uint16_t rv = 0;
while (len > 0)
{
#if defined(ESP8266) || defined(ESP32) // || defined(...)
uint8_t cnt = _min(len, I2C_TWIBUFFERSIZE);
#else
uint8_t cnt = min(len, I2C_TWIBUFFERSIZE);
#endif
rv += _ReadBlock(addr, buffer, cnt);
addr += cnt;
buffer += cnt;
len -= cnt;
}
return rv;
uint16_t addr = memoryAddress;
uint16_t len = length;
uint16_t rv = 0;
while (len > 0)
{
uint8_t cnt = I2C_TWIBUFFERSIZE;
if (cnt > len) cnt = len;
rv += _ReadBlock(addr, buffer, cnt);
addr += cnt;
buffer += cnt;
len -= cnt;
}
return rv;
}
#ifdef I2C_EEPROM_EXTENDED
// returns 64, 32, 16, 8, 4, 2, 1, 0
// 0 is smaller than 1K
int I2C_eeprom::determineSize()
{
int rv = 0; // unknown
uint8_t orgValues[8];
uint16_t addr;
int rv = 0; // unknown
uint8_t orgValues[8];
uint16_t addr;
// try to read a byte to see if connected
rv += _ReadBlock(0x00, orgValues, 1);
if (rv == 0) return -1;
// try to read a byte to see if connected
rv += _ReadBlock(0x00, orgValues, 1);
if (rv == 0) return -1;
// remember old values, non destructive
for (uint8_t i=0; i<8; i++)
// remember old values, non destructive
for (uint8_t i = 0; i < 8; i++)
{
addr = (512 << i) + 1;
orgValues[i] = readByte(addr);
}
// scan page folding
for (uint8_t i = 0; i < 8; i++)
{
rv = i;
uint16_t addr1 = (512 << i) + 1;
uint16_t addr2 = (512 << (i+1)) + 1;
writeByte(addr1, 0xAA);
writeByte(addr2, 0x55);
if (readByte(addr1) == 0x55) // folded!
{
addr = (512 << i) + 1;
orgValues[i] = readByte(addr);
break;
}
}
// scan page folding
for (uint8_t i=0; i<8; i++)
{
rv = i;
uint16_t addr1 = (512 << i) + 1;
uint16_t addr2 = (512 << (i+1)) + 1;
writeByte(addr1, 0xAA);
writeByte(addr2, 0x55);
if (readByte(addr1) == 0x55) // folded!
{
break;
}
}
// restore original values
for (uint8_t i=0; i<8; i++)
{
uint16_t addr = (512 << i) + 1;
writeByte(addr, orgValues[i]);
}
return 0x01 << (rv-1);
// restore original values
for (uint8_t i = 0; i < 8; i++)
{
uint16_t addr = (512 << i) + 1;
writeByte(addr, orgValues[i]);
}
return 0x01 << (rv - 1);
}
#endif
////////////////////////////////////////////////////////////////////
//
@ -189,28 +176,24 @@ int I2C_eeprom::determineSize()
// returns 0 = OK otherwise error
int I2C_eeprom::_pageBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint16_t length, const bool incrBuffer)
{
uint16_t addr = memoryAddress;
uint16_t len = length;
while (len > 0)
{
uint8_t bytesUntilPageBoundary = this->_pageSize - addr % this->_pageSize;
uint16_t addr = memoryAddress;
uint16_t len = length;
while (len > 0)
{
uint8_t bytesUntilPageBoundary = this->_pageSize - addr % this->_pageSize;
#if defined(ESP8266) || defined(ESP32) // || defined(...)
uint8_t cnt = _min(len, bytesUntilPageBoundary);
cnt = _min(cnt, I2C_TWIBUFFERSIZE);
#else
uint8_t cnt = min(len, bytesUntilPageBoundary);
cnt = min(cnt, I2C_TWIBUFFERSIZE);
#endif
uint8_t cnt = I2C_TWIBUFFERSIZE;
if (cnt > len) cnt = len;
if (cnt > bytesUntilPageBoundary) cnt = bytesUntilPageBoundary;
int rv = _WriteBlock(addr, buffer, cnt);
if (rv != 0) return rv;
int rv = _WriteBlock(addr, buffer, cnt);
if (rv != 0) return rv;
addr += cnt;
if (incrBuffer) buffer += cnt;
len -= cnt;
}
return 0;
addr += cnt;
if (incrBuffer) buffer += cnt;
len -= cnt;
}
return 0;
}
// supports one and 2 bytes addresses
@ -220,61 +203,62 @@ void I2C_eeprom::_beginTransmission(const uint16_t memoryAddress)
if (this->_isAddressSizeTwoWords)
{
WIRE_WRITE((memoryAddress >> 8)); // Address High Byte
// Address High Byte
Wire.write((memoryAddress >> 8));
}
WIRE_WRITE((memoryAddress & 0xFF)); // Address Low Byte (or only byte for chips 16K or smaller that only have one-word addresses)
// Address Low Byte (or only byte for chips 16K or smaller that only have one-word addresses)
Wire.write((memoryAddress & 0xFF));
}
// pre: length <= this->_pageSize && length <= I2C_TWIBUFFERSIZE;
// returns 0 = OK otherwise error
int I2C_eeprom::_WriteBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint8_t length)
{
waitEEReady();
_waitEEReady();
this->_beginTransmission(memoryAddress);
WIRE_WRITE(buffer, length);
int rv = Wire.endTransmission();
this->_beginTransmission(memoryAddress);
Wire.write(buffer, length);
int rv = Wire.endTransmission();
_lastWrite = micros();
return rv;
_lastWrite = micros();
return rv;
}
// pre: buffer is large enough to hold length bytes
// returns bytes read
uint8_t I2C_eeprom::_ReadBlock(const uint16_t memoryAddress, uint8_t* buffer, const uint8_t length)
{
waitEEReady();
_waitEEReady();
this->_beginTransmission(memoryAddress);
this->_beginTransmission(memoryAddress);
int rv = Wire.endTransmission();
if (rv != 0) return 0; // error
int rv = Wire.endTransmission();
if (rv != 0) return 0; // error
// readbytes will always be equal or smaller to length
uint8_t readBytes = Wire.requestFrom(_deviceAddress, length);
uint8_t cnt = 0;
while (cnt < readBytes)
{
buffer[cnt++] = WIRE_READ();
}
return readBytes;
// readbytes will always be equal or smaller to length
uint8_t readBytes = Wire.requestFrom(_deviceAddress, length);
uint8_t cnt = 0;
while (cnt < readBytes)
{
buffer[cnt++] = Wire.read();
}
return readBytes;
}
void I2C_eeprom::waitEEReady()
void I2C_eeprom::_waitEEReady()
{
#define I2C_WRITEDELAY 5000
// Wait until EEPROM gives ACK again.
// this is a bit faster than the hardcoded 5 milliSeconds
while ((micros() - _lastWrite) <= I2C_WRITEDELAY)
{
Wire.beginTransmission(_deviceAddress);
int x = Wire.endTransmission();
if (x == 0) break;
yield();
}
// Wait until EEPROM gives ACK again.
// this is a bit faster than the hardcoded 5 milliSeconds
while ((micros() - _lastWrite) <= I2C_WRITEDELAY)
{
Wire.beginTransmission(_deviceAddress);
int x = Wire.endTransmission();
if (x == 0) return;
yield();
}
return;
}
// END OF FILE
// -- END OF FILE --

View File

@ -1,27 +1,18 @@
#ifndef I2C_EEPROM_H
#define I2C_EEPROM_H
#pragma once
//
// FILE: I2C_eeprom.h
// AUTHOR: Rob Tillaart
// PURPOSE: I2C_eeprom library for Arduino with EEPROM 24LC256 et al.
// VERSION: 1.2.7
// VERSION: 1.3.0
// PURPOSE: Arduino Library for external I2C EEPROM 24LC256 et al.
// URL: https://github.com/RobTillaart/I2C_EEPROM.git
//
// HISTORY: See I2C_eeprom.cpp
// URL: http://arduino.cc/playground/Main/LibraryForI2CEEPROM
//
// Released to the public domain
//
#include "Wire.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#include "Wstring.h"
#include "Wiring.h"
#endif
#define I2C_EEPROM_VERSION "1.2.7"
#define I2C_EEPROM_VERSION "1.3.0"
// The DEFAULT page size. This is overriden if you use the second constructor.
// I2C_EEPROM_PAGESIZE must be multiple of 2 e.g. 16, 32 or 64
@ -32,9 +23,6 @@
// 1 byte for eeprom register address is available in txbuffer
#define I2C_TWIBUFFERSIZE 30
// comment next line to keep lib small (idea a read only lib?)
#define I2C_EEPROM_EXTENDED
class I2C_eeprom
{
public:
@ -47,45 +35,52 @@ public:
* Initializes the EEPROM for the given device address.
*
* It will try to guess page size and address word size based on the size of the device.
*
*
* @param deviceAddress Byte address of the device.
* @param deviceSize Max size in bytes of the device (divide your device size in Kbits by 8)
*/
I2C_eeprom(const uint8_t deviceAddress, const unsigned int deviceSize);
#if defined (ESP8266) || defined(ESP32)
void begin(uint8_t sda, uint8_t scl);
#endif
void begin();
// writes a byte to memaddr
int writeByte(const uint16_t memoryAddress, const uint8_t value);
// writes length bytes from buffer to EEPROM
int writeBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint16_t length);
// set length bytes in the EEPROM to the same value.
int setBlock(const uint16_t memoryAddress, const uint8_t value, const uint16_t length);
uint8_t readByte(const uint16_t memoryAddress);
// returns the value stored in memaddr
uint8_t readByte(const uint16_t memoryAddress);
// reads length bytes into buffer
uint16_t readBlock(const uint16_t memoryAddress, uint8_t* buffer, const uint16_t length);
#ifdef I2C_EEPROM_EXTENDED
int determineSize();
#endif
int determineSize();
private:
uint8_t _deviceAddress;
uint8_t _deviceAddress;
uint32_t _lastWrite; // for waitEEReady
uint8_t _pageSize;
uint8_t _pageSize;
// for some smaller chips that use one-word addresses
bool _isAddressSizeTwoWords;
bool _isAddressSizeTwoWords;
/**
* Begins wire transmission and selects the given address to write/read.
*
*
* @param memoryAddress Address to write/read
*/
void _beginTransmission(const uint16_t memoryAddress);
void _beginTransmission(const uint16_t memoryAddress);
int _pageBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint16_t length, const bool incrBuffer);
int _WriteBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint8_t length);
uint8_t _ReadBlock(const uint16_t memoryAddress, uint8_t* buffer, const uint8_t length);
int _pageBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint16_t length, const bool incrBuffer);
int _WriteBlock(const uint16_t memoryAddress, const uint8_t* buffer, const uint8_t length);
uint8_t _ReadBlock(const uint16_t memoryAddress, uint8_t* buffer, const uint8_t length);
void waitEEReady();
void _waitEEReady();
};
#endif
// END OF FILE
// -- END OF FILE --

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2011-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,128 @@
Try this (not tested)
[code]
//#include <UTFT.h>
#include <SD.h>
#include <Wire.h>
#include <ArduCAM.h>
#include <avr/pgmspace.h>
#define SD_CS 9
//UTFT(byte model, int RS, int WR, int RD, int CS)
//UTFT myGLCD(ITDB32S,A2,A1,A0,10); // Remember to change the model parameter to suit your display module!
//ArduCAM(byte model,int RS, int WR, int RD, int REG_CS, int FIFO_CS)
ArduCAM myCAM(OV2640,A2,A1,A0,A3,10); // Remember to change the model parameter to suit your canera module!
void setup()
{
Serial.begin(115200); // <<<<<<<<<<<<<<<<<<<<<<<
Serial.println("Start arducam test");
//Initialize I2C Bus
Wire.begin();
//Switch to FIFO Mode
myCAM.write_reg(ARDUCHIP_TIM, MODE_MASK);
//Set sensor to JPEG mode. Note don't all the camera modules support JPEG mode
myCAM.OV2640_set_format(JPEG);
//Initialize Camera Module
myCAM.InitCAM();
myCAM.OV2640_set_JPEG_size(OV2640_320x240);
Serial.println("SD.begin");
//Initialize SD Card
if (!SD.begin(SD_CS))
{
Serial.println("SD card failed");
//while (1); //If failed, stop here
}
Serial.println("SD card success");
Serial.println("End setup");
}
void loop()
{
Serial.print(millis());
Serial.println("\tstart loop");
char str[8];
File outFile;
static int k = 0;
uint8_t temp,temp_last;
uint8_t start_capture = 0;
Serial.print(millis());
Serial.println("\tWait trigger from shutter buttom");
//Wait trigger from shutter buttom
if(myCAM.read_reg(ARDUCHIP_TRIG) & SHUTTER_MASK)
{
//Wait until buttom released
while(myCAM.read_reg(ARDUCHIP_TRIG) & SHUTTER_MASK);
start_capture = 1;
}
Serial.print(millis());
Serial.println("\tStart capture I");
//Start capture when detect a valid shutter press
if(start_capture)
{
//Flush the FIFO
myCAM.flush_fifo();
//Start capture
myCAM.start_capture();
}
if(myCAM.read_reg(ARDUCHIP_TRIG) & CAP_DONE_MASK)
{
//Construct a file name
k = k + 1;
itoa(k, str, 10);
strcat(str,".jpg");
//Open the new file
outFile = SD.open(str,FILE_WRITE);
if (! outFile)
{
Serial.print(millis());
Serial.println("\tfailure outfile");
return;
}
Serial.print(millis());
Serial.println("\toutfile succes");
//Enable FIFO
myCAM.enable_fifo();
uint32_t bytecounter = 0;
//Read the first dummy byte from FIFO
temp = myCAM.read_fifo();
//Read JPEG data from FIFO
while( (temp != 0xD9) | (temp_last != 0xFF) )
{
bytecounter++;
if (bytecounter % 32 == 0) Serial.print('.');
if (bytecounter % 1024 == 0) Serial.println;
temp_last = temp;
temp = myCAM.read_fifo();
//Write image data to file
outFile.write(temp);
}
Serial.println();
//Disable FIFO when all the image data is saved to the file
myCAM.disable_fifo();
//Close the file
outFile.close();
//Clear the capture done flag
myCAM.clear_fifo_flag();
//Clear the start capture flag
start_capture = 0;
}
}
[/code]

View File

@ -1,9 +1,12 @@
//
// FILE: I2C_small_eeprom_test.ino
// AUTHOR:
// VERSION: 0.1.00
// AUTHOR: Tyler Freeman
// VERSION: 0.1.1
// PURPOSE: show/test I2C_EEPROM library with small EEPROMS
//
// HISTORY
// 0.1.0 2014-05-xx initial version
// 0.1.1 2020-07-14 fix #1 compile for ESP; fix author
#include <Wire.h>
#include <I2C_eeprom.h>
@ -30,7 +33,8 @@
#define UNALIGNED_BUFFER_LEN 35
#define UNALIGNED_TEST_PAGE_ADDR (LONG_TEST_PAGE_ADDR + LONG_BUFFER_LEN + 5)
#define SERIAL_DEBUG SerialUSB
// #define SERIAL_DEBUG SerialUSB
#define SERIAL_DEBUG Serial
I2C_eeprom eeprom(DEVICEADDRESS, EE24LC01MAXBYTES);
@ -64,7 +68,7 @@ void readAndWritePage(unsigned int pageAddress, int bufferLen) {
byte testBuffer[LONG_BUFFER_LEN + 1];
// null-terminate for printing!
testBuffer[bufferLen] = NULL;
testBuffer[bufferLen] = '\0';
eeprom.readBlock(pageAddress, testBuffer, bufferLen);
@ -74,7 +78,9 @@ void readAndWritePage(unsigned int pageAddress, int bufferLen) {
for (int i = 0; i < bufferLen; i++) {
// use max to init to all AAAA's on first run.
testBuffer[i] = max('A', (testBuffer[i] + ((i % 4) + 1) % 'z'));
testBuffer[i] = 'A';
char c = (testBuffer[i] + ((i % 4) + 1) % 'z');
if (testBuffer[i] < c) testBuffer[i] = c;
}
eeprom.writeBlock(pageAddress, testBuffer, bufferLen);
@ -128,9 +134,11 @@ void setup()
SERIAL_DEBUG.println("----------------------------------------------");
readAndWritePage(UNALIGNED_TEST_PAGE_ADDR, UNALIGNED_BUFFER_LEN);
SERIAL_DEBUG.println("\nDone...");
}
void loop()
{
// Nothing to do during loop
}
}

View File

@ -1,24 +1,14 @@
#######################################
# Syntax Coloring Map For I2C_EEPROM
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
I2C_eeprom KEYWORD1
I2C_eeprom KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
readByte KEYWORD2
writeByte KEYWORD2
setBlock KEYWORD2
readBlock KEYWORD2
writeBlock KEYWORD2
determineSize KEYWORD2
readByte KEYWORD2
writeByte KEYWORD2
setBlock KEYWORD2
readBlock KEYWORD2
writeBlock KEYWORD2
determineSize KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -1,6 +1,6 @@
{
"name": "I2C_EEPROM",
"keywords": "EEPROM,24LC256",
"keywords": "EEPROM, 24LC256, 24LC64",
"description": "Library for I2C EEPROMS.",
"authors":
[
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/I2C_EEPROM.git"
},
"version":"1.2.7",
"version":"1.3.0",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/I2C_EEPROM"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=I2C_EEPROM
version=1.2.7
version=1.3.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library for I2C EEPROMS.
paragraph=24LC256 et al
category=Data Storage
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/I2C_EEPROM.git
architectures=*
includes=I2C_eeprom.h
depends=

View File

@ -0,0 +1,29 @@
# I2C_EEPROM
Arduino Library for external I2C EEPROM - 24LC256, 24LC64
## Description
Library to access external I2C EEPROM.
The interface is pretty straightforward
* readByte - read a single byte from a given address
* writeByte
* setBlock
* readBlock
* writeBlock
* determineSize
## Limitation
The library does not offer multiple EEPROMS as one
continuous storage device.
## Operational
See examples

View File

@ -1,34 +1,28 @@
#pragma once
//
// FILE: IEEE754tools.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.03
// PURPOSE: IEEE754 tools
// VERSION: 0.2.0
// PURPOSE: manipulate IEEE754 float numbers fast
// URL: https://github.com/RobTillaart/IEEE754tools.git
//
// http://playground.arduino.cc//Main/IEEE754tools
// EXPERIMENTAL ==> USE WITH CARE
// not tested extensively,
//
// Released to the public domain
// not tested, use with care
//
// 0.1.03 renamed IEEE_Sign IEEE_Exponent
// 0.1.02 added SHIFT_POW2
// 0.1.01 added IEEE_NAN, IEEE_INF tests + version string
// 0.1.00 initial version
// 0.1.00 2013-09-08 initial version
// 0.1.01 2013-09-08 added IEEE_NAN, IEEE_INF tests + version string
// 0.1.02 2013-09-08 added SHIFT_POW2
// 0.1.03 2013-09-10 renamed IEEE_Sign IEEE_Exponent
// 0.2.0 2020-06-30 own repo + some refactor...
//
#ifndef IEEE754tools_h
#define IEEE754tools_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define IEEE754_VERSION "0.1.03"
#define IEEE754_VERSION "0.2.0"
// (un)comment lines to configure functionality / size
//#define IEEE754_ENABLE_MSB // +78 bytes
#define IEEE754_ENABLE_DUMP
// IEEE754 float layout;
struct IEEEfloat
@ -74,10 +68,10 @@ union _DBLCONV
byte b[8];
};
//
// DEBUG FUNCTIONS
//
#ifdef IEEE754_ENABLE_DUMP
// print float components
void dumpFloat(float number)
{
@ -102,7 +96,6 @@ void dumpDBL(struct _DBL dbl)
Serial.print("\t");
Serial.println(dbl.m, HEX);
}
#endif
//
// mapping to/from 64bit double - best effort
@ -111,11 +104,12 @@ void dumpDBL(struct _DBL dbl)
// converts a float to a packed array of 8 bytes representing a 64 bit double
// restriction exponent and mantisse.
// float; array of 8 bytes; LSBFIRST; MSBFIRST
void float2DoublePacked(float number, byte* bar, int byteOrder=LSBFIRST)
void float2DoublePacked(float number, byte* bar, int byteOrder = LSBFIRST)
{
_FLOATCONV fl;
fl.f = number;
_DBLCONV dbl;
dbl.p.filler = 0;
dbl.p.s = fl.p.s;
dbl.p.e = fl.p.e-127 +1023; // exponent adjust
dbl.p.m = fl.p.m;
@ -124,7 +118,7 @@ void float2DoublePacked(float number, byte* bar, int byteOrder=LSBFIRST)
if (byteOrder == LSBFIRST)
{
#endif
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
bar[i] = dbl.b[i];
}
@ -132,7 +126,7 @@ void float2DoublePacked(float number, byte* bar, int byteOrder=LSBFIRST)
}
else
{
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
bar[i] = dbl.b[7-i];
}
@ -143,7 +137,7 @@ void float2DoublePacked(float number, byte* bar, int byteOrder=LSBFIRST)
// converts a packed array of bytes into a 32bit float.
// there can be an exponent overflow
// the mantisse is truncated to 23 bits.
float doublePacked2Float(byte* bar, int byteOrder=LSBFIRST)
float doublePacked2Float(byte* bar, int byteOrder = LSBFIRST)
{
_FLOATCONV fl;
_DBLCONV dbl;
@ -152,7 +146,7 @@ float doublePacked2Float(byte* bar, int byteOrder=LSBFIRST)
if (byteOrder == LSBFIRST)
{
#endif
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
dbl.b[i] = bar[i];
}
@ -160,14 +154,14 @@ float doublePacked2Float(byte* bar, int byteOrder=LSBFIRST)
}
else
{
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
dbl.b[i] = bar[7-i];
}
}
#endif
int e = dbl.p.e-1023 +127; // exponent adjust
int e = dbl.p.e - 1023 + 127; // exponent adjust
// TODO check exponent overflow.
if (e >=0 || e <= 255)
{
@ -281,7 +275,7 @@ void doublePacked2Float2(byte* bar, int byteOrder, float* value, float* error)
if (byteOrder == LSBFIRST)
{
#endif
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
dbl.b[i] = bar[i];
}
@ -289,14 +283,14 @@ void doublePacked2Float2(byte* bar, int byteOrder, float* value, float* error)
}
else
{
for (int i=0; i<8; i++)
for (int i = 0; i < 8; i++)
{
dbl.b[i] = bar[7-i];
dbl.b[i] = bar[7 - i];
}
}
#endif
int e = dbl.p.e-1023 +127; // exponent adjust
int e = dbl.p.e - 1023 + 127; // exponent adjust
// TODO check exponent overflow.
if (e >=0 || e <= 255)
{
@ -381,5 +375,5 @@ bool IEEE_EQ(float f, float g)
}
*/
#endif
// END OF FILE
// -- END OF FILE --

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,172 @@
//
// FILE: FastNegate.ino
// AUTHOR: Rob dot Tillaart at gmail dot com
// VERSION: 0.2.0
// PURPOSE: Fast negate for floating points
//
// HISTORY:
// 0.2.0 2020-06-30 main refactor
// 0.1.01 - 2013-09-08 added Gain:
// 0.1.00 - 2011-08-21 initial version
//
volatile float zz = 100;
volatile int x = 3;
uint32_t start, duration1, duration2;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println();
test_negfabs();
test_fabs();
test_negate();
test_less_zero();
test5();
}
void test_negfabs()
{
Serial.println(__FUNCTION__);
Serial.println("zz = -fabs(zz)");
start = micros();
for (int i = 0; i < 30000; i++)
{
*(((byte*) &zz) + 3) |= 0x80; // Force negative == -fabs(zz);
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.println(zz);
delay(10);
zz = 100;
start = micros();
for (int i = 0; i < 30000; i++)
{
zz = -fabs(zz);
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.println(zz);
Serial.println();
delay(10);
}
void test_fabs()
{
Serial.println(__FUNCTION__);
Serial.println("zz = fabs(zz)");
start = micros();
for (int i = 0; i < 30000; i++)
{
*(((byte*) &zz) + 3) &= 0x7F; // force positive == fabs(zz);
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.println(zz);
delay(10);
start = micros();
for (int i = 0; i < 30000; i++)
{
zz = fabs(zz);
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.println(zz);
Serial.println();
delay(10);
}
void test_negate()
{
Serial.println(__FUNCTION__);
Serial.println("zz = -zz");
start = micros();
for (int i = 0; i < 30000; i++)
{
*(((byte*) &zz) + 3) ^= 0x80;
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.println(zz);
start = micros();
for (int i = 0; i < 30000; i++)
{
zz = -zz;
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.println(zz);
Serial.println();
delay(10);
}
void test_less_zero()
{
Serial.println(__FUNCTION__);
Serial.println("if (zz < 0) ");
start = micros();
for (int i = 0; i < 30000; i++)
{
if ( *(((byte*) &zz) + 3) & 0x80) x = 2; // equals if (zz < 0);
}
duration1 = micros() - start;
Serial.println(duration1 / 30000.0);
Serial.println(zz);
start = micros();
for (int i = 0; i < 30000; i++)
{
if (zz < 0) x = 2;
}
duration2 = micros() - start;
Serial.println(duration2 / 30000.0);
Serial.print("Gain:\t");
Serial.println(1.0 * duration2 / duration1);
Serial.println(zz);
start = micros();
for (int i = 0; i < 30000; i++)
{
x = 2;
}
Serial.println((micros() - start) / 30000.0, 4);
Serial.println(zz);
Serial.println();
delay(10);
}
void test5()
{
Serial.println(__FUNCTION__);
zz = -100;
if (zz < 0) Serial.println("N");
else Serial.println("P");
zz = 100;
if (zz < 0) Serial.println("N");
else Serial.println("P");
zz = -100;
if (*(((byte*) &zz) + 3) & 0x80) Serial.println("N");
else Serial.println("P");
zz = 100;
if (*(((byte*) &zz) + 3) & 0x80) Serial.println("N");
else Serial.println("P");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,93 @@
//
// FILE: float2double.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// PURPOSE: experimental expands a float in a IEEE 754 double to be printed to PC.
//
#include <IEEE754tools.h>
byte x[8];
void setup()
{
Serial.begin(115200);
test1();
test2();
test3();
test4();
Serial.println("done");
}
void test1()
{
Serial.println();
for (float f = -50.0; f < 50.0; f += 10.0)
{
dumpFloat(f);
float2DoublePacked(f, x, LSBFIRST);
dumpByteArray(x);
float g = doublePacked2Float(x, LSBFIRST);
Serial.println(g, 10);
if (f != g) Serial.println("-- FAIL --");
Serial.println();
}
}
void test2()
{
Serial.println("\n0.15625");
dumpFloat(0.15625);
// sign = 0
// exponent = 7C
// mantissa = 0020 0000
}
void test3()
{
Serial.println("\nPI-check");
Serial.println(PI, 20);
float2DoublePacked(PI, x);
dumpByteArray(x);
float f = doublePacked2Float(x, LSBFIRST);
Serial.println(f, 20);
Serial.println();
}
void test4()
{
Serial.println("\nBIG-check");
Serial.println(1.23456789e38, 20);
dumpFloat(1.23456789e38);
float2DoublePacked(1.23456789e38, x);
dumpByteArray(x);
float f = doublePacked2Float(x, LSBFIRST);
Serial.println(f / 1e38, 20); // divide prevents ovf in output
dumpFloat(f);
Serial.println();
}
void loop()
{
}
void dumpByteArray(byte *ar)
{
for (int i = 0; i < 8; i++)
{
if (ar[i] < 0x10) Serial.print('0');
Serial.print(ar[i], HEX);
Serial.print('\t');
}
Serial.println();
}

View File

@ -1,7 +1,7 @@
{
"name": "IEEE754tools",
"keywords": "IEEE,754,float,double,algorithms",
"description": "Helper functions for IEEE 754 floating point format.",
"description": "Fast helper functions for IEEE 754 floats.",
"authors":
[
{
@ -13,12 +13,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Arduino.git"
"url": "https://github.com/RobTillaart/IEEE754tools.git"
},
"version":"0.1.3",
"version":"0.2.0",
"frameworks": "arduino",
"platforms": "*",
"export": {
"include": "libraries/IEEE754tools"
}
"platforms": "*"
}

View File

@ -1,9 +1,11 @@
name=IEEE754tools
version=0.1.3
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Helper functions for IEEE 754 floating point format.
sentence=Fast helper functions for IEEE754 floats.
paragraph=
category=Data Processing
url=https://github.com/RobTillaart/Arduino/tree/master/libraries/
architectures=*
url=https://github.com/RobTillaart/IEEE754tools
architectures=*
includes=IEEE754tools.h
depends=

View File

@ -1,4 +1,32 @@
IEEE754_tools.h contains a collection of bit-hacks to speed up a number of operations on floating pointnumbers on the Arduino.
If you don't need micro-second speedups do not use these code snippets.
in short: USE WITH CARE
# IEEE754_tools
Arduino library to manipulate IEEE754 float numbers fast.
## Description
IEEE754tools.h contains a collection of bit-hacks to speed up a number
of operations on floating point numbers on the Arduino. These bit-hacks
started in 2010 (oldest code found) or maybe even earlier.
http://en.wikipedia.org/wiki/Double_precision
http://en.wikipedia.org/wiki/Single-precision_floating-point_format
## WARNING
* If you don't need micro-second speedups **do not use** these code snippets.
* code is experimental, so use with care.
* only tested on UNO
## Future
* include https://playground.arduino.cc/Main/IEEE754tools/
* write examples
* test on ESP32
* binary transfer over serial (see playground)
## Operations
See examples

21
libraries/hmc6352/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2011-2020 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More