Add OLED library - update tiny wire libraries - add support for all PWM channels and PWM on pin 8

This commit is contained in:
Erik Tylek Kettenburg 2015-01-14 18:08:45 -08:00
parent fb93846380
commit 52f444d221
60 changed files with 3285 additions and 710 deletions

View File

@ -103,6 +103,11 @@ int digitalRead(uint8_t);
int analogRead(uint8_t);
void analogReference(uint8_t mode);
void analogWrite(uint8_t, int);
void pwmWrite(uint8_t, int);
void pwmConnect(uint8_t);
void pwmDisconnect(uint8_t);
void pwmReset(void);
unsigned long millis(void);
unsigned long micros(void);
@ -160,8 +165,8 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
#define TIMER1B 4
#define TIMER1D 5
#define CHANNELA 3; //TIMER1A
#define CHANNELB 4; //TIMER1B
#define CHANNELA 3 //TIMER1A
#define CHANNELB 4 //TIMER1B
#include "pins_arduino.h"

View File

@ -1,35 +0,0 @@
/*
Stream.h - base class for character-based streams.
Copyright (c) 2010 David A. Mellis. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef Stream_h
#define Stream_h
#include <inttypes.h>
#include "Print.h"
class Stream : public Print
{
public:
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
virtual void flush() = 0;
};
#endif

View File

@ -185,15 +185,15 @@ void analogWrite(uint8_t pin, int val)
uint8_t timer = digitalPinToTimer(pin);
/*
if( timer == TIMER0B){
// connect pwm to pin on timer 0, channel B
sbi(TCCR0A, COM0B1);
cbi(TCCR0A, COM0B0);
OCR0B = val; // set pwm duty
} else
*/
if( timer == TIMER0A){
// connect pwm to pin 8 on timer 0, channel A
sbi(TCCR0A, COM0A1);
cbi(TCCR0A, COM0A0);
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);
OCR0A = val; // set pwm duty
} else
if( timer == TIMER1A){
// connect pwm to pin on timer 1, channel A

View File

@ -0,0 +1,52 @@
CHANGES
2015-01-10 (nb)
Fixed and improved initialization. New and improved printing of text. Updated texts.
- Fixed initialization commands.
- Implemented ssd1306_init more efficiently.
- New functions ssd1306_send_command_start, ssd1306_send_command_stop.
- Improved ssd1306_setpos, more efficient.
- Reimplemented function ssd1306_char_f6x8 as improved ssd1306_char_font6x8 printing single characters only, added new ssd1306_string_font6x8 for printing strings.
- The font8x16 functions and data moved to another file until properly implemented.
- Added to the Make file ssd1306xled8x16 file.
- Updated testing text displayed on the screen.
2015-01-10 (nb)
Changed default pins for display, now PB0=SCL and PB1=SDA. Source code reformatting, improvements, edited comments.
- Changed default pins for connecting to the display for consistency with the built-in TWI interface, they are now PB0=SCL and PB1=SDA.
- Source code, removed redundant initializations of variables.
- Source code reformatting, edited comments.
2015-01-10 (nb)
Fixed some warnings, variables renamed. The font16x16/Chinese stuff moved to other folders.
- Font variable ssd1306xled_font8X16 renamed to ssd1306xled_font8x16 for consistency. TODO: rename "ssd1306xled_font8x16" file as well.
- Fixed ssd1306_draw_bmp prototype: added "const" to bitmap so it wont't generate warning.
- Files related to font 16x16 (Chinese) moved to different folders: font16x16cn.h.
- Functions and other code related to font 16x16 (Chinese) moved to different folders.
- Source code reformatting, edited comments.
2014-10-26 (gs)
Make project compile on gcc-avr - turn static images to consts, and rename include of font16x16cn.h
2014-08-13 (nb)
More files added to the repository.
2014-08-11 (nb)
Files added to the repository.

View File

@ -0,0 +1,231 @@
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
*
* Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
*
*/
// ----------------------------------------------------------------------------
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "DigisparkOLED.h"
#include "font6x8.h"
#include "font8x16.h"
// ----------------------------------------------------------------------------
// Some code based on "IIC_wtihout_ACK" by http://www.14blog.com/archives/1358
const uint8_t ssd1306_init_sequence [] PROGMEM = { // Initialization Sequence
0xAE, // Display OFF (sleep mode)
0x20, 0b00, // Set Memory Addressing Mode
// 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
// 10=Page Addressing Mode (RESET); 11=Invalid
0xB0, // Set Page Start Address for Page Addressing Mode, 0-7
0xC8, // Set COM Output Scan Direction
0x00, // ---set low column address
0x10, // ---set high column address
0x40, // --set start line address
0x81, 0x3F, // Set contrast control register
0xA1, // Set Segment Re-map. A0=address mapped; A1=address 127 mapped.
0xA6, // Set display mode. A6=Normal; A7=Inverse
0xA8, 0x3F, // Set multiplex ratio(1 to 64)
0xA4, // Output RAM to Display
// 0xA4=Output follows RAM content; 0xA5,Output ignores RAM content
0xD3, 0x00, // Set display offset. 00 = no offset
0xD5, // --set display clock divide ratio/oscillator frequency
0xF0, // --set divide ratio
0xD9, 0x22, // Set pre-charge period
0xDA, 0x12, // Set com pins hardware configuration
0xDB, // --set vcomh
0x20, // 0x20,0.77xVcc
0x8D, 0x14, // Set DC-DC enable
0xAF // Display ON in normal mode
};
uint8_t oledFont, oledX, oledY = 0;
// Program: 5248 bytes
SSD1306Device::SSD1306Device(void){}
void SSD1306Device::begin(void)
{
Wire.begin();
for (uint8_t i = 0; i < sizeof (ssd1306_init_sequence); i++) {
ssd1306_send_command(pgm_read_byte(&ssd1306_init_sequence[i]));
}
clear();
}
void SSD1306Device::setFont(uint8_t font)
{
oledFont = font;
}
void SSD1306Device::ssd1306_send_command_start(void) {
Wire.beginTransmission(SSD1306);
Wire.write(0x00); // write command
}
void SSD1306Device::ssd1306_send_command_stop(void) {
Wire.endTransmission();
}
void SSD1306Device::ssd1306_send_data_byte(uint8_t byte)
{
if(Wire.writeAvailable()){
ssd1306_send_data_stop();
ssd1306_send_data_start();
}
Wire.write(byte);
}
void SSD1306Device::ssd1306_send_command(uint8_t command)
{
ssd1306_send_command_start();
Wire.write(command);
ssd1306_send_command_stop();
}
void SSD1306Device::ssd1306_send_data_start(void)
{
Wire.beginTransmission(SSD1306);
Wire.write(0x40); //write data
}
void SSD1306Device::ssd1306_send_data_stop(void)
{
Wire.endTransmission();
}
void SSD1306Device::setCursor(uint8_t x, uint8_t y)
{
ssd1306_send_command_start();
Wire.write(0xb0 + y);
Wire.write(((x & 0xf0) >> 4) | 0x10); // | 0x10
Wire.write((x & 0x0f) | 0x01); // | 0x01
ssd1306_send_command_stop();
oledX = x;
oledY = y;
}
void SSD1306Device::clear(void)
{
fill(0x00);
}
void SSD1306Device::fill(uint8_t fill)
{
uint8_t m,n;
for (m = 0; m < 8; m++)
{
ssd1306_send_command(0xb0 + m); // page0 - page1
ssd1306_send_command(0x00); // low column start address
ssd1306_send_command(0x10); // high column start address
ssd1306_send_data_start();
for (n = 0; n < 128; n++)
{
ssd1306_send_data_byte(fill);
}
ssd1306_send_data_stop();
}
setCursor(0, 0);
}
size_t SSD1306Device::write(byte c) {
uint8_t i = 0;
uint8_t ci = c - 32;
if(c == '\r')
return 1;
if(c == '\n'){
if(oledFont == 0)
setCursor(0, oledY+1);
else
setCursor(0, oledY+2);
return 1;
}
if(oledFont == 0){
if (oledX > 122)
{
oledX = 0;
oledY++;
setCursor(oledX, oledY);
}
ssd1306_send_data_start();
for (i= 0; i < 6; i++)
{
ssd1306_send_data_byte(pgm_read_byte(&ssd1306xled_font6x8[ci * 6 + i]));
}
ssd1306_send_data_stop();
setCursor(oledX+6, oledY);
}
else{
if (oledX > 120)
{
oledX = 0;
oledY++;
setCursor(oledX, oledY);
}
ssd1306_send_data_start();
for (i = 0; i < 8; i++)
{
Wire.write(pgm_read_byte(&ssd1306xled_font8x16[ci * 16 + i]));
}
ssd1306_send_data_stop();
setCursor(oledX, oledY + 1);
ssd1306_send_data_start();
for (i = 0; i < 8; i++)
{
Wire.write(pgm_read_byte(&ssd1306xled_font8x16[ci * 16 + i + 8]));
}
ssd1306_send_data_stop();
setCursor(oledX + 8, oledY - 1);
}
return 1;
}
void SSD1306Device::bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[])
{
uint16_t j = 0;
uint8_t y, x;
if (y1 % 8 == 0) y = y1 / 8;
else y = y1 / 8 + 1;
for (y = y0; y < y1; y++)
{
setCursor(x0,y);
ssd1306_send_data_start();
for (x = x0; x < x1; x++)
{
ssd1306_send_data_byte(pgm_read_byte(&bitmap[j++]));
}
ssd1306_send_data_stop();
}
setCursor(0, 0);
}
SSD1306Device oled;
// ----------------------------------------------------------------------------

View File

@ -0,0 +1,59 @@
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
*
* Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
*
*/
#include <stdint.h>
#include <Arduino.h>
#include <Wire.h>
// #include <avr/pgmspace.h>
// #include <avr/interrupt.h>
#include <util/delay.h>
#ifndef DIGISPARKOLED_H
#define DIGISPARKOLED_H
#define FONT8X16 1
#define FONT6X8 0
// ----------------------------------------------------------------------------
#ifndef SSD1306
#define SSD1306 0x3C // Slave address
#endif
// ----------------------------------------------------------------------------
class SSD1306Device: public Print {
public:
SSD1306Device(void);
void begin(void);
void setFont(uint8_t font);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_byte(uint8_t byte);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void setCursor(uint8_t x, uint8_t y);
void fill(uint8_t fill);
void clear(void);
void bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[]);
void ssd1306_send_command_start(void);
void ssd1306_send_command_stop(void);
void ssd1306_char_f8x16(uint8_t x, uint8_t y, const char ch[]);
virtual size_t write(byte c);
using Print::write;
};
extern SSD1306Device oled;
// ----------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,11 @@
Overhauled to support Wire library, print library, and generally be more "Arduino friendly" - renamed DigiOLED to avoid confussion - 1/14/2015 by Erik Kettenburg/Digistump
Ported to Arduino CPP and working on DigiStump Pro 1/11/15 by defragster
SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
SSD1306xLED is a C library for working with the SSD1306 display driver to control dot matrix OLED/PLED 128x64 displays. It is intended to be used with the Tinusaur board but should also work with any other board based on ATtiny85 or similar microcontroller.
SSD1306xLED is written in plain C and does not require any additional libraries to function except those that come with the WinAVR SDK.

View File

@ -0,0 +1,28 @@
RESEARCH
SSD1306
http://www.solomon-systech.com/en/product/display-ic/oled-driver-controller/ssd1306/
SSD1306 is a single-chip CMOS OLED/PLED driver with controller for organic / polymer light emitting diode dot-matrix graphic display system. It consists of 128 segments and 64 commons. This IC is designed for Common Cathode type OLED panel.
---- SSD1306 Datasheets ----
https://www.adafruit.com/datasheets/SSD1306.pdf
http://www.crystalfontz.com/controllers/Solomon_Systech_SSD1306_v1.1_April_2008.pdf
---- Other SSD1306 Libraries ----
---- Projects ----
---- Other References ----

View File

@ -0,0 +1,6 @@
TODO
- Implement power saving mode

View File

@ -0,0 +1,39 @@
#include <DigisparkOLED.h>
#include <Wire.h>
// ============================================================================
#include "img0_128x64c1.h"
#include "digistump_128x64c1.h"
void setup() {
// put your setup code here, to run once:
oled.begin();
}
void loop() {
// put your main code here, to run repeatedly:
oled.fill(0xFF); //fill screen with color
delay(1000);
oled.clear(); //all black
delay(1000);
//usage: oled.setCursor(X IN PIXELS, Y IN ROWS OF 8 PIXELS STARTING WITH 0);
oled.setCursor(0, 0); //top left
oled.setFont(FONT8X16);
oled.print(F("DIGISTUMP")); //wrap strings in F() to save RAM!
oled.setFont(FONT6X8);
oled.print(F(" OLED!"));
oled.setCursor(0, 2); //two rows down because the 8x16 font takes two rows of 8
oled.println(F("test")); //println will move the cursor 8 or 16 pixels down (based on the front) and back to X=0
oled.print(F("test test test test test")); //lines auto wrap
delay(3000);
//usage oled.bitmap(START X IN PIXELS, START Y IN ROWS OF 8 PIXELS, END X IN PIXELS, END Y IN ROWS OF 8 PIXELS, IMAGE ARRAY);
oled.bitmap(0, 0, 128, 8, img0_128x64c1);
delay(3000);
oled.bitmap(0, 0, 128, 8, digistumplogo);
delay(3000);
}

View File

@ -0,0 +1,75 @@
//------------------------------------------------------------------------------
// File generated by LCD Assistant
// http://en.radzio.dxp.pl/bitmap_converter/
//------------------------------------------------------------------------------
#include <avr/pgmspace.h>
const uint8_t digistumplogo [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xE0, 0xF0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xDF, 0xFF,
0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C,
0xFC, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC,
0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xFC,
0xFC, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3F, 0xBF, 0x9F, 0xDF, 0xEF, 0xEF, 0xF7, 0xF7, 0xFB, 0xFD, 0xFD, 0xFE, 0xFF, 0x7F, 0x7F, 0x7F,
0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0xFF, 0xFF,
0xFF, 0x3F, 0x3F, 0x03, 0x03, 0x03, 0x03, 0x3F, 0x3F, 0x3F, 0x3F, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C,
0x3C, 0x3C, 0xFC, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xFC,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xFC, 0xFC,
0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x1E,
0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0xFF, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1E, 0x1E, 0x1E, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x1E, 0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x1F, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, 0x1E, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F,
0x1F, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E,
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, 0x1E,
0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F,
0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

View File

@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
// File generated by LCD Assistant
// http://en.radzio.dxp.pl/bitmap_converter/
//------------------------------------------------------------------------------
#include <avr/pgmspace.h>
// ----------------------------------------------------------------------------
const uint8_t img0_128x64c1 [] PROGMEM = {
0x00,0x03,0x05,0x09,0x11,0xFF,0x11,0x89,0x05,0xC3,0x00,0xE0,0x00,0xF0,0x00,0xF8,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x28,0xFF,0x11,0xAA,0x44,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x01,0x38,0x44,0x82,0x92,
0x92,0x74,0x01,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x44,0xFF,0x01,0x7D,
0x7D,0x7D,0x01,0x7D,0x7D,0x7D,0x7D,0x01,0x7D,0x7D,0x7D,0x7D,0x7D,0x01,0xFF,0x00,
0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3F,0x03,0x03,
0xF3,0x13,0x11,0x11,0x11,0x11,0x11,0x11,0x01,0xF1,0x11,0x61,0x81,0x01,0x01,0x01,
0x81,0x61,0x11,0xF1,0x01,0x01,0x01,0x01,0x41,0x41,0xF1,0x01,0x01,0x01,0x01,0x01,
0xC1,0x21,0x11,0x11,0x11,0x11,0x21,0xC1,0x01,0x01,0x01,0x01,0x41,0x41,0xF1,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x11,0x11,0x11,0x11,0x11,0xD3,0x33,
0x03,0x03,0x3F,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xE0,0x00,0x00,
0x7F,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x7F,0x00,0x00,0x01,0x06,0x18,0x06,
0x01,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x40,0x40,0x7F,0x40,0x40,0x00,0x00,0x00,
0x1F,0x20,0x40,0x40,0x40,0x40,0x20,0x1F,0x00,0x00,0x00,0x00,0x40,0x40,0x7F,0x40,
0x40,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x40,0x30,0x0C,0x03,0x00,0x00,
0x00,0x00,0xE0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x06,0x06,
0x06,0x06,0x04,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,0x04,0x84,0x44,0x44,0x44,
0x84,0x04,0x04,0x04,0x84,0xC4,0x04,0x04,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,
0x04,0x04,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,0x04,0x04,0x04,0x04,0x84,0x44,
0x44,0x44,0x84,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,0x04,0x04,0x04,0x06,0x06,
0x06,0x06,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x10,0x18,0x14,0x12,0x11,0x00,0x00,0x0F,0x10,0x10,0x10,
0x0F,0x00,0x00,0x00,0x10,0x1F,0x10,0x00,0x00,0x00,0x08,0x10,0x12,0x12,0x0D,0x00,
0x00,0x18,0x00,0x00,0x0D,0x12,0x12,0x12,0x0D,0x00,0x00,0x18,0x00,0x00,0x10,0x18,
0x14,0x12,0x11,0x00,0x00,0x10,0x18,0x14,0x12,0x11,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x7F,0x03,0x0C,0x30,0x0C,0x03,0x7F,0x00,0x00,0x38,0x54,0x54,0x58,0x00,0x00,
0x7C,0x04,0x04,0x78,0x00,0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xAA,0xAA,0xAA,
0x28,0x08,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x03,0x0C,0x30,0x0C,0x03,0x7F,
0x00,0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00,0x7F,0x02,0x04,0x08,0x10,0x7F,0x00,
};

View File

@ -0,0 +1,113 @@
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
*
* Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
*
*/
// ----------------------------------------------------------------------------
#include <avr/pgmspace.h>
// ----------------------------------------------------------------------------
/* Standard ASCII 6x8 font */
const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp
0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // %
0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55
0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y
0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z
0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // horiz lines
};
// ----------------------------------------------------------------------------

View File

@ -0,0 +1,116 @@
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
*
* Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
*
*/
// ----------------------------------------------------------------------------
#include <avr/pgmspace.h>
// ----------------------------------------------------------------------------
/* Standard ASCII 8x16 font */
const uint8_t ssd1306xled_font8x16 [] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0
0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00, // ! 1
0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // " 2
0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00, // # 3
0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00, // $ 4
0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00, // % 5
0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10, // & 6
0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ' 7
0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00, // ( 8
0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00, // ) 9
0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00, // * 10
0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00, // + 11
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00, // , 12
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // - 13
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00, // . 14
0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00, // / 15
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00, // 0 16
0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // 1 17
0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00, // 2 18
0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00, // 3 19
0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00, // 4 20
0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00, // 5 21
0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00, // 6 22
0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00, // 7 23
0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00, // 8 24
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00, // 9 25
0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, // : 26
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00, // ; 27
0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00, // < 28
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00, // = 29
0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00, // > 30
0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00, // ? 31
0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00, // @ 32
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20, // A 33
0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00, // B 34
0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00, // C 35
0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00, // D 36
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00, // E 37
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00, // F 38
0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00, // G 39
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20, // H 40
0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // I 41
0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00, // J 42
0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00, // K 43
0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00, // L 44
0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00, // M 45
0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00, // N 46
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00, // O 47
0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00, // P 48
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00, // Q 49
0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20, // R 50
0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00, // S 51
0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00, // T 52
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00, // U 53
0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00, // V 54
0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00, // W 55
0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20, // X 56
0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00, // Y 57
0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00, // Z 58
0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00, // [ 59
0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00, // \ 60
0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00, // ] 61
0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^ 62
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // _ 63
0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ` 64
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20, // a 65
0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00, // b 66
0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00, // c 67
0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20, // d 68
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00, // e 69
0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // f 70
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00, // g 71
0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20, // h 72
0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // i 73
0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00, // j 74
0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00, // k 75
0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // l 76
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F, // m 77
0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20, // n 78
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00, // o 79
0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00, // p 80
0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80, // q 81
0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00, // r 82
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00, // s 83
0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00, // t 84
0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20, // u 85
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00, // v 86
0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00, // w 87
0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00, // x 88
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00, // y 89
0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00, // z 90
0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40, // { 91
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00, // | 92
0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00, // } 93
0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~ 94
};
// ----------------------------------------------------------------------------

View File

@ -17,7 +17,7 @@ This sequence uses:
IMPORTANT:
=========
For this sketch, which is using <DigiUSB> library:
1) Comment "#define RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT" in "arduino-1.xx\libraries\RcSeq.h".
1) Comment "#define RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT" AND #define RC_SEQ_CONTROL_SUPPORT in "arduino-1.xx\libraries\RcSeq.h".
This will disable the code to manage incoming RC pulses and save some flash memory.
RC_SEQ_WITH_SHORT_ACTION_SUPPORT and RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT shall be defined
2) Replace #define RING_BUFFER_SIZE 128 with #define RING_BUFFER_SIZE 32 in "arduino-1.xx\libraries\DigisparkUSB\DigiUSB.h".
@ -38,17 +38,17 @@ static void ToggleLed(void); /* Declare Short Action: Toggle a LED */
#include <RcSeq.h>
#include <SoftRcPulseOut.h>
#define LED_PIN 1
#define LED_PIN 1
/*****************************************************************/
/* STEP #2: Enumeration of the servos used in the sequence */
/*****************************************************************/
enum {ROTATION_SERVO=0, UP_DOWN_SERVO , SERVO_NB};
enum {ROTATION_SERVO=0, UP_DOWN_SERVO, SERVO_NB};
/*****************************************************************/
/* STEP #3: Servos Digital Pins assignment */
/*****************************************************************/
#define UP_DOWN_SERVO_PIN 2
#define UP_DOWN_SERVO_PIN 2
/* /!\ Do not use Pin 3 (used by USB) /!\ */
/* /!\ Do not use Pin 4 (used by USB) /!\ */
#define ROTATION_SERVO_PIN 5
@ -56,12 +56,12 @@ enum {ROTATION_SERVO=0, UP_DOWN_SERVO , SERVO_NB};
/**************************************************************************************/
/* STEP #4: Declaration of the angles of the servos for the different motions (in °) */
/**************************************************************************************/
#define UP_DOWN_ON_DECK_POS 120 /* Zodiac on the deck */
#define UP_DOWN_ON_AIR_POS 180 /* Zodiac in the air */
#define UP_DOWN_ON_SEA_POS 0 /* Zodiac at sea level */
#define UP_DOWN_ON_DECK_POS 120 /* Zodiac on the deck */
#define UP_DOWN_ON_AIR_POS 180 /* Zodiac in the air */
#define UP_DOWN_ON_SEA_POS 0 /* Zodiac at sea level */
#define ROTATION_ABOVE_DECK_POS 90 /* crane at deck side */
#define ROTATION_ABOVE_SEA_POS 0 /* crane at sea side */
#define ROTATION_ABOVE_DECK_POS 90 /* crane at deck side */
#define ROTATION_ABOVE_SEA_POS 0 /* crane at sea side */
/***************************************************************************************************************************************/
@ -80,26 +80,26 @@ Order <--DECK_TO_AIR_DURATION_MS--> <--DECK_TO_SEA_ROTATION_DUR
/* STEP #6: With the help of the temporal diagram, declare start up time, the motion duration of servo and optional delay */
/**************************************************************************************************************************************************/
/* Tune below all the motion duration. Do not forget to add a trailer 'UL' for each value to force them in Unsigned Long type */
#define START_UP_DECK_TO_AIR_MS 0UL /* 0 for immediate start up, but you can put a delay here. Ex: 2000UL, will delay the startup of the whole sequence after 2 seconds */
#define DECK_TO_AIR_DURATION_MS 3000UL
#define START_UP_DECK_TO_AIR_MS 0UL /* 0 for immediate start up, but you can put a delay here. Ex: 2000UL, will delay the startup of the whole sequence after 2 seconds */
#define DECK_TO_AIR_DURATION_MS 3000UL
#define START_UP_DECK_TO_SEA_ROTATION_MS (START_UP_DECK_TO_AIR_MS + DECK_TO_AIR_DURATION_MS)
#define DECK_TO_SEA_ROTATION_DURATION_MS 3000UL
#define START_UP_DECK_TO_SEA_ROTATION_MS (START_UP_DECK_TO_AIR_MS + DECK_TO_AIR_DURATION_MS)
#define DECK_TO_SEA_ROTATION_DURATION_MS 3000UL
#define START_UP_AIR_TO_SEA_FALLING_MS (START_UP_DECK_TO_SEA_ROTATION_MS + DECK_TO_SEA_ROTATION_DURATION_MS)
#define AIR_TO_SEA_FALLING_DURATION_MS 9000UL
#define START_UP_AIR_TO_SEA_FALLING_MS (START_UP_DECK_TO_SEA_ROTATION_MS + DECK_TO_SEA_ROTATION_DURATION_MS)
#define AIR_TO_SEA_FALLING_DURATION_MS 9000UL
#define DELAY_BEFORE_RISING_UP_MS 6000UL
#define DELAY_BEFORE_RISING_UP_MS 6000UL
#define START_UP_SEA_TO_AIR_RISING_MS (START_UP_AIR_TO_SEA_FALLING_MS + AIR_TO_SEA_FALLING_DURATION_MS + DELAY_BEFORE_RISING_UP_MS)
#define SEA_TO_AIR_RISING_DURATION_MS 9000UL
#define START_UP_SEA_TO_AIR_RISING_MS (START_UP_AIR_TO_SEA_FALLING_MS + AIR_TO_SEA_FALLING_DURATION_MS + DELAY_BEFORE_RISING_UP_MS)
#define SEA_TO_AIR_RISING_DURATION_MS 9000UL
#define START_UP_SEA_TO_DECK_ROTATION_MS (START_UP_SEA_TO_AIR_RISING_MS + SEA_TO_AIR_RISING_DURATION_MS)
#define SEA_TO_DECK_ROTATION_DURATION_MS 3000UL
#define START_UP_SEA_TO_DECK_ROTATION_MS (START_UP_SEA_TO_AIR_RISING_MS + SEA_TO_AIR_RISING_DURATION_MS)
#define SEA_TO_DECK_ROTATION_DURATION_MS 3000UL
#define START_UP_AIR_TO_DECK_FALLING_MS (START_UP_SEA_TO_DECK_ROTATION_MS + SEA_TO_DECK_ROTATION_DURATION_MS)
#define AIR_TO_DECK_FALLING_DURATION_MS 3000UL
#define START_UP_AIR_TO_DECK_FALLING_MS (START_UP_SEA_TO_DECK_ROTATION_MS + SEA_TO_DECK_ROTATION_DURATION_MS)
#define AIR_TO_DECK_FALLING_DURATION_MS 3000UL
/********************************************************************************************************************/
/* STEP #7: Declare here the percentage of motion to be performed at half speed for servo start up and stop */
@ -118,7 +118,7 @@ Order <--DECK_TO_AIR_DURATION_MS--> <--DECK_TO_SEA_ROTATION_DUR
/* - Percentage of motion performed at half speed for servo start and servo stop (Soft start and Soft stop) */
/* Note: START_STOP_PER_CENT not used (MOTION_WITHOUT_SOFT_START_AND_STOP() macro used) */
/************************************************************************************************************/
SequenceSt_t ZodiacSequence[] PROGMEM = {
const SequenceSt_t ZodiacSequence[] PROGMEM = {
SHORT_ACTION_TO_PERFORM(ToggleLed, START_UP_DECK_TO_AIR_MS) /* Switch ON the Led at the beginning of the sequence */
SHORT_ACTION_TO_PERFORM(ToggleLed, START_UP_AIR_TO_DECK_FALLING_MS+AIR_TO_DECK_FALLING_DURATION_MS) /* Switch OFF the Led at the beginning of the sequence: You are not obliged to put this line at the end of the table */
/* 1) The crane lifts the pneumatic Zodiac from the deck to the air and stops */
@ -155,7 +155,7 @@ void setup()
/**************************************************************************************************************************/
/* STEP #11: declare the sequence command signal (0), the stick level (0), and the sequence to call */
/**************************************************************************************************************************/
RcSeq_DeclareCommandAndSequence(0, 0, RC_SEQUENCE(ZodiacSequence)); /* 0,0 since there's no RC command */
RcSeq_DeclareCommandAndSequence(0, 0, RC_SEQUENCE(ZodiacSequence)); /* 0, 0 since there's no RC command */
}
void loop()
@ -172,12 +172,12 @@ char RxChar;
/****************************************************************************************************************/
if(DigiUSB.available())
{
RxChar=DigiUSB.read();
if(RxChar=='g') /* Go ! */
RxChar = DigiUSB.read();
if(RxChar == 'g') /* Go ! */
{
RcSeq_LaunchSequence(ZodiacSequence);
}
if(RxChar=='t') /* Toggle LED ! */
if(RxChar == 't') /* Toggle LED ! */
{
RcSeq_LaunchShortAction(ToggleLed); /* You can toggle LED during Servo Motion! */
}
@ -187,7 +187,7 @@ char RxChar;
static void ToggleLed(void)
{
static boolean Status=LOW;
Status=!Status; /* Toggle Status */
static boolean Status = LOW;
Status = !Status; /* Toggle Status */
digitalWrite(LED_PIN, Status);
}
}

View File

@ -81,11 +81,11 @@ enum {RC_CHANNEL, RC_CHANNEL_NB}; /* Here, as there is a single channel, we coul
/* Declaration of the custom keyboard": the pulse width of the push buttons do not need to be equidistant */
enum {PUSH_BUTTON1, PUSH_BUTTON2, PUSH_BUTTON3, PUSH_BUTTON4, PUSH_BUTTON5, PUSH_BUTTON_NBR};
#define TOLERANCE 40 /* Tolerance +/- (in microseconds): CAUTION, no overlap allowed between 2 adjacent active areas . active area width = 2 x TOLERANCE (us) */
KeyMap_t CustomKeyboard[] PROGMEM ={ {CENTER_VALUE_US(1100,TOLERANCE)}, /* PUSH_BUTTON1: +/-40 us */
{CENTER_VALUE_US(1300,TOLERANCE)}, /* PUSH_BUTTON2: +/-40 us */
{CENTER_VALUE_US(1500,TOLERANCE)}, /* PUSH_BUTTON3: +/-40 us */
{CENTER_VALUE_US(1700,TOLERANCE)}, /* PUSH_BUTTON4: +/-40 us */
{CENTER_VALUE_US(1900,TOLERANCE)}, /* PUSH_BUTTON5: +/-40 us */
const KeyMap_t CustomKeyboard[] PROGMEM ={ {CENTER_VALUE_US(1100,TOLERANCE)}, /* PUSH_BUTTON1: +/-40 us */
{CENTER_VALUE_US(1300,TOLERANCE)}, /* PUSH_BUTTON2: +/-40 us */
{CENTER_VALUE_US(1500,TOLERANCE)}, /* PUSH_BUTTON3: +/-40 us */
{CENTER_VALUE_US(1700,TOLERANCE)}, /* PUSH_BUTTON4: +/-40 us */
{CENTER_VALUE_US(1900,TOLERANCE)}, /* PUSH_BUTTON5: +/-40 us */
};
//==============================================================================================

View File

@ -0,0 +1,307 @@
/*
This sketch illustrates 2 new features of the <RcSeq> library (since the V2.1 version):
1) the "control" capability: it's a function passed as argument to the RcSeq_DeclareCommandAndSequence() method.
It is used to check if a sequence can be launched or not, depending of specific condition
It is also used to inform the sequence is finished: this can be used to memorize in EEPROM the sequence id.
Like that, at the next start-up the position of the servos can be restored according to the last position of the sequence.
2) the "timeout" capability:
The RcSeq_Timeout() method can be used to check if the command signal remains constant (HIGH or LOW).
It's then possible to launch the sequence based on the static state of the command pin rather than a Rc Pulse width.
In practice, it's possible to use both manners to launch a sequence as done in the sketch below.
THE SKETCH:
==========
In this sketch, the first declared sequence opens the 2 doors with the help of 2 servos (1 per door).
The second declared sequence closes the 2 doors with the help of 2 servos (1 per door).
The 2 doors cannot open or close simultaneously with the same speed since there is a nosing secured to the right door.
This nosing forces to open and close the doors using sequences.
Opening <- -> Opening
. .
. .
. .
. .
. .
. .
. .
__ nosing -> .------. __
/ \----------------'---. '----------------/ \
\__/-------------------''-------------------\__/
Left door Right door
TOP VIEW
The opening sequence is like hereafter:
======================================
1) The servo assigned to the right door starts
2) Once rigth door slightly opened, the servo assigned to the left door starts, whilst the servo assigned to the right door resumes its travel
3) Once the 2 servos reached 90°, the 2 doors stop; the opening sequence is finished
The closing sequence is like hereafter:
======================================
1) The 2 servos assigned to the left and right doors start together but the left servo rotates more quickly than the right servo.
2) As a consequence, the left door is closed berfore the right door
3) Once the 2 servos reached 90°, the 2 doors stop; the closing sequence is finished
The sequences of this sketch can be launched either a RC channel either a regular ON/OFF switch:
===============================================================================================
A) Command from a RC channel:
------------------------- _______________
V _______________ | __ |
| __________ | ARDUINO: |________| / \ Left |
| | | | | | \__/ Servo |
| | RC |CH | UNO | |_______________|
'-+ Receiver |----| MEGA | _______________
| | | Digispark | | __ |
|__________| | Digispark pro |________| / \ Right |
|_______________| | \__/ Servo |
|_______________|
B) Command from a ON/OFF switch:
---------------------------- _______________
_______________ | __ |
| ARDUINO: |________| / \ Left |
| | | \__/ Servo |
| UNO | |_______________|
.----| MEGA | _______________
| | Digispark | | __ |
ON/OFF Switch \ | Digispark pro |________| / \ Right |
| |_______________| | \__/ Servo |
-+- |_______________|
GND
*/
/*************************************************/
/* STEP #1: Include the required libraries */
/*************************************************/
#include <RcSeq.h>
#include <TinyPinChange.h>
#include <SoftRcPulseIn.h>
#include <SoftRcPulseOut.h>
#include <EEPROM.h>
/*****************************************************************/
/* STEP #2: Enumeration of the RC Signals used in the sequence */
/*****************************************************************/
enum {RC_SIGNAL = 0, SIGNAL_NB}; /* Here, a single RC signal is used */
/******************************************************************/
/* STEP #3: Enumeration of the different position of the RC stick */
/******************************************************************/
enum {RC_PULSE_LEVEL_MINUS_1 = 0, RC_PULSE_LEVEL_PLUS_1, RC_PULSE_NB};
/*****************************************************************/
/* STEP #4: Enumeration of the servos used in the sequences */
/*****************************************************************/
enum {DOOR_SERVO_LEFT = 0, DOOR_SERVO_RIGHT, SERVO_NB}; /* In this sketch, 2 servos are declared */
/***************************************************/
/* STEP #5: Digital pin assignment for Command */
/***************************************************/
#define COMMAND_PIN 2 /* This pin can be connected to a channel of a RC Receiver or to a regular ON/OFF switch (switch wired between pin and Ground) */
/**************************************************/
/* STEP #6: Digital Pins assignment for Servos */
/**************************************************/
#define DOOR_SERVO_LEFT_PIN 3
#define DOOR_SERVO_RIGHT_PIN 4
/*************************************************************************************/
/* STEP #7: Declaration of the angle of the servos for the different motions (in °) */
/*************************************************************************************/
#define DOOR_SERVO_OPENED_LEFT_POS 135 /* position of the left Servo when left door is opened */
#define DOOR_SERVO_CLOSED_LEFT_POS 45 /* position of the left Servo when left door is closed */
#define DOOR_SERVO_OPENED_RIGHT_POS 45 /* position of the right Servo when right door is opened */
#define DOOR_SERVO_CLOSED_RIGHT_POS 135 /* position of the right Servo when right door is closed */
/***************************************************************************************************************************************/
/* STEP #8: Do a temporal diagram showing the start up and the duration of each motions of each servo */
/***************************************************************************************************************************************/
/*
1) OPENING MOTION OF THE DOORS
===========================
All the start up values (time stamp) have as reference the moment of the sequence startup order (t=0).
1.1 MOTION OF THE LEFT DOOR SERVO FOR OPENING
=========================================
Order <---OPENING_DURATION_LEFT_MS--->
|-----------------------------|--------------------------------|-->Time Axis
0 OPENING_START_LEFT_MS
1.2 MOTION OF THE RIGHT DOOR SERVO FOR OPENING
==========================================
Order <--------OPENING_DURATION_RIGHT_MS------->
|-------------------|------------------------------------------|-->Time Axis
0 OPENING_START_RIGHT_MS
2) CLOSING MOTION OF THE DOORS
===========================
All the start up values (time stamp) have as reference the moment of the sequence startup order (t=0).
2.1 MOTION OF THE LEFT DOOR SERVO FOR CLOSING
=========================================
Order <---CLOSING_DURATION_LEFT_MS--->
|-------------------|--------------------------------|------------>Time Axis
0 CLOSING_START_LEFT_MS
2.2 MOTION OF THE RIGTH DOOR SERVO FOR CLOSING
==========================================
Order <--------CLOSING_DURATION_RIGHT_MS------->
|-------------------|------------------------------------------|-->Time Axis
0 CLOSING_START_RIGHT_MS
*/
/**************************************************************************************************************************************************/
/* STEP #9: With the help of the temporal diagram, declare start up time, the motion duration of servo and optional delay */
/**************************************************************************************************************************************************/
/* Tune below all the motion duration. Do not forget to add a trailer 'UL' for each value to force them in Unsigned Long type */
#define OPENING_START_LEFT_MS 500UL //This means the left servo motion will be delayed of 500ms AFTER the order
#define OPENING_DURATION_LEFT_MS 2500UL //The left door motion ends after 500+2500=3s, as the right door
#define OPENING_START_RIGHT_MS 0UL //Immediate start
#define OPENING_DURATION_RIGHT_MS 3000UL //The right door motion ends after 3s
#define CLOSING_START_LEFT_MS 0UL //Immediate start
#define CLOSING_DURATION_LEFT_MS 3000UL //The left door will be closed BEFORE the right door
#define CLOSING_START_RIGHT_MS 0UL //Immediate start
#define CLOSING_DURATION_RIGHT_MS 4000UL //The right door will be closed AFTER the left door
/********************************************************************************************************************/
/* STEP #10: Declare here the percentage of motion to be performed at half speed for servo start up and stop */
/********************************************************************************************************************/
#define START_STOP_PER_CENT 5 /* Percentage of motion performed at half-speed for starting and stopping the servos (Soft start et Soft stop) */
/************************************************************************************************************/
/* STEP #11: Use a "const SequenceSt_t" structure table to declare the servo sequence */
/* For each table entry, arguments are: */
/* - Servo Index */
/* - Initial Servo Position in ° */
/* - Final Servo Position in ° */
/* - Motion Start Time Stamp in ms */
/* - Motion duration in ms between initial and final position */
/* - Percentage of motion performed at half speed for servo start and servo stop (Soft start and Soft stop) */
/************************************************************************************************************/
/* Table describing the motions of the 2 servos for opening the 2 doors */
const SequenceSt_t OpeningSequence[] PROGMEM = {/* Servo Id , Initial Angle , Final Angle , Delay after order , Motion Duration , Percentage at half speed */
/* 1st Servo */ MOTION_WITH_SOFT_START_AND_STOP(DOOR_SERVO_LEFT, DOOR_SERVO_CLOSED_LEFT_POS, DOOR_SERVO_OPENED_LEFT_POS, OPENING_START_LEFT_MS, OPENING_DURATION_LEFT_MS, START_STOP_PER_CENT)
/* 2nd Servo */ MOTION_WITH_SOFT_START_AND_STOP(DOOR_SERVO_RIGHT, DOOR_SERVO_CLOSED_RIGHT_POS, DOOR_SERVO_OPENED_RIGHT_POS, OPENING_START_RIGHT_MS, OPENING_DURATION_RIGHT_MS, START_STOP_PER_CENT)
};
/* Table describing the motions of the 2 servos for closing the 2 doors */
const SequenceSt_t ClosingSequence[] PROGMEM = {/* Servo Id , Initial Angle , Final Angle , Delai after order , Motion Duration , Percentage at half speed */
/* 1st Servo */ MOTION_WITH_SOFT_START_AND_STOP(DOOR_SERVO_LEFT, DOOR_SERVO_OPENED_LEFT_POS, DOOR_SERVO_CLOSED_LEFT_POS, CLOSING_START_LEFT_MS, CLOSING_DURATION_LEFT_MS, START_STOP_PER_CENT)
/* 2nd Servo */ MOTION_WITH_SOFT_START_AND_STOP(DOOR_SERVO_RIGHT, DOOR_SERVO_OPENED_RIGHT_POS, DOOR_SERVO_CLOSED_RIGHT_POS, CLOSING_START_RIGHT_MS, CLOSING_DURATION_RIGHT_MS, START_STOP_PER_CENT)
};
enum {COMMAND_OPEN = 0, COMMAND_CLOSE};
/* GLOBAL VARIABLES */
uint8_t LastExecutedSeqIdx;
void setup()
{
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny167__)
Serial.begin(9600);
Serial.print(F("RcSeq library V"));Serial.print(RcSeq_LibTextVersionRevision());Serial.println(F(" demo: advanced doors sequences"));
#endif
/***************************************************************************/
/* STEP #12: Init <RcSeq> library */
/***************************************************************************/
RcSeq_Init();
/****************************************************************************************/
/* STEP #13: declare the servo command signals with their digital pin number */
/****************************************************************************************/
RcSeq_DeclareSignal(RC_SIGNAL, COMMAND_PIN);
/******************************************************************************************/
/* STEP #14: declare a stick assigned to the RC signal having RC_PULSE_NB positions */
/******************************************************************************************/
RcSeq_DeclareStick(RC_SIGNAL, 1000, 2000, RC_PULSE_NB);
/****************************************************************************************/
/* STEP #15: declare the servo command signals with their digital pin number */
/****************************************************************************************/
RcSeq_DeclareServo(DOOR_SERVO_LEFT, DOOR_SERVO_LEFT_PIN);
RcSeq_DeclareServo(DOOR_SERVO_RIGHT, DOOR_SERVO_RIGHT_PIN);
/*******************************************************************************************************/
/* STEP #16: declare the sequence assigned to specific position of the stick assigned to the RC signal */
/*******************************************************************************************************/
RcSeq_DeclareCommandAndSequence(RC_SIGNAL, RC_PULSE_LEVEL_MINUS_1, RC_SEQUENCE(OpeningSequence), Control); // Declare a sequence triggered by a RC pulse Level Minus 1 (stick at extreme position during at least 250 ms)
RcSeq_DeclareCommandAndSequence(RC_SIGNAL, RC_PULSE_LEVEL_PLUS_1, RC_SEQUENCE(ClosingSequence), Control); // Declare a sequence triggered by a RC pulse Level Plus 1 (stick at extreme position during at least 250 ms)
/*******************************************************************************************/
/* STEP #17: Initialize the position of the servos according to the last finished sequence */
/*******************************************************************************************/
LastExecutedSeqIdx = EEPROM.read(0);
if (LastExecutedSeqIdx == COMMAND_OPEN)
{
RcSeq_ServoWrite(DOOR_SERVO_LEFT, DOOR_SERVO_OPENED_LEFT_POS);
RcSeq_ServoWrite(DOOR_SERVO_RIGHT, DOOR_SERVO_OPENED_RIGHT_POS);
}
else
{
RcSeq_ServoWrite(DOOR_SERVO_LEFT, DOOR_SERVO_CLOSED_LEFT_POS);
RcSeq_ServoWrite(DOOR_SERVO_RIGHT, DOOR_SERVO_CLOSED_RIGHT_POS);
}
}
void loop()
{
uint8_t RcSignalPinState;
/****************************************************************************************************************/
/* STEP #18: call the refresh function inside the loop() to catch RC commands and to manage the servo positions */
/****************************************************************************************************************/
RcSeq_Refresh();
/*********************************************************************************************************/
/* STEP #19: optionally, allow launching the Sequences ou Actions on Timeout (cmd with a regular switch) */
/*********************************************************************************************************/
if (RcSeq_SignalTimeout(RC_SIGNAL, 250, &RcSignalPinState))
{
/* Launch the"OpeningSequence" sequence if a LOW level is present during at least 250ms: this allows testing the sequence of servo without using a RC set, just using a regular switch */
if((LastExecutedSeqIdx == COMMAND_CLOSE) && (RcSignalPinState == LOW))
{
RcSeq_LaunchSequence(OpeningSequence);
}
/* Launch the"ClosingSequence" sequence if a HIGH level is present during at least 250ms: this allows testing the sequence of servo without using a RC set, just using a regular switch */
if((LastExecutedSeqIdx == COMMAND_OPEN) && (RcSignalPinState == HIGH))
{
RcSeq_LaunchSequence(ClosingSequence);
}
}
}
/* The Control() fonction is automatically called by the RcSeq library */
uint8_t Control(uint8_t Action, uint8_t SeqIdx)
{
uint8_t Ret = 0;
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny167__)
Serial.print("Action=");Serial.print(Action);Serial.print(" SeqIdx=");Serial.println(SeqIdx);
#endif
switch(Action)
{
case RC_SEQ_START_CONDITION: /* RcSeq asks if the conditions are met to launch the sequence SeqIdx */
/* Put here a condition to allow RcSeq launching the sequence SeqIdx (Put Ret=1 if no specific condition) */
Ret = (SeqIdx != LastExecutedSeqIdx); /* Allows RcSeq launching the sequence if the sequence to launch is different from the last one */
break;
case RC_SEQ_END_OF_SEQ: /* RcSeq informs the sequence SeqIdx is finished */
/* We memorize the last finished sequence id in EEPROM memory. Like that, at next power-up, we will know how to position the servos ( done in the Setup() ) */
EEPROM.write(0, SeqIdx);
LastExecutedSeqIdx = SeqIdx;
break;
}
return(Ret);
}

View File

@ -1,7 +1,7 @@
#include <RcSeq.h>
#include <TinyPinChange.h> /* Ne pas oublier d'inclure la librairie <TinyPinChange> qui est utilisee par la librairie <RcSeq> */
#include <SoftRcPulseIn.h> /* Ne pas oublier d'inclure la librairie <SoftRcPulseIn> qui est utilisee par la librairie <RcSeq> */
#include <SoftRcPulseOut.h> /* Ne pas oublier d'inclure la librairie <SoftRcPulseOut> qui est utilisee par la librairie <RcSeq> */
#include <TinyPinChange.h>
#include <SoftRcPulseIn.h>
#include <SoftRcPulseOut.h>
/*
IMPORTANT:
@ -25,16 +25,16 @@ enum {RC_VOIE1, RC_VOIE2, RC_VOIE3, NBR_VOIES_RC}; /* Declaration des voies */
enum {BP1, BP2, NBR_BP}; /* Declaration des Boutons-Poussoirs (On peut aller jusqu'à BP8) */
enum {POS_MINUS1, POS_PLUS1,NBR_POS}; /* Declaration des positions du Manche on peut aller de POS_MOINS2 à POS_PLUS2 (4 Positions actives Max)*/
enum {POS_MINUS1, POS_PLUS1, NBR_POS}; /* Declaration des positions du Manche on peut aller de POS_MOINS2 à POS_PLUS2 (4 Positions actives Max)*/
/* Declaration d'un clavier "Maison": les impulsions des Boutons-Poussoirs n'ont pas besoin d'etre equidistantes */
enum {BP_MAISON1, BP_MAISON2, BP_MAISON3, NBR_BP_MAISON};
#define TOLERANCE 40 /* Tolerance en + ou en - (en micro-seconde) */
KeyMap_t ClavierMaison[] PROGMEM ={ {VALEUR_CENTRALE_US(1100,TOLERANCE)}, /* BP_MAISON1: 1100 +/-40 us */
{VALEUR_CENTRALE_US(1300,TOLERANCE)}, /* BP_MAISON2: 1300 +/-40 us */
{VALEUR_CENTRALE_US(1700,TOLERANCE)}, /* BP_MAISON3: 1700 +/-40 us */
};
const KeyMap_t ClavierMaison[] PROGMEM ={ {VALEUR_CENTRALE_US(1100, TOLERANCE)}, /* BP_MAISON1: 1100 +/-40 us */
{VALEUR_CENTRALE_US(1300, TOLERANCE)}, /* BP_MAISON2: 1300 +/-40 us */
{VALEUR_CENTRALE_US(1700, TOLERANCE)}, /* BP_MAISON3: 1700 +/-40 us */
};
enum {AZIMUT=0, ELEVATION , NBR_SERVO}; /* Delaration de tous les servos, 2 dans cet exemple (On peut déclaer jusqu'à 8 servos) */
@ -80,23 +80,23 @@ enum {AZIMUT=0, ELEVATION , NBR_SERVO}; /* Delaration de tous les servos, 2 dans
#define DEM_ARRET_POUR_CENT 5 /* Pourcentage du mouvement devant etre effectue a mi-vitesse pour demarrage servo et arret servo (Soft start et Soft stop) */
/* Declaration de la table de sequence des mouvements des servo et des actions courtes */
SequenceSt_t SequenceServoEtActionCourte[] PROGMEM = {
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_MONTEE_PONT_HAUT_MS)
const SequenceSt_t SequenceServoEtActionCourte[] PROGMEM = {
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_MONTEE_PONT_HAUT_MS)
/* Montee du Zodiac du pont vers la position haute */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_PONT,ELEVATION_POS_HAUT,DEMARRAGE_MONTEE_PONT_HAUT_MS,DUREE_MONTEE_PONT_HAUT_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_PONT, ELEVATION_POS_HAUT, DEMARRAGE_MONTEE_PONT_HAUT_MS, DUREE_MONTEE_PONT_HAUT_MS, DEM_ARRET_POUR_CENT)
/* Rotation Grue du pont vers la mer */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT,AZIMUT_POS_PONT,AZIMUT_POS_MER,DEMARRAGE_ROTATION_PONT_MER_MS,DUREE_ROTATION_PONT_MER_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT, AZIMUT_POS_PONT, AZIMUT_POS_MER, DEMARRAGE_ROTATION_PONT_MER_MS, DUREE_ROTATION_PONT_MER_MS, DEM_ARRET_POUR_CENT)
/* Descente du Zodiac depuis la position haute vers la la mer */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_HAUT,ELEVATION_POS_MER,DEMARRAGE_DESCENTE_HAUT_MER_MS,DUREE_DESCENTE_HAUT_MER_MS,DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_DESCENTE_HAUT_MER_MS+DUREE_DESCENTE_HAUT_MER_MS)
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_MONTEE_MER_HAUT_MS)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_HAUT, ELEVATION_POS_MER, DEMARRAGE_DESCENTE_HAUT_MER_MS, DUREE_DESCENTE_HAUT_MER_MS, DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_DESCENTE_HAUT_MER_MS + DUREE_DESCENTE_HAUT_MER_MS)
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_MONTEE_MER_HAUT_MS)
/* Montee du Zodiac de la mer vers la position haute */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_MER,ELEVATION_POS_HAUT,DEMARRAGE_MONTEE_MER_HAUT_MS,DUREE_MONTEE_MER_HAUT_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_MER, ELEVATION_POS_HAUT, DEMARRAGE_MONTEE_MER_HAUT_MS, DUREE_MONTEE_MER_HAUT_MS, DEM_ARRET_POUR_CENT)
/* Rotation Grue de la mer vers le pont */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT,AZIMUT_POS_MER,AZIMUT_POS_PONT,DEMARRAGE_ROTATION_MER_PONT_MS,DUREE_ROTATION_MER_PONT_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT, AZIMUT_POS_MER, AZIMUT_POS_PONT, DEMARRAGE_ROTATION_MER_PONT_MS, DUREE_ROTATION_MER_PONT_MS, DEM_ARRET_POUR_CENT)
/* Descente du Zodiac de la position haute vers le pont */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_HAUT,ELEVATION_POS_PONT,DEMARRAGE_DESCENTE_HAUT_PONT_MS,DUREE_DESCENTE_HAUT_PONT_MS,DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_DESCENTE_HAUT_PONT_MS+DUREE_DESCENTE_HAUT_PONT_MS)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_HAUT, ELEVATION_POS_PONT, DEMARRAGE_DESCENTE_HAUT_PONT_MS, DUREE_DESCENTE_HAUT_PONT_MS, DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_DESCENTE_HAUT_PONT_MS + DUREE_DESCENTE_HAUT_PONT_MS)
};
#define LED 13
@ -114,22 +114,22 @@ void setup()
RcSeq_DeclareServo(AZIMUT, BROCHE_SIGNAL_SERVO_AZ);
/* Commande d'une action courte et d'une sequence de servos avec 2 BP du clavier de la VOIE1 */
RcSeq_DeclareSignal(RC_VOIE1,BROCHE_SIGNAL_RECEPTEUR_VOIE1);
RcSeq_DeclareSignal(RC_VOIE1, BROCHE_SIGNAL_RECEPTEUR_VOIE1);
RcSeq_DeclareClavier(RC_VOIE1, 1000, 2000, NBR_BP);
RcSeq_DeclareCommandeEtActionCourte(RC_VOIE1, BP1, InverseLed);
RcSeq_DeclareCommandeEtSequence(RC_VOIE1, BP2, RC_SEQUENCE(SequenceServoEtActionCourte));
RcSeq_DeclareCommandeEtSequence(RC_VOIE1, BP2, RC_SEQUENCE(SequenceServoEtActionCourte), NULL);
/* Commande d'une action courte et d'une sequence de servos avec le manche de la VOIE2 */
RcSeq_DeclareSignal(RC_VOIE2,BROCHE_SIGNAL_RECEPTEUR_VOIE2);
RcSeq_DeclareSignal(RC_VOIE2, BROCHE_SIGNAL_RECEPTEUR_VOIE2);
RcSeq_DeclareManche(RC_VOIE2, 1000, 2000, NBR_POS);
RcSeq_DeclareCommandeEtActionCourte(RC_VOIE2, POS_MINUS1, InverseLed);
RcSeq_DeclareCommandeEtSequence(RC_VOIE2, POS_PLUS1, RC_SEQUENCE(SequenceServoEtActionCourte));
RcSeq_DeclareCommandeEtSequence(RC_VOIE2, POS_PLUS1, RC_SEQUENCE(SequenceServoEtActionCourte), NULL);
/* Commande d'une action courte et d'une sequence de servos avec le clavier "maison" de la VOIE3 */
RcSeq_DeclareSignal(RC_VOIE3,BROCHE_SIGNAL_RECEPTEUR_VOIE3);
RcSeq_DeclareSignal(RC_VOIE3, BROCHE_SIGNAL_RECEPTEUR_VOIE3);
RcSeq_DeclareClavierMaison(RC_VOIE3, RC_CLAVIER_MAISON(ClavierMaison));
RcSeq_DeclareCommandeEtActionCourte(RC_VOIE3, BP_MAISON1, InverseLed);
RcSeq_DeclareCommandeEtSequence(RC_VOIE3, BP_MAISON3, RC_SEQUENCE(SequenceServoEtActionCourte));
RcSeq_DeclareCommandeEtSequence(RC_VOIE3, BP_MAISON3, RC_SEQUENCE(SequenceServoEtActionCourte), NULL);
pinMode(LED, OUTPUT);
}
@ -142,13 +142,13 @@ void loop()
/* Action associee au BP1 de la VOIE1 ou au manche position basse de la VOIE2 ou au BP_MAISON1 de la VOIE3 */
void InverseLed(void)
{
static uint32_t DebutMs=millis(); /* static, pour conserver l'etat entre 2 appels de la fonction */
static boolean Etat=HIGH; /* static, pour conserver l'etat entre 2 appels de la fonction */
static uint32_t DebutMs = millis(); /* static, pour conserver l'etat entre 2 appels de la fonction */
static boolean Etat = HIGH; /* static, pour conserver l'etat entre 2 appels de la fonction */
if(millis() - DebutMs >= 500UL) /* Depuis RcSeq V2.0, la tempo inter-commande doit etre geree dans le sketch utilisateur */
{
DebutMs=millis();
DebutMs = millis();
digitalWrite(LED, Etat);
Etat=!Etat; /* Au prochain appel de InverseLed(), l'etat de la LED sera inverse */
Etat = !Etat; /* Au prochain appel de InverseLed(), l'etat de la LED sera inverse */
}
}

View File

@ -32,9 +32,9 @@ Cette sequence utilise:
/* ETAPE N°1: Inclure les 4 librairies necessaires */
/***************************************************/
#include <RcSeq.h>
#include <TinyPinChange.h> /* Ne pas oublier d'inclure la librairie <TinyPinChange> qui est utilisee par la librairie <RcSeq> */
#include <SoftRcPulseIn.h> /* Ne pas oublier d'inclure la librairie <SoftRcPulseIn> qui est utilisee par la librairie <RcSeq> */
#include <SoftRcPulseOut.h> /* Ne pas oublier d'inclure la librairie <SoftRcPulseOut> qui est utilisee par la librairie <RcSeq> */
#include <TinyPinChange.h>
#include <SoftRcPulseIn.h>
#include <SoftRcPulseOut.h>
/*****************************************************/
/* ETAPE N°2: Enumeration des signaux de commande RC */
@ -121,23 +121,23 @@ Ordre <---DUREE_MONTEE_PONT_HAUT_MS--> <--DUREE_ROTATION_PONT_M
/* Il est possible d'inclure des actions courtes. Il suffit d'utiliser la macro ACTION_COURTE_A_EFFECTUER() en donnant le nom de la fonction a appeler et le */
/* moment ou l'action doit avoir lieu. Dans cet exemple, la LED s'allume pendant que les servos tournent et s'eteint pendant la pause de 6 secondes. */
/***************************************************************************************************************************************************************/
SequenceSt_t SequencePlus2[] PROGMEM = {
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_MONTEE_PONT_HAUT_MS)
const SequenceSt_t SequencePlus2[] PROGMEM = {
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_MONTEE_PONT_HAUT_MS)
/* Montee du Zodiac du pont vers la position haute */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_PONT,ELEVATION_POS_HAUT,DEMARRAGE_MONTEE_PONT_HAUT_MS,DUREE_MONTEE_PONT_HAUT_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_PONT, ELEVATION_POS_HAUT, DEMARRAGE_MONTEE_PONT_HAUT_MS, DUREE_MONTEE_PONT_HAUT_MS, DEM_ARRET_POUR_CENT)
/* Rotation Grue du pont vers la mer */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT,AZIMUT_POS_PONT,AZIMUT_POS_MER,DEMARRAGE_ROTATION_PONT_MER_MS,DUREE_ROTATION_PONT_MER_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT, AZIMUT_POS_PONT, AZIMUT_POS_MER, DEMARRAGE_ROTATION_PONT_MER_MS, DUREE_ROTATION_PONT_MER_MS, DEM_ARRET_POUR_CENT)
/* Descente du Zodiac depuis la position haute vers la la mer */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_HAUT,ELEVATION_POS_MER,DEMARRAGE_DESCENTE_HAUT_MER_MS,DUREE_DESCENTE_HAUT_MER_MS,DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_DESCENTE_HAUT_MER_MS+DUREE_DESCENTE_HAUT_MER_MS)
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_MONTEE_MER_HAUT_MS)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_HAUT, ELEVATION_POS_MER, DEMARRAGE_DESCENTE_HAUT_MER_MS, DUREE_DESCENTE_HAUT_MER_MS, DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_DESCENTE_HAUT_MER_MS + DUREE_DESCENTE_HAUT_MER_MS)
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_MONTEE_MER_HAUT_MS)
/* Montee du Zodiac de la mer vers la position haute */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_MER,ELEVATION_POS_HAUT,DEMARRAGE_MONTEE_MER_HAUT_MS,DUREE_MONTEE_MER_HAUT_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_MER, ELEVATION_POS_HAUT, DEMARRAGE_MONTEE_MER_HAUT_MS, DUREE_MONTEE_MER_HAUT_MS, DEM_ARRET_POUR_CENT)
/* Rotation Grue de la mer vers le pont */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT,AZIMUT_POS_MER,AZIMUT_POS_PONT,DEMARRAGE_ROTATION_MER_PONT_MS,DUREE_ROTATION_MER_PONT_MS,DEM_ARRET_POUR_CENT)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(AZIMUT, AZIMUT_POS_MER, AZIMUT_POS_PONT, DEMARRAGE_ROTATION_MER_PONT_MS, DUREE_ROTATION_MER_PONT_MS, DEM_ARRET_POUR_CENT)
/* Descente du Zodiac de la position haute vers le pont */
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION,ELEVATION_POS_HAUT,ELEVATION_POS_PONT,DEMARRAGE_DESCENTE_HAUT_PONT_MS,DUREE_DESCENTE_HAUT_PONT_MS,DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed,DEMARRAGE_DESCENTE_HAUT_PONT_MS+DUREE_DESCENTE_HAUT_PONT_MS)
MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS(ELEVATION, ELEVATION_POS_HAUT, ELEVATION_POS_PONT, DEMARRAGE_DESCENTE_HAUT_PONT_MS, DUREE_DESCENTE_HAUT_PONT_MS, DEM_ARRET_POUR_CENT)
ACTION_COURTE_A_EFFECTUER(InverseLed, DEMARRAGE_DESCENTE_HAUT_PONT_MS + DUREE_DESCENTE_HAUT_PONT_MS)
};
#define LED 13
@ -158,7 +158,7 @@ void setup()
/**************************************************************************************/
/* ETAPE N°13: declarer le(s) signal(aux) de commande RC avec leur N° de pin digitale */
/**************************************************************************************/
RcSeq_DeclareSignal(SIGNAL_RC,BROCHE_SIGNAL_RECEPTEUR);
RcSeq_DeclareSignal(SIGNAL_RC, BROCHE_SIGNAL_RECEPTEUR);
/******************************************************************************************/
/* ETAPE N°14: que le signal RC est associe a un manche qui a NBR_RC_IMPULSIONS positions */
@ -174,7 +174,7 @@ void setup()
/**************************************************************************************************************************/
/* ETAPE N°16: declarer le signal de commande de sequence, le niveau du manche, et la sequence ou action courte a appeler */
/**************************************************************************************************************************/
RcSeq_DeclareCommandeEtSequence(SIGNAL_RC, RC_IMPULSION_NIVEAU_PLUS_2, RC_SEQUENCE(SequencePlus2)); // Voici comment declarer une sequence actionnee par une impulsion Niveau Plus 2 (manche en position extreme pendant au moins 250 ms)
RcSeq_DeclareCommandeEtSequence(SIGNAL_RC, RC_IMPULSION_NIVEAU_PLUS_2, RC_SEQUENCE(SequencePlus2), NULL); // Voici comment declarer une sequence actionnee par une impulsion Niveau Plus 2 (manche en position extreme pendant au moins 250 ms)
pinMode(LED, OUTPUT);
RcSeq_DeclareCommandeEtActionCourte(SIGNAL_RC, RC_IMPULSION_NIVEAU_MOINS_1, InverseLed); // Voici comment declarer une action actionnee par une impulsion Niveau Moins 1 (manche en position mi-course pendant au moins 250 ms)

View File

@ -30,31 +30,31 @@ enum {RC_VOIE, NBR_VOIES_RC}; /* Ici, comme il n'y a qu'une voie, on aurait pu f
/* Declaration d'un clavier "Maison": les impulsions des Boutons-Poussoirs n'ont pas besoin d'etre equidistantes */
enum {BP1, BP2, BP3, BP4, BP5, BP6, BP7, BP8, NBR_BP};
#define TOLERANCE 40 /* Tolerance en + ou en - (en micro-seconde): ATTENTION, il ne doit pas y avoir recouvrement entre 2 zones actives adjascentes. Zone active = 2 x TOLERANCE (us) */
KeyMap_t ClavierMaison[] PROGMEM ={ {VALEUR_CENTRALE_US(1100,TOLERANCE)}, /* BP1: +/-40 us */
{VALEUR_CENTRALE_US(1200,TOLERANCE)}, /* BP2: +/-40 us */
{VALEUR_CENTRALE_US(1300,TOLERANCE)}, /* BP3: +/-40 us */
{VALEUR_CENTRALE_US(1400,TOLERANCE)}, /* BP4: +/-40 us */
{VALEUR_CENTRALE_US(1600,TOLERANCE)}, /* BP5: +/-40 us */
{VALEUR_CENTRALE_US(1700,TOLERANCE)}, /* BP6: +/-40 us */
{VALEUR_CENTRALE_US(1800,TOLERANCE)}, /* BP7: +/-40 us */
{VALEUR_CENTRALE_US(1900,TOLERANCE)}, /* BP8: +/-40 us */
};
const KeyMap_t ClavierMaison[] PROGMEM ={ {VALEUR_CENTRALE_US(1100,TOLERANCE)}, /* BP1: +/-40 us */
{VALEUR_CENTRALE_US(1200,TOLERANCE)}, /* BP2: +/-40 us */
{VALEUR_CENTRALE_US(1300,TOLERANCE)}, /* BP3: +/-40 us */
{VALEUR_CENTRALE_US(1400,TOLERANCE)}, /* BP4: +/-40 us */
{VALEUR_CENTRALE_US(1600,TOLERANCE)}, /* BP5: +/-40 us */
{VALEUR_CENTRALE_US(1700,TOLERANCE)}, /* BP6: +/-40 us */
{VALEUR_CENTRALE_US(1800,TOLERANCE)}, /* BP7: +/-40 us */
{VALEUR_CENTRALE_US(1900,TOLERANCE)}, /* BP8: +/-40 us */
};
//==============================================================================================
/* Astuce: une macro pour n'ecrire qu'une seule fois la fonction ActionX() */
#define DECLARE_ACTION(Idx) \
void Action##Idx(void) \
{ \
static uint32_t DebutMs=millis(); \
static boolean Etat=HIGH; \
static uint32_t DebutMs = millis(); \
static boolean Etat = HIGH; \
/* Depuis la version 2.0 de la lib <RcSeq>, pour */ \
/* des raisons de reactivite, la tempo inter-commande */ \
/* doit etre geree dans le sketch utilisateur. */ \
if(millis() - DebutMs >= 500UL) \
{ \
DebutMs=millis(); \
DebutMs = millis(); \
digitalWrite(Idx, Etat); \
Etat=!Etat; \
Etat = !Etat; \
} \
}

View File

@ -1,6 +1,6 @@
#include "RcSeq.h"
/*
English: by RC Navy (2012/2013)
English: by RC Navy (2012-2015)
=======
<RcSeq> is an asynchronous library for ATmega328P (UNO), ATtiny84 and ATtiny85 to easily create servo's sequences and/or to execute short actions from RC commands.
It can also be used to trig some short "actions" (the duration must be less than 20ms to not disturb the servo commands)
@ -21,7 +21,7 @@
CAUTION: the end user shall also use asynchronous programmation method in the loop() function (no blocking functions such as delay() or pulseIn()).
http://p.loussouarn.free.fr
Francais: par RC Navy (2012/2013)
Francais: par RC Navy (2012-2015)
========
<RcSeq> est une librairie asynchrone pour ATmega328P (UNO), ATtiny84 et ATtiny85 pour creer facilement des sequences de servos et/ou executer des actions depuis des commandes RC.
Elle peut egalement etre utilisee pour lancer des "actions courtes" (la duree doit etre inferieure a 20ms pour ne pas perturber la commande des servos)
@ -58,7 +58,7 @@
| | | \
| X O X | --> RC_IMPULSION_NIVEAU_MOINS_1 |
| | | |
| X O X | --> RC_IMPULSION_NIVEAU_MOINS_2 |
| X O X | --> RC_IMPULSION_NIVEAU_MOINS_2 |
'---------------------' /
| | | | |
| | | | | \
@ -76,6 +76,10 @@
/*************************************************************************
MACROS
*************************************************************************/
/* For an easy Library Version Management */
#define RC_SEQ_LIB_VERSION 2
#define RC_SEQ_LIB_REVISION 1
#define STR(s) #s
#define MAKE_TEXT_VER_REV(Ver,Rev) (char*)(STR(Ver)"."STR(Rev))
@ -123,12 +127,12 @@ Pos 0 1 2 3 4
1000us 2000us (Typical Pulse Width values)
*/
#define ACTIVE_AREA_STEP_NBR 3
#define INACTIVE_AREA_STEP_NBR 1
#define TOTAL_STEP_NBR(KeyNb,Type) ((Type==RC_CMD_STICK)?((KeyNb)*(ACTIVE_AREA_STEP_NBR+INACTIVE_AREA_STEP_NBR)):(((KeyNb)*(ACTIVE_AREA_STEP_NBR+INACTIVE_AREA_STEP_NBR))-1))
#define STEP(MinUs, MaxUs,KeyNb,Type) ((MaxUs-MinUs)/TOTAL_STEP_NBR(KeyNb,Type))
#define KEY_MIN_VAL(Idx,Step) ((ACTIVE_AREA_STEP_NBR+INACTIVE_AREA_STEP_NBR)*(Step)*(Idx))
#define KEY_MAX_VAL(Idx,Step) (KEY_MIN_VAL(Idx,Step)+(ACTIVE_AREA_STEP_NBR*(Step)))
#define ACTIVE_AREA_STEP_NBR 3
#define INACTIVE_AREA_STEP_NBR 1
#define TOTAL_STEP_NBR(KeyNb, Type) ((Type==RC_CMD_STICK)?((KeyNb)*(ACTIVE_AREA_STEP_NBR+INACTIVE_AREA_STEP_NBR)):(((KeyNb)*(ACTIVE_AREA_STEP_NBR+INACTIVE_AREA_STEP_NBR))-1))
#define STEP(MinUs, MaxUs, KeyNb, Type) ((MaxUs-MinUs)/TOTAL_STEP_NBR(KeyNb,Type))
#define KEY_MIN_VAL(Idx, Step) ((ACTIVE_AREA_STEP_NBR+INACTIVE_AREA_STEP_NBR)*(Step)*(Idx))
#define KEY_MAX_VAL(Idx, Step) (KEY_MIN_VAL(Idx,Step)+(ACTIVE_AREA_STEP_NBR*(Step)))
typedef struct {
int8_t InProgress;
@ -138,6 +142,9 @@ typedef struct {
void *TableOrShortAction;
uint8_t SequenceLength;
uint8_t ShortActionMap;
#ifdef RC_SEQ_CONTROL_SUPPORT
uint8_t(*Control)(uint8_t Action, uint8_t SeqIdx);
#endif
}CmdSequenceSt_t;
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
@ -147,14 +154,14 @@ typedef struct {
}PosST_t;
typedef struct {
SoftRcPulseIn Pulse;
PosST_t Pos;
uint8_t Type; /* RC_CMD_STICK or RC_CMD_KEYBOARD or RC_CMD_CUSTOM */
uint8_t PosNb;
uint16_t PulseMinUs;
uint16_t PulseMaxUs;
uint16_t StepUs;
KeyMap_t *KeyMap;
SoftRcPulseIn Pulse;
PosST_t Pos;
uint8_t Type; /* RC_CMD_STICK or RC_CMD_KEYBOARD or RC_CMD_CUSTOM */
uint8_t PosNb;
uint16_t PulseMinUs;
uint16_t PulseMaxUs;
uint16_t StepUs;
const KeyMap_t *KeyMap;
}RcCmdSt_t;
#endif
@ -175,47 +182,42 @@ static uint8_t CmdSignalNb;
static RcCmdSt_t RcChannel[RC_CMD_MAX_NB];
#endif
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
#define AsMember .
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
static ServoSt_t Servo[SERVO_MAX_NB];
#endif
static CmdSequenceSt_t CmdSequence[SEQUENCE_MAX_NB];
#else
#define AsMember ->
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
static ServoSt_t **Servo=NULL;
static ServoSt_t *Servo = NULL;
#endif
static CmdSequenceSt_t **CmdSequence=NULL;
static CmdSequenceSt_t *CmdSequence = NULL;
#endif
/*************************************************************************
PRIVATE FUNCTION PROTOTYPES
*************************************************************************/
static void ExecuteSequence(uint8_t CmdIdx, uint8_t Pos);
#ifndef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
static void LoadSequenceOrShortAction(uint8_t CmdIdx,uint8_t Pos,void *SequenceOrShortAction, uint8_t SequenceLength);
#endif
static uint8_t ExecuteSequence(uint8_t CmdIdx, uint8_t Pos);
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
static int8_t GetPos(uint8_t ChIdx,uint16_t PulseWidthUs);
static int8_t GetPos(uint8_t ChIdx, uint16_t PulseWidthUs);
#endif
//========================================================================================================================
void RcSeq_Init(void)
{
SeqNb=0;
ServoNb=0;
SeqNb = 0;
ServoNb = 0;
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
for(uint8_t ChIdx=0;ChIdx<RC_CMD_MAX_NB;ChIdx++)
for(uint8_t ChIdx = 0; ChIdx < RC_CMD_MAX_NB; ChIdx++)
{
RcChannel[ChIdx].Pos.Idx=NO_POS;
RcChannel[ChIdx].Pos.Idx = NO_POS;
}
#endif
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
for(uint8_t SeqIdx=0;SeqIdx<SEQUENCE_MAX_NB;SeqIdx++)
for(uint8_t SeqIdx = 0; SeqIdx < SEQUENCE_MAX_NB; SeqIdx++)
{
CmdSequence[SeqIdx].InProgress=0;
CmdSequence[SeqIdx].TableOrShortAction=NULL;
CmdSequence[SeqIdx].SequenceLength=0;
CmdSequence[SeqIdx].ShortActionMap=0;
CmdSequence[SeqIdx].InProgress = 0;
CmdSequence[SeqIdx].TableOrShortAction = NULL;
CmdSequence[SeqIdx].SequenceLength = 0;
CmdSequence[SeqIdx].ShortActionMap = 0;
}
#endif
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
@ -242,36 +244,53 @@ char *RcSeq_LibTextVersionRevision(void)
void RcSeq_DeclareServo(uint8_t Idx, uint8_t DigitalPin)
{
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
if(Idx<SERVO_MAX_NB)
if(Idx < SERVO_MAX_NB)
{
Servo[Idx].Motor.attach(DigitalPin);
Servo[Idx].SeqLineInProgress=NO_SEQ_LINE;
if(ServoNb<(Idx+1)) ServoNb=(Idx+1);
Servo[Idx].SeqLineInProgress = NO_SEQ_LINE;
if(ServoNb < (Idx + 1)) ServoNb = (Idx + 1);
}
#else
if(Idx<SERVO_MAX_NB)
if(Idx < SERVO_MAX_NB)
{
ServoNb++;
if(!Servo) Servo=(ServoSt_t**)malloc(sizeof(ServoSt_t));
else Servo=(ServoSt_t**)realloc(Servo, sizeof(ServoSt_t)*ServoNb);
Servo[Idx] AsMember Motor.attach(DigitalPin);
Servo[Idx] AsMember SeqLineInProgress=NO_SEQ_LINE;
if(!Servo) Servo = (ServoSt_t*)malloc(sizeof(ServoSt_t));
else Servo = (ServoSt_t*)realloc(Servo, sizeof(ServoSt_t) * ServoNb);
Servo[Idx].Motor.attach(DigitalPin);
Servo[Idx].SeqLineInProgress = NO_SEQ_LINE;
}
#endif
}
//========================================================================================================================
void RcSeq_ServoWrite(uint8_t Idx, uint16_t Angle)
{
if(Idx < SERVO_MAX_NB)
{
Servo[Idx].Motor.write(Angle);
}
}
#endif
//========================================================================================================================
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
void RcSeq_DeclareSignal(uint8_t Idx, uint8_t DigitalPin)
{
if(Idx<RC_CMD_MAX_NB)
if(Idx < RC_CMD_MAX_NB)
{
RcChannel[Idx].Pulse.attach(DigitalPin);
CmdSignalNb++;
}
}
//========================================================================================================================
void RcSeq_DeclareKeyboardOrStickOrCustom(uint8_t ChIdx, uint8_t Type, uint16_t PulseMinUs, uint16_t PulseMaxUs, KeyMap_t *KeyMap, uint8_t PosNb)
boolean RcSeq_SignalTimeout(uint8_t Idx, uint8_t TimeoutMs, uint8_t *State)
{
if(Idx < RC_CMD_MAX_NB)
{
return(RcChannel[Idx].Pulse.timeout(TimeoutMs, State));
}
return(0);
}
//========================================================================================================================
void RcSeq_DeclareKeyboardOrStickOrCustom(uint8_t ChIdx, uint8_t Type, uint16_t PulseMinUs, uint16_t PulseMaxUs, const KeyMap_t *KeyMap, uint8_t PosNb)
{
RcChannel[ChIdx].Type = Type;
RcChannel[ChIdx].PosNb = PosNb;
@ -281,50 +300,62 @@ void RcSeq_DeclareKeyboardOrStickOrCustom(uint8_t ChIdx, uint8_t Type, uint16_t
RcChannel[ChIdx].KeyMap = KeyMap;
}
//========================================================================================================================
void RcSeq_DeclareCustomKeyboard(uint8_t ChIdx, KeyMap_t *KeyMapTbl, uint8_t KeyNb)
void RcSeq_DeclareCustomKeyboard(uint8_t ChIdx, const KeyMap_t *KeyMapTbl, uint8_t KeyNb)
{
RcSeq_DeclareKeyboardOrStickOrCustom(ChIdx, RC_CMD_CUSTOM, 0, 0, KeyMapTbl, KeyNb);
}
#endif
//========================================================================================================================
void RcSeq_DeclareCommandAndSequence(uint8_t CmdIdx,uint8_t Pos,SequenceSt_t *Table, uint8_t SequenceLength)
#ifdef RC_SEQ_CONTROL_SUPPORT
void RcSeq_DeclareCommandAndSequence(uint8_t CmdIdx,uint8_t Pos, const SequenceSt_t *Table, uint8_t SequenceLength, uint8_t(*Control)(uint8_t Action, uint8_t SeqIdx))
#else
void RcSeq_DeclareCommandAndSequence(uint8_t CmdIdx,uint8_t Pos, const SequenceSt_t *Table, uint8_t SequenceLength)
#endif
{
uint8_t Idx, ServoIdx;
uint16_t StartInDegrees;
uint32_t StartMinMs[SERVO_MAX_NB];
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
for(Idx=0;Idx<SEQUENCE_MAX_NB;Idx++)
#ifndef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
if(!CmdSequence) CmdSequence = (CmdSequenceSt_t*)malloc(sizeof(CmdSequenceSt_t));
else CmdSequence = (CmdSequenceSt_t*)realloc(CmdSequence, sizeof(CmdSequenceSt_t) * (SeqNb + 1));
Idx = SeqNb;
SeqNb++;
#else
for(Idx = 0; Idx < SEQUENCE_MAX_NB; Idx++)
{
if(!CmdSequence[Idx].TableOrShortAction)
{
CmdSequence[Idx].CmdIdx=CmdIdx;
CmdSequence[Idx].Pos=Pos;
CmdSequence[Idx].TableOrShortAction=(void*)Table;
CmdSequence[Idx].SequenceLength=SequenceLength;
#endif
CmdSequence[Idx].CmdIdx = CmdIdx;
CmdSequence[Idx].Pos = Pos;
CmdSequence[Idx].TableOrShortAction = (void*)Table;
CmdSequence[Idx].SequenceLength = SequenceLength;
#ifdef RC_SEQ_CONTROL_SUPPORT
CmdSequence[Idx].Control = Control;
#endif
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
SeqNb++;
break;
}
}
#else
LoadSequenceOrShortAction(CmdIdx,Pos,(void*)Table, SequenceLength);
#endif
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
/* Get initial pulse width for each Servo */
for(Idx=0;Idx<SERVO_MAX_NB;Idx++)
for(Idx = 0; Idx < SERVO_MAX_NB; Idx++)
{
StartMinMs[Idx]=0xFFFFFFFF;
StartMinMs[Idx] = 0xFFFFFFFF;
}
for(Idx=0;Idx<SequenceLength;Idx++)
for(Idx = 0; Idx < SequenceLength; Idx++)
{
ServoIdx=(int8_t)PGM_READ_8(Table[Idx].ServoIndex);
if(ServoIdx!=255)
ServoIdx = (int8_t)PGM_READ_8(Table[Idx].ServoIndex);
if(ServoIdx != 255)
{
if((uint32_t)PGM_READ_32(Table[Idx].StartMotionOffsetMs)<=StartMinMs[ServoIdx])
if((uint32_t)PGM_READ_32(Table[Idx].StartMotionOffsetMs) <= StartMinMs[ServoIdx])
{
StartMinMs[ServoIdx]=(uint32_t)PGM_READ_32(Table[Idx].StartMotionOffsetMs);
StartInDegrees=(uint16_t)PGM_READ_8(Table[Idx].StartInDegrees);
Servo[ServoIdx] AsMember Motor.write(StartInDegrees);
StartMinMs[ServoIdx] = (uint32_t)PGM_READ_32(Table[Idx].StartMotionOffsetMs);
StartInDegrees = (uint16_t)PGM_READ_8(Table[Idx].StartInDegrees);
Servo[ServoIdx].Motor.write(StartInDegrees);
}
}
}
@ -332,49 +363,42 @@ uint32_t StartMinMs[SERVO_MAX_NB];
}
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
//========================================================================================================================
void RcSeq_DeclareCommandAndShortAction(uint8_t CmdIdx,uint8_t Pos,void(*ShortAction)(void))
void RcSeq_DeclareCommandAndShortAction(uint8_t CmdIdx, uint8_t Pos, void(*ShortAction)(void))
{
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
uint8_t Idx;
for(Idx=0;Idx<SEQUENCE_MAX_NB;Idx++)
#ifndef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
if(!CmdSequence) CmdSequence = (CmdSequenceSt_t*)malloc(sizeof(CmdSequenceSt_t));
else CmdSequence = (CmdSequenceSt_t*)realloc(CmdSequence, sizeof(CmdSequenceSt_t) * (SeqNb + 1));
Idx = SeqNb;
SeqNb++;
#else
for(Idx = 0; Idx < SEQUENCE_MAX_NB; Idx++)
{
if(!CmdSequence[Idx].TableOrShortAction)
{
CmdSequence[Idx].CmdIdx=CmdIdx;
CmdSequence[Idx].Pos=Pos;
CmdSequence[Idx].TableOrShortAction=(void*)ShortAction;
#endif
CmdSequence[Idx].CmdIdx = CmdIdx;
CmdSequence[Idx].Pos = Pos;
CmdSequence[Idx].TableOrShortAction = (void*)ShortAction;
CmdSequence[Idx].SequenceLength = 0;
#ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
SeqNb++;
break;
}
}
#else
LoadSequenceOrShortAction(CmdIdx,Pos,(void*)ShortAction, 0);
#endif
}
#endif
#ifndef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
static void LoadSequenceOrShortAction(uint8_t CmdIdx,uint8_t Pos,void *SequenceOrShortAction, uint8_t SequenceLength)
{
if(!CmdSequence) CmdSequence=(CmdSequenceSt_t**)malloc(sizeof(CmdSequenceSt_t));
else CmdSequence=(CmdSequenceSt_t**)realloc(CmdSequence,sizeof(CmdSequence)+sizeof(CmdSequenceSt_t));
CmdSequence[SeqNb] AsMember CmdIdx=CmdIdx;
CmdSequence[SeqNb] AsMember Pos=Pos;
CmdSequence[SeqNb] AsMember TableOrShortAction=(void*)SequenceOrShortAction;
CmdSequence[SeqNb] AsMember SequenceLength=SequenceLength;
SeqNb++;
}
#endif
//========================================================================================================================
uint8_t RcSeq_LaunchSequence(SequenceSt_t *Table)
uint8_t RcSeq_LaunchSequence(const SequenceSt_t *Table)
{
uint8_t Idx, Ret=0;
for(Idx=0;Idx<SEQUENCE_MAX_NB;Idx++)
uint8_t Idx, Ret = 0;
for(Idx = 0; Idx < SEQUENCE_MAX_NB; Idx++)
{
if(CmdSequence[Idx] AsMember TableOrShortAction==(void*)Table)
if(CmdSequence[Idx].TableOrShortAction == (void*)Table)
{
ExecuteSequence(CmdSequence[Idx] AsMember CmdIdx,CmdSequence[Idx] AsMember Pos);
Ret=1;
Ret = ExecuteSequence(CmdSequence[Idx].CmdIdx, CmdSequence[Idx].Pos);
break;
}
}
@ -383,8 +407,8 @@ uint8_t Idx, Ret=0;
//========================================================================================================================
void RcSeq_Refresh(void)
{
static uint32_t NowMs=millis();
static uint32_t StartChronoInterPulseMs=millis();
static uint32_t NowMs = millis();
static uint32_t StartChronoInterPulseMs = millis();
SequenceSt_t *SequenceTable;
void (*ShortAction)(void);
int8_t ShortActionCnt;
@ -398,100 +422,104 @@ int8_t CmdPos; /* Shall be signed */
uint32_t RcPulseWidthUs;
/* Asynchronous RC Command acquisition */
for(ChIdx=0;ChIdx<CmdSignalNb;ChIdx++)
for(ChIdx = 0; ChIdx < CmdSignalNb; ChIdx++)
{
if(!RcChannel[ChIdx].Pulse.available()) continue; /* Channel not used or no pulse received */
RcPulseWidthUs=RcChannel[ChIdx].Pulse.width_us();
CmdPos=GetPos(ChIdx,RcPulseWidthUs);
if(CmdPos>=0)
RcPulseWidthUs = RcChannel[ChIdx].Pulse.width_us();
CmdPos = GetPos(ChIdx, RcPulseWidthUs);
// Serial.print("W=");Serial.print(RcPulseWidthUs);Serial.print(" P=");Serial.println((int)CmdPos);
if(CmdPos >= 0)
{
if(RcChannel[ChIdx].Pos.Idx!=CmdPos)
if(RcChannel[ChIdx].Pos.Idx != CmdPos)
{
RcChannel[ChIdx].Pos.Idx=CmdPos;
RcChannel[ChIdx].Pos.StartChronoMs=millis();
RcChannel[ChIdx].Pos.Idx = CmdPos;
RcChannel[ChIdx].Pos.StartChronoMs = millis();
}
else
{
if((millis()-RcChannel[ChIdx].Pos.StartChronoMs)>=((RcChannel[ChIdx].Type==RC_CMD_STICK)?STICK_PULSE_CHECK_MS:KBD_PULSE_CHECK_MS)) /* Check the Pulse is valid at least for 100 ms or 50 ms */
if((millis() - RcChannel[ChIdx].Pos.StartChronoMs) >= ((RcChannel[ChIdx].Type == RC_CMD_STICK)?STICK_PULSE_CHECK_MS:KBD_PULSE_CHECK_MS)) /* Check the Pulse is valid at least for 100 ms or 50 ms */
{
ExecuteSequence(ChIdx,CmdPos);
RcChannel[ChIdx].Pos.Idx=NO_POS;
ExecuteSequence(ChIdx, CmdPos);
RcChannel[ChIdx].Pos.Idx = NO_POS;
}
}
}
else
{
RcChannel[ChIdx].Pos.Idx=NO_POS;
RcChannel[ChIdx].Pos.Idx = NO_POS;
}
}
#endif
NowMs=millis();
NowMs = millis();
if((NowMs - StartChronoInterPulseMs) >= 20UL)
{
/* We arrive here every 20 ms */
/* Asynchronous Servo Sequence management */
for(int8_t Idx=0;Idx<SeqNb;Idx++)
for(int8_t Idx = 0; Idx < SeqNb; Idx++)
{
if(!CmdSequence[Idx] AsMember InProgress || !CmdSequence[Idx] AsMember SequenceLength) continue;
ShortActionCnt=-1;
for(int8_t SeqLine=0;SeqLine<CmdSequence[Idx] AsMember SequenceLength;SeqLine++) /* Read all lines of the sequence table: this allows to run several servos simultaneously (not forcibly one after the other) */
if(!CmdSequence[Idx].InProgress || !CmdSequence[Idx].SequenceLength) continue;
ShortActionCnt = -1;
for(int8_t SeqLine = 0; SeqLine < CmdSequence[Idx].SequenceLength; SeqLine++) /* Read all lines of the sequence table: this allows to run several servos simultaneously (not forcibly one after the other) */
{
SequenceTable=(SequenceSt_t *)CmdSequence[Idx] AsMember TableOrShortAction;
ServoIdx=(int8_t)PGM_READ_8(SequenceTable[SeqLine].ServoIndex);
SequenceTable = (SequenceSt_t *)CmdSequence[Idx].TableOrShortAction;
ServoIdx = (int8_t)PGM_READ_8(SequenceTable[SeqLine].ServoIndex);
#ifdef RC_SEQ_WITH_SHORT_ACTION_SUPPORT
if(ServoIdx==255) /* Not a Servo: it's a short Action to perform only if not already done */
if(ServoIdx == 255) /* Not a Servo: it's a short Action to perform only if not already done */
{
ShortActionCnt++;
StartOfSeqMs = CmdSequence[Idx] AsMember StartChronoMs + (int32_t)PGM_READ_32(SequenceTable[SeqLine].StartMotionOffsetMs);
if( (NowMs>=StartOfSeqMs) && !TST_BIT(CmdSequence[Idx] AsMember ShortActionMap,ShortActionCnt) )
StartOfSeqMs = CmdSequence[Idx].StartChronoMs + (int32_t)PGM_READ_32(SequenceTable[SeqLine].StartMotionOffsetMs);
if( (NowMs >= StartOfSeqMs) && !TST_BIT(CmdSequence[Idx].ShortActionMap, ShortActionCnt) )
{
ShortAction=(void(*)(void))PGM_READ_16(SequenceTable[SeqLine].ShortAction);
ShortAction = (void(*)(void))PGM_READ_16(SequenceTable[SeqLine].ShortAction);
ShortAction();
SET_BIT(CmdSequence[Idx] AsMember ShortActionMap,ShortActionCnt); /* Mark short Action as performed */
/* If the last line contains an Action AsMember End of Sequence */
if(SeqLine==(CmdSequence[Idx] AsMember SequenceLength-1))
SET_BIT(CmdSequence[Idx].ShortActionMap, ShortActionCnt); /* Mark short Action as performed */
/* If the last line contains an Action: End of Sequence */
if(SeqLine == (CmdSequence[Idx].SequenceLength - 1))
{
CmdSequence[Idx] AsMember InProgress=0;
CmdSequence[Idx] AsMember ShortActionMap=0; /* Mark all Short Action as not performed */
CmdSequence[Idx].InProgress = 0;
CmdSequence[Idx].ShortActionMap = 0; /* Mark all Short Action as not performed */
}
}
continue;
}
#endif
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
if(Servo[ServoIdx] AsMember RefreshNb && SeqLine!=Servo[ServoIdx] AsMember SeqLineInProgress)
if(Servo[ServoIdx].RefreshNb && SeqLine != Servo[ServoIdx].SeqLineInProgress)
{
continue;
}
StartOfSeqMs = CmdSequence[Idx] AsMember StartChronoMs + (int32_t)PGM_READ_32(SequenceTable[SeqLine].StartMotionOffsetMs);
StartOfSeqMs = CmdSequence[Idx].StartChronoMs + (int32_t)PGM_READ_32(SequenceTable[SeqLine].StartMotionOffsetMs);
MotionDurationMs = (int32_t)PGM_READ_32(SequenceTable[SeqLine].MotionDurationMs);
EndOfSeqMs = StartOfSeqMs + MotionDurationMs;
if(!Servo[ServoIdx] AsMember RefreshNb && Servo[ServoIdx] AsMember SeqLineInProgress==NO_SEQ_LINE)
if(!Servo[ServoIdx].RefreshNb && Servo[ServoIdx].SeqLineInProgress == NO_SEQ_LINE)
{
if( (NowMs>=StartOfSeqMs) && (NowMs<=EndOfSeqMs) )
if( (NowMs >= StartOfSeqMs) && (NowMs <= EndOfSeqMs) )
{
Servo[ServoIdx] AsMember SeqLineInProgress=SeqLine;
StartInDegrees=(uint16_t)PGM_READ_8(SequenceTable[SeqLine].StartInDegrees);
Servo[ServoIdx] AsMember RefreshNb=REFRESH_NB(MotionDurationMs);
Servo[ServoIdx] AsMember Motor.write(StartInDegrees);
Servo[ServoIdx].SeqLineInProgress = SeqLine;
StartInDegrees = (uint16_t)PGM_READ_8(SequenceTable[SeqLine].StartInDegrees);
Servo[ServoIdx].RefreshNb = REFRESH_NB(MotionDurationMs);
Servo[ServoIdx].Motor.write(StartInDegrees);
}
}
else
{
/* A sequence line is in progress: update the next position */
if(Servo[ServoIdx] AsMember RefreshNb) Servo[ServoIdx] AsMember RefreshNb--;
StartInDegrees=(uint16_t)PGM_READ_8(SequenceTable[SeqLine].StartInDegrees);
EndInDegrees=(uint16_t)PGM_READ_8(SequenceTable[SeqLine].EndInDegrees);
Pos=(int32_t)EndInDegrees-((int32_t)Servo[ServoIdx] AsMember RefreshNb*STEP_IN_DEGREES_PER_REFRESH((int32_t)StartInDegrees,(int32_t)EndInDegrees,(int32_t)MotionDurationMs)); //For refresh max nb, Pos = StartInDegrees
Servo[ServoIdx] AsMember Motor.write(Pos);
if( !Servo[ServoIdx] AsMember RefreshNb )
if(Servo[ServoIdx].RefreshNb) Servo[ServoIdx].RefreshNb--;
StartInDegrees = (uint16_t)PGM_READ_8(SequenceTable[SeqLine].StartInDegrees);
EndInDegrees = (uint16_t)PGM_READ_8(SequenceTable[SeqLine].EndInDegrees);
Pos = (int32_t)EndInDegrees - ((int32_t)Servo[ServoIdx].RefreshNb * STEP_IN_DEGREES_PER_REFRESH((int32_t)StartInDegrees,(int32_t)EndInDegrees,(int32_t)MotionDurationMs)); //For refresh max nb, Pos = StartInDegrees
Servo[ServoIdx].Motor.write(Pos);
if( !Servo[ServoIdx].RefreshNb )
{
Servo[ServoIdx] AsMember SeqLineInProgress=NO_SEQ_LINE;
Servo[ServoIdx].SeqLineInProgress = NO_SEQ_LINE;
/* Last servo motion and refresh = 0 -> End of Sequence */
if(SeqLine==(CmdSequence[Idx] AsMember SequenceLength-1))
if(SeqLine == (CmdSequence[Idx].SequenceLength - 1))
{
CmdSequence[Idx] AsMember InProgress=0;
CmdSequence[Idx] AsMember ShortActionMap=0; /* Mark all Short Action as not performed */
CmdSequence[Idx].InProgress = 0;
CmdSequence[Idx].ShortActionMap = 0; /* Mark all Short Action as not performed */
#ifdef RC_SEQ_CONTROL_SUPPORT
if(CmdSequence[Idx].Control != NULL) CmdSequence[Idx].Control(RC_SEQ_END_OF_SEQ, Idx);
#endif
}
}
}
@ -501,76 +529,94 @@ uint32_t RcPulseWidthUs;
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
SoftRcPulseOut::refresh(1); /* Force Refresh */
#endif
StartChronoInterPulseMs=millis();
StartChronoInterPulseMs = millis();
}
}
//========================================================================================================================
// PRIVATE FUNCTIONS
//========================================================================================================================
static void ExecuteSequence(uint8_t CmdIdx, uint8_t Pos)
static uint8_t ExecuteSequence(uint8_t CmdIdx, uint8_t Pos)
{
void(*ShortAction)(void);
uint8_t Idx;
uint8_t Idx, Ret = 0;
for(Idx=0;Idx<SeqNb;Idx++)
for(Idx = 0; Idx < SeqNb; Idx++)
{
if((CmdSequence[Idx] AsMember CmdIdx==CmdIdx) && (CmdSequence[Idx] AsMember Pos==Pos))
if((CmdSequence[Idx].CmdIdx == CmdIdx) && (CmdSequence[Idx].Pos == Pos))
{
#ifdef RC_SEQ_WITH_SHORT_ACTION_SUPPORT
if(CmdSequence[Idx] AsMember TableOrShortAction && !CmdSequence[Idx] AsMember SequenceLength)
if(CmdSequence[Idx].TableOrShortAction && !CmdSequence[Idx].SequenceLength)
{
/* It's a short action */
ShortAction=(void(*)(void))CmdSequence[Idx] AsMember TableOrShortAction;
ShortAction = (void(*)(void))CmdSequence[Idx].TableOrShortAction;
ShortAction();
Ret = 1;
}
else
#endif
{
/* It's a Table of Sequence */
if(!CmdSequence[Idx] AsMember InProgress)
if(!CmdSequence[Idx].InProgress)
{
CmdSequence[Idx] AsMember InProgress=1;
CmdSequence[Idx] AsMember StartChronoMs=millis();
#ifdef RC_SEQ_CONTROL_SUPPORT
uint8_t Go = 1;
if(CmdSequence[Idx].Control != NULL)
{
Go = CmdSequence[Idx].Control(RC_SEQ_START_CONDITION, Idx);
// Serial.print(F("Go for Seq["));Serial.print(Idx);Serial.print(F("] "));Serial.println(Go?F("Yes"):F("No"));
}
if(Go)
{
CmdSequence[Idx].InProgress = 1;
CmdSequence[Idx].StartChronoMs = millis();
Ret = 1;
}
#else
CmdSequence[Idx].InProgress = 1;
CmdSequence[Idx].StartChronoMs = millis();
Ret = 1;
#endif
}
}
break;
}
}
return(Ret);
}
//========================================================================================================================
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
static int8_t GetPos(uint8_t ChIdx,uint16_t PulseWidthUs)
static int8_t GetPos(uint8_t ChIdx, uint16_t PulseWidthUs)
{
int8_t Idx, Ret=-1;
uint16_t PulseMinUs,PulseMaxUs;
int8_t Idx, Ret = -1;
uint16_t PulseMinUs, PulseMaxUs;
for(Idx=0;Idx<RcChannel[ChIdx].PosNb;Idx++)
for(Idx = 0; Idx < RcChannel[ChIdx].PosNb; Idx++)
{
switch(RcChannel[ChIdx].Type)
{
case RC_CMD_STICK: /* No break: normal */
case RC_CMD_MULTI_POS_SW:
if( (RcChannel[ChIdx].Type==RC_CMD_MULTI_POS_SW) || ((RcChannel[ChIdx].Type==RC_CMD_STICK) && (Idx<(RcChannel[ChIdx].PosNb/2))) )
if( (RcChannel[ChIdx].Type == RC_CMD_MULTI_POS_SW) || ((RcChannel[ChIdx].Type == RC_CMD_STICK) && (Idx < (RcChannel[ChIdx].PosNb / 2))) )
{
PulseMinUs=RcChannel[ChIdx].PulseMinUs+KEY_MIN_VAL(Idx,RcChannel[ChIdx].StepUs);
PulseMaxUs=RcChannel[ChIdx].PulseMinUs+KEY_MAX_VAL(Idx,RcChannel[ChIdx].StepUs);
PulseMinUs = RcChannel[ChIdx].PulseMinUs + KEY_MIN_VAL(Idx,RcChannel[ChIdx].StepUs);
PulseMaxUs = RcChannel[ChIdx].PulseMinUs + KEY_MAX_VAL(Idx,RcChannel[ChIdx].StepUs);
}
else
{
PulseMinUs=RcChannel[ChIdx].PulseMaxUs-KEY_MAX_VAL(RcChannel[ChIdx].PosNb-1-Idx,RcChannel[ChIdx].StepUs);
PulseMaxUs=RcChannel[ChIdx].PulseMaxUs-KEY_MIN_VAL(RcChannel[ChIdx].PosNb-1-Idx,RcChannel[ChIdx].StepUs);
PulseMinUs = RcChannel[ChIdx].PulseMaxUs - KEY_MAX_VAL(RcChannel[ChIdx].PosNb - 1 - Idx, RcChannel[ChIdx].StepUs);
PulseMaxUs = RcChannel[ChIdx].PulseMaxUs - KEY_MIN_VAL(RcChannel[ChIdx].PosNb - 1 - Idx, RcChannel[ChIdx].StepUs);
}
break;
case RC_CMD_CUSTOM:
PulseMinUs=(uint16_t)PGM_READ_16(RcChannel[ChIdx].KeyMap[Idx].Min);
PulseMaxUs=(uint16_t)PGM_READ_16(RcChannel[ChIdx].KeyMap[Idx].Max);
PulseMinUs = (uint16_t)PGM_READ_16(RcChannel[ChIdx].KeyMap[Idx].Min);
PulseMaxUs = (uint16_t)PGM_READ_16(RcChannel[ChIdx].KeyMap[Idx].Max);
break;
}
if((PulseWidthUs>=PulseMinUs) && (PulseWidthUs<=PulseMaxUs))
if((PulseWidthUs >= PulseMinUs) && (PulseWidthUs <= PulseMaxUs))
{
Ret=Idx;
Ret = Idx;
break;
}
}

View File

@ -2,7 +2,7 @@
#define RC_SEQ_H
/*
English: by RC Navy (2012/2013)
English: by RC Navy (2012-2015)
=======
<RcSeq> is an asynchronous library for ATmega328P (UNO), ATtiny84 and ATtiny85 to easily create servo's sequences and/or to execute short actions from RC commands.
It can also be used to trig some short "actions" (the duration must be less than 20ms to not disturb the servo commands)
@ -23,7 +23,7 @@
CAUTION: the end user shall also use asynchronous programmation method in the loop() function (no blocking functions such as delay() or pulseIn()).
http://p.loussouarn.free.fr
Francais: par RC Navy (2012/2013)
Francais: par RC Navy (2012-2015)
========
<RcSeq> est une librairie asynchrone pour ATmega328P (UNO), ATtiny84 et ATtiny85 pour creer facilement des sequences de servos et/ou executer des actions depuis des commandes RC.
Elle peut egalement etre utilisee pour lancer des "actions courtes" (la duree doit etre inferieure a 20ms pour ne pas perturber la commande des servos)
@ -48,19 +48,17 @@
/* RCSEQ LIBRARY CONFIGURATION */
/**********************************************/
#define RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT /* Comment this line if you use <DigiUSB> library in your sketch */
//#define RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT /* Uncomment this if you use <SoftRcPulseOut> library in your sketch for servos and ESC */
#define RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT /* Uncomment this if you use <SoftRcPulseOut> library in your sketch for servos and ESC */
#define RC_SEQ_WITH_SHORT_ACTION_SUPPORT /* Uncomment this to allows to put call to short action in sequence table */
#define RC_SEQ_CONTROL_SUPPORT /* Uncomment this to allow control on sequences: start condition and end of sequence */
/**********************************************/
/* /!\ Do not touch below /!\ */
/**********************************************/
/* For an easy Library Version Management */
#define RC_SEQ_LIB_VERSION 2
#define RC_SEQ_LIB_REVISION 0
#define RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT /* Do NOT comment this line for DigiSpark, but you can for UNO */
#define RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT /* Do NOT comment this line for now: still buggy (to do: fix this) */
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
#include <TinyPinChange.h>
@ -86,14 +84,26 @@
#include <inttypes.h>
#include <stdio.h>
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
#define SERVO_MAX_NB 10
#define SEQUENCE_MAX_NB 8
#define RC_CMD_MAX_NB 4
#if defined(RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT)
#if defined(__AVR_ATtiny85__)
#define SERVO_MAX_NB 3 /* 3 is the maximum for DigiSpark if DigiUSB is used in the skecth */
#else
#if (defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny167__))
#define SERVO_MAX_NB 6
#else
#define SERVO_MAX_NB 10
#endif
#endif
#else
#define SERVO_MAX_NB 3 /* 3 is the maximum for DigiSpark if DigiUSB is used in the skecth */
#define SEQUENCE_MAX_NB 1 /* 1 is the maximum for DigiSpark if DigiUSB is used in the skecth */
#define RC_CMD_MAX_NB 0
#define SERVO_MAX_NB 0
#endif
#if (defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny167__))
#define SEQUENCE_MAX_NB 4 /* 2 is the maximum for DigiSpark if DigiUSB is used in the skecth */
#define RC_CMD_MAX_NB 2
#else
#define SEQUENCE_MAX_NB 10
#define RC_CMD_MAX_NB 4
#endif
typedef struct {
@ -122,7 +132,7 @@ typedef struct {
#define MOTION_WITHOUT_SOFT_START_AND_STOP(ServoIndex,StartInDegrees,EndInDegrees,StartMvtOffsetMs,MvtDurationMs) \
{ServoIndex, StartInDegrees, EndInDegrees, StartMvtOffsetMs, MvtDurationMs, NULL},
/* Macro to declare a short action (to use in "Sequence[]" structure table) */
/* Macro to declare a short action (to be used in "Sequence[]" structure table) */
#define SHORT_ACTION_TO_PERFORM(ShortAction, StartActionOffsetMs) {255, 0, 0, (StartActionOffsetMs), 0L, (ShortAction)},
enum {RC_CMD_STICK=0, RC_CMD_MULTI_POS_SW, RC_CMD_CUSTOM};
@ -138,20 +148,27 @@ uint8_t RcSeq_LibRevision(void);
char *RcSeq_LibTextVersionRevision(void);
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
void RcSeq_DeclareServo(uint8_t Idx, uint8_t DigitalPin);
void RcSeq_ServoWrite(uint8_t Idx, uint16_t Angle);
#endif
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
void RcSeq_DeclareSignal(uint8_t Idx, uint8_t DigitalPin);
void RcSeq_DeclareKeyboardOrStickOrCustom(uint8_t ChIdx, uint8_t Type, uint16_t PulseMinUs, uint16_t PulseMaxUs, KeyMap_t *KeyMapTbl, uint8_t PosNb);
void RcSeq_DeclareCustomKeyboard(uint8_t ChIdx, KeyMap_t *KeyMapTbl, uint8_t PosNb);
boolean RcSeq_SignalTimeout(uint8_t Idx, uint8_t TimeoutMs, uint8_t *State);
void RcSeq_DeclareKeyboardOrStickOrCustom(uint8_t ChIdx, uint8_t Type, uint16_t PulseMinUs, uint16_t PulseMaxUs, const KeyMap_t *KeyMapTbl, uint8_t PosNb);
void RcSeq_DeclareCustomKeyboard(uint8_t ChIdx, const KeyMap_t *KeyMapTbl, uint8_t PosNb);
#define RcSeq_DeclareStick(ChIdx, PulseMinUs, PulseMaxUs, PosNb) RcSeq_DeclareKeyboardOrStickOrCustom(ChIdx, RC_CMD_STICK, PulseMinUs, PulseMaxUs, NULL, PosNb)
#define RcSeq_DeclareMultiPosSwitch(ChIdx, PulseMinUs, PulseMaxUs, PosNb) RcSeq_DeclareKeyboardOrStickOrCustom(ChIdx, RC_CMD_MULTI_POS_SW, PulseMinUs, PulseMaxUs, NULL, PosNb)
#define RcSeq_DeclareKeyboard(ChIdx, PulseMinUs, PulseMaxUs, KeyNb) RcSeq_DeclareKeyboardOrStickOrCustom(ChIdx, RC_CMD_MULTI_POS_SW, PulseMinUs, PulseMaxUs, NULL, KeyNb)
#ifdef RC_SEQ_WITH_SHORT_ACTION_SUPPORT
void RcSeq_DeclareCommandAndShortAction(uint8_t CmdIdx,uint8_t TypeCmd,void(*ShortAction)(void));
void RcSeq_DeclareCommandAndShortAction(uint8_t CmdIdx, uint8_t TypeCmd, void(*ShortAction)(void));
#endif
#endif
void RcSeq_DeclareCommandAndSequence(uint8_t CmdIdx,uint8_t TypeCmd,SequenceSt_t *Table, uint8_t SequenceLength);
uint8_t RcSeq_LaunchSequence(SequenceSt_t *Table);
#ifdef RC_SEQ_CONTROL_SUPPORT
void RcSeq_DeclareCommandAndSequence(uint8_t CmdIdx, uint8_t TypeCmd, const SequenceSt_t *Table, uint8_t SequenceLength, uint8_t(*Control)(uint8_t Action, uint8_t CmdSeqIdx));
enum {RC_SEQ_START_CONDITION, RC_SEQ_END_OF_SEQ};
#else
void RcSeq_DeclareCommandAndSequence(uint8_t CmdIdx, uint8_t TypeCmd, const SequenceSt_t *Table, uint8_t SequenceLength);
#endif
uint8_t RcSeq_LaunchSequence(const SequenceSt_t *Table);
#ifdef RC_SEQ_WITH_SHORT_ACTION_SUPPORT
#define RcSeq_LaunchShortAction(ShortAction) if(ShortAction) ShortAction()
#endif
@ -162,25 +179,28 @@ void RcSeq_Refresh(void);
/*******************************************************/
/* Macro en Francais de declaration mouvement English native Macro to declare a motion */
#define MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS MOTION_WITH_SOFT_START_AND_STOP
#define MVT_SANS_DEBUT_ET_FIN_MVT_LENTS MOTION_WITHOUT_SOFT_START_AND_STOP
#define ACTION_COURTE_A_EFFECTUER SHORT_ACTION_TO_PERFORM
#define MVT_AVEC_DEBUT_ET_FIN_MVT_LENTS MOTION_WITH_SOFT_START_AND_STOP
#define MVT_SANS_DEBUT_ET_FIN_MVT_LENTS MOTION_WITHOUT_SOFT_START_AND_STOP
#define ACTION_COURTE_A_EFFECTUER SHORT_ACTION_TO_PERFORM
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
#define RC_CLAVIER_MAISON RC_CUSTOM_KEYBOARD
#define VALEUR_CENTRALE_US CENTER_VALUE_US
#define RC_CLAVIER_MAISON RC_CUSTOM_KEYBOARD
#define VALEUR_CENTRALE_US CENTER_VALUE_US
#endif
#ifdef RC_SEQ_CONTROL_SUPPORT
#define RC_SEQ_CONDITION_DE_DEPART RC_SEQ_START_CONDITION
#define RC_SEQ_FIN_DE_SEQ RC_SEQ_END_OF_SEQ
#endif
/* Methodes en Francais English native methods */
#ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
#define RcSeq_DeclareManche RcSeq_DeclareStick
#define RcSeq_DeclareClavier RcSeq_DeclareKeyboard
#define RcSeq_DeclareClavierMaison RcSeq_DeclareCustomKeyboard
#define RcSeq_DeclareInterMultiPos RcSeq_DeclareMultiPosSwitch
#define RcSeq_DeclareCommandeEtActionCourte RcSeq_DeclareCommandAndShortAction
#define RcSeq_DeclareManche RcSeq_DeclareStick
#define RcSeq_DeclareClavier RcSeq_DeclareKeyboard
#define RcSeq_DeclareClavierMaison RcSeq_DeclareCustomKeyboard
#define RcSeq_DeclareInterMultiPos RcSeq_DeclareMultiPosSwitch
#define RcSeq_DeclareCommandeEtActionCourte RcSeq_DeclareCommandAndShortAction
#endif
#define RcSeq_DeclareCommandeEtSequence RcSeq_DeclareCommandAndSequence
#define RcSeq_LanceSequence RcSeq_LaunchSequence
#define RcSeq_LanceActionCourte RcSeq_LaunchShortAction
#define RcSeq_Rafraichit RcSeq_Refresh
#define RcSeq_DeclareCommandeEtSequence RcSeq_DeclareCommandAndSequence
#define RcSeq_LanceSequence RcSeq_LaunchSequence
#define RcSeq_LanceActionCourte RcSeq_LaunchShortAction
#define RcSeq_Rafraichit RcSeq_Refresh
#endif

View File

@ -0,0 +1,116 @@
RcSeq library
=============
**RcSeq** is an asynchronous library for ATmega328P (**UNO**), ATmega2560 (**MEGA**), ATtiny84, ATtiny85 (**Digispark**) and ATtiny167 (**Digispark pro**) to easily create **servo's sequences** and/or to execute **short actions** from RC commands, from a digital input, or from a launch function called in the sketch.
The **A**pplication **P**rogramming **I**nterface (**API**) makes **RcSeq** library very easy to use.
Some definitions:
----------------
* **Sequence**: is used to sequence one or several servos (sequence is defined in a structure in the user's sketch to be performed when the RC command rises). The Sequence table (structure) may contain some servo motions and some short actions to call at a predefined time. For each servo, start angle, end angle and speed are tunable.
* **Short Action**: is used to perform a quick action (action is a short function defined in the user's sketch to be called when the RC command rises). The duration must be less than 20ms to not disturb the servo commands.
Some examples of use cases:
--------------------------
* **A landing gear retract:**
* Lock, door and leg servos sequenced with a single RC channel
* from a predefined position of the stick on the transmitter
* from the 2 positions "Aux Channel" of the transmitter
* **Navigation lights for aircraft:**
* Anticollision, beacon, landing lights commanded:
* from predefined positions of the stick on the transmitter
* from push-buttons in place of the stick potentiometer
* **Multi-switch:**
* Up to 8 digital pins driven from a single RC channel
* using the stick of the transmitter
* using 8 push-buttons in place of the stick potentiometer
* **Zodiac animation:**
* A pneumatic Zodiac dropped at sea and lifted back to the deck of a ship. Drop and lift sequences commanded:
* from predefined positions of the stick on the transmitter
* from a regular ON/OFF switch (for demo on table without RC set)
* **Animatronics sequences:**
* leg motion,
* mouth motion,
* eyes motion,
* etc.
Triggers:
--------
**Sequences** and **short actions** can be trigged by:
* a RC signal (eg: RC receiver output)
* from one or several **predefined positions of a stick** of the transmitter
* from one or several **push-button** (keyboard) replacing a a stick of the transmitter. (**RcSeq** assumes Push-Buttons associated Pulse duration are equidistant).
* from **Custom Keyboard** replacing a stick of the the transmitter. (The pulse durations can be defined independently for each Push-Button).
* from **Multi position switch** (2 pos switch, 3 pos switch, or more, eg. rotactor) replacing a stick of the the transmitter.
* a regular ON/OFF switch (no RC set required).
* a launch function call in the sketch.
API/methods:
-----------
* RcSeq_Init()
* RcSeq_DeclareSignal()
* RcSeq_DeclareStick()
* RcSeq_DeclareKeyboard()
* RcSeq_DeclareCustomKeyboard()
* RcSeq_DeclareMultiPosSwitch()
* RcSeq_SignalTimeout()
* RcSeq_DeclareServo()
* RcSeq_DeclareCommandAndSequence()
* RcSeq_DeclareCommandAndShortAction()
* RcSeq_LaunchSequence()
* RcSeq_LaunchShortAction()
* RcSeq_Refresh()
* RcSeq_LibVersion()
* RcSeq_LibRevision()
* RcSeq_LibTextVersionRevision()
Macros and constants:
--------------------
* const SequenceSt_t
* const KeyMap_t
* RC_SEQUENCE()
* RC_CUSTOM_KEYBOARD()
* SHORT_ACTION_TO_PERFORM()
* MOTION_WITH_SOFT_START_AND_STOP()
* MOTION_WITHOUT_SOFT_START_AND_STOP()
* CENTER_VALUE_US
* RC_SEQ_START_CONDITION
* RC_SEQ_END_OF_SEQ
Design considerations:
---------------------
The **RcSeq** library requires 3 other libraries written by the same author:
1. **TinyPinChange**: a library to catch asynchronously the input change using Pin Change Interruption capability of the AVR microcontroller.
2. **SoftRcPulseIn**: a library to catch asynchronously the input pulses using **TinyPinChange** library.
3. **SoftRcPulseOut**: a library mainly based on the **SoftwareServo** library, but with a better pulse generation to limit jitter and with some other enhancements.
CAUTION:
-------
The end user shall also use asynchronous programmation method in the loop() function (no blocking functions such as delay() or pulseIn()).
Contact
-------
If you have some ideas of enhancement, please contact me by clicking on: [RC Navy](http://p.loussouarn.free.fr/contact.html).

View File

@ -15,11 +15,13 @@ RcSeq_LibRevision KEYWORD2
RcSeq_LibTextVersionRevision KEYWORD2
RcSeq_Init KEYWORD2
RcSeq_DeclareSignal KEYWORD2
RcSeq_SignalTimeout KEYWORD2
RcSeq_DeclareKeyboard KEYWORD2
RcSeq_DeclareClavier KEYWORD2
RcSeq_DeclareStick KEYWORD2
RcSeq_DeclareManche KEYWORD2
RcSeq_DeclareServo KEYWORD2
RcSeq_ServoWrite KEYWORD2
RcSeq_DeclareCustomKeyboard KEYWORD2
RcSeq_DeclareClavierMaison KEYWORD2
RcSeq_DeclareMultiPosSwitch KEYWORD2
@ -51,3 +53,8 @@ RC_CLAVIER_MAISON LITERAL1
RC_SEQUENCE LITERAL1
CENTER_VALUE_US LITERAL1
VALEUR_CENTRALE_US LITERAL1
RC_SEQ_START_CONDITION LITERAL1
RC_SEQ_CONDITION_DE_DEPART LITERAL1
RC_SEQ_END_OF_SEQ LITERAL1
RC_SEQ_FIN_DE_SEQ LITERAL1

View File

@ -0,0 +1,52 @@
SoftRcPulseIn library
======================
**SoftRcPulseIn** is an asynchronous library designed to read RC pulse signals. It is a non-blocking version of arduino **pulseIn()** function.
Some examples of use cases:
-------------------------
* **RC Servo/ESC/Brushless Controller**
* **Multi-switch (RC Channel to digital outputs converter)** (look at **RcSeq** library)
* **Servo sequencer** (look at **RcSeq** library which uses **SoftRcPulseOut** library)
* **RC Robot using wheels with modified Servo to support 360° rotation**
* **RC pulse stretcher** (in conjunction with **SoftRcPulseOut** library)
Supported Arduinos:
------------------
* **ATmega328 (UNO)**
* **ATmega2560 (MEGA)**
* **ATtiny84 (Standalone)**
* **ATtiny85 (Standalone or Digispark)**
* **ATtiny167 (Digispark pro)**
Tip and Tricks:
--------------
Develop your project on an arduino UNO or MEGA, and then shrink it by loading the sketch in an ATtiny or Digispark (pro).
API/methods:
-----------
* attach()
* available()
* width_us()
* timeout()
* LibVersion()
* LibRevision()
* LibTextVersionRevision()
Design considerations:
---------------------
The **SoftRcPulseIn** library relies the **TinyPinChange** library. This one shall be included in the sketch as well.
On the arduino MEGA, as all the pins do not support "pin change interrupt", only the following pins are supported:
* 10 -> 15
* 50 -> 53
* A8 -> A15
On other devices (ATmega328, ATtiny84, ATtiny85 and ATtiny167), all the pins are usable.
Contact
-------
If you have some ideas of enhancement, please contact me by clicking on: [RC Navy](http://p.loussouarn.free.fr/contact.html).

View File

@ -1,19 +1,22 @@
/*
English: by RC Navy (2012)
English: by RC Navy (2012-2015)
=======
<SoftRcPulseIn>: an asynchronous library to read Input Pulse Width from standard Hobby Radio-Control. This library is a non-blocking version of pulseIn().
http://p.loussouarn.free.fr
Francais: par RC Navy (2012)
V1.0: initial release
V1.1: asynchronous timeout support added (up to 250ms)
Francais: par RC Navy (2012-2015)
========
<SoftRcPulseIn>: une librairie asynchrone pour lire les largeur d'impulsions des Radio-Commandes standards. Cette librairie est une version non bloquante de pulsIn().
http://p.loussouarn.free.fr
V1.0: release initiale
V1.1: support de timeout asynchrone ajoutee (jusqu'a 250ms)
*/
#include "SoftRcPulseIn.h"
#define LIB_VERSION 1
#define LIB_REVISION 0
#define LIB_VERSION 1
#define LIB_REVISION 1
#define STR(s) #s
#define MAKE_TEXT_VER_REV(Ver,Rev) STR(Ver)"."STR(Rev)
@ -31,15 +34,15 @@ uint8_t SoftRcPulseIn::attach(uint8_t Pin, uint16_t PulseMin_us/*=600*/, uint16_
uint8_t Ret=0;
_Pin=Pin;
_PinMask=TinyPinChange_PinToMsk(Pin);
_Min_us=PulseMin_us;
_Max_us=PulseMax_us;
_PinMask = TinyPinChange_PinToMsk(Pin);
_Min_us = PulseMin_us;
_Max_us = PulseMax_us;
next = first;
first = this;
pinMode(_Pin,INPUT);
digitalWrite(_Pin, HIGH);
_VirtualPortIdx=TinyPinChange_RegisterIsr(_Pin,SoftRcPulseIn::SoftRcPulseInInterrupt);
if(_VirtualPortIdx>=0)
_VirtualPortIdx = TinyPinChange_RegisterIsr(_Pin, SoftRcPulseIn::SoftRcPulseInInterrupt);
if(_VirtualPortIdx >= 0)
{
TinyPinChange_EnablePin(_Pin);
Ret=1;
@ -70,19 +73,32 @@ uint16_t PulseWidth_us;
if(_Available)
{
noInterrupts();
PulseWidth_us=_Width_us;
PulseWidth_us = _Width_us;
interrupts();
Ret=_Available && (PulseWidth_us>=_Min_us) && (PulseWidth_us<=_Max_us);
Ret=_Available && (PulseWidth_us >= _Min_us) && (PulseWidth_us <= _Max_us);
_Available=0;
}
return(Ret);
}
#ifdef SOFT_RC_PULSE_IN_TIMEOUT_SUPPORT
boolean SoftRcPulseIn::timeout(uint8_t TimeoutMs, uint8_t *CurrentState)
{
uint8_t CurMs, Ret = 0;
CurMs = (uint8_t)(millis() & 0x000000FF);
if((uint8_t)(CurMs - _LastTimeStampMs) >= TimeoutMs)
{
*CurrentState = digitalRead(_Pin);
Ret = 1;
}
return(Ret);
}
#endif
uint16_t SoftRcPulseIn::width_us(void)
{
uint16_t PulseWidth_us;
noInterrupts();
PulseWidth_us=_Width_us;
PulseWidth_us = _Width_us;
interrupts();
return(PulseWidth_us);
}
@ -98,13 +114,16 @@ SoftRcPulseIn *RcPulseIn;
if(digitalRead(RcPulseIn->_Pin))
{
/* High level, rising edge: start chrono */
RcPulseIn->_Start_us=micros();
RcPulseIn->_Start_us = micros();
}
else
{
/* Low level, falling edge: stop chrono */
RcPulseIn->_Width_us=micros()-RcPulseIn->_Start_us;
RcPulseIn->_Available=1;
RcPulseIn->_Width_us = micros() - RcPulseIn->_Start_us;
RcPulseIn->_Available = 1;
#ifdef SOFT_RC_PULSE_IN_TIMEOUT_SUPPORT
RcPulseIn->_LastTimeStampMs = (uint8_t)(millis() & 0x000000FF);
#endif
}
}
}

View File

@ -1,10 +1,10 @@
/*
English: by RC Navy (2012)
English: by RC Navy (2012-2015)
=======
<SoftRcPulseIn>: an asynchronous library to read Input Pulse Width from standard Hobby Radio-Control. This library is a non-blocking version of pulseIn().
http://p.loussouarn.free.fr
Francais: par RC Navy (2012)
Francais: par RC Navy (2012-2015)
========
<SoftRcPulseIn>: une librairie asynchrone pour lire les largeur d'impulsions des Radio-Commandes standards. Cette librairie est une version non bloquante de pulsIn().
http://p.loussouarn.free.fr
@ -23,6 +23,8 @@
#include <inttypes.h>
#define SOFT_RC_PULSE_IN_TIMEOUT_SUPPORT
class SoftRcPulseIn
{
public:
@ -31,8 +33,9 @@ class SoftRcPulseIn
static int LibRevision(void);
static char *LibTextVersionRevision(void);
static void SoftRcPulseInInterrupt(void);
uint8_t attach(uint8_t Pin, uint16_t PulseMin_us=600, uint16_t PulseMax_us=2400);
uint8_t attach(uint8_t Pin, uint16_t PulseMin_us = 600, uint16_t PulseMax_us = 2400);
boolean available();
boolean timeout(uint8_t TimeoutMs, uint8_t *State);
uint16_t width_us();
private:
class SoftRcPulseIn *next;
@ -45,6 +48,9 @@ class SoftRcPulseIn
uint32_t _Start_us;
uint32_t _Width_us;
boolean _Available;
#ifdef SOFT_RC_PULSE_IN_TIMEOUT_SUPPORT
uint8_t _LastTimeStampMs;
#endif
};
/*******************************************************/
/* Application Programming Interface (API) en Francais */

View File

@ -52,6 +52,8 @@
#define RX_AUX_GEAR_PIN 0 //Choose here the pin for the RC signal
#define DEBUG_TX_RX_PIN 1 //Adjust here your Tx/Rx debug pin (Do NOT work on Digispark PIN 5: choose another PIN)
#define SERIAL_BAUD_RATE 57600 //Adjust rate here
SoftRcPulseIn RxAuxGear; //Choose a name for your RC channel signal
SoftSerial MyDbgSerial(DEBUG_TX_RX_PIN, DEBUG_TX_RX_PIN, true); //true allows to connect to a regular RS232 without RS232 line driver
@ -60,7 +62,7 @@ void setup()
{
RxAuxGear.attach(RX_AUX_GEAR_PIN);
MyDbgSerial.begin(38400); //Do NOT forget to setup your terminal at 38400 (eg: arduino IDE serial monitor)
MyDbgSerial.begin(SERIAL_BAUD_RATE); //Do NOT forget to setup your terminal at same baud rate (eg: arduino IDE serial monitor)
MyDbgSerial.txMode(); //Before sending a message, switch to txMode
MyDbgSerial.print(F("SoftRcPulseIn lib V"));MyDbgSerial.print(SoftRcPulseIn::LibTextVersionRevision());MyDbgSerial.println(F(" demo"));
}

View File

@ -52,7 +52,7 @@ boolean LedState=HIGH;
void setup()
{
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__)
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny167__)
Serial.begin(9600);
Serial.print("SoftRcPulseIn library V");Serial.print(SoftRcPulseIn::LibTextVersionRevision());Serial.print(" demo"); /* For arduino UNO which has an hardware UART, display the library version in the console */
#endif
@ -74,8 +74,8 @@ static uint16_t Width_us=NEUTRAL_US; /* Static to keep the value at the next loo
ServoMotor[INVERTED].write_us((NEUTRAL_US*2)-Width_us); /* Inverted Signal */
SoftRcPulseOut::refresh(NOW); /* NOW argument (=1) allows to synchronize outgoing pulses with incoming pulses */
RxPulseStartMs=millis(); /* Restart the Chrono for Pulse */
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__)
Serial.print("Pulse=");Serial.println(Largeur_us); /* For arduino UNO which has an hardware UART, display the library version in the console */
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny167__)
Serial.print("Pulse=");Serial.println(Width_us); /* For arduino UNO which has an hardware UART, display the library version in the console */
#endif
}
else

View File

@ -18,6 +18,7 @@ attach KEYWORD2
attache KEYWORD2
available KEYWORD2
disponible KEYWORD2
timeout KEYWORD2
width_us KEYWORD2
largeur_us KEYWORD2

View File

@ -0,0 +1,67 @@
SoftRcPulseOut library
======================
**SoftRcPulseOut** is pseudo-asynchronous library designed to generate RC pulse signals. RC pulse signals are intended to command servos, **E**lectronic **S**peed **C**ontrollers (**ESC**), Brushless Controllers and any devices expecting such a command signal.
Some examples of use cases:
-------------------------
* **Servo/ESC/Brushless Controller tester**
* **Servo sequencer** (look at RcSeq library which uses _SoftRcPulseOut_)
* **Robot wheels using modified Servo to support 360° rotation**
* **RC pulse stretcher** (in conjunction with **SoftRcPulseIn** library)
Supported Arduinos:
------------------
* **ATmega368 (UNO)**
* **ATmega2560 (MEGA)**
* **ATtiny84 (Standalone)**
* **ATtiny85 (Standalone or Digispark)**
* **ATtiny167 (Digispark pro)**
Tip and Tricks:
--------------
Develop your project on an arduino UNO or MEGA, and then shrink it by loading the sketch in an ATtiny or Digispark (pro).
API/methods:
-----------
* The **SoftRcPulseOut** library uses the same API as the regular **SoftwareServo** library:
* attach()
* attached()
* detach()
* write()
* read()
* setMinimumPulse()
* setMaximumPulse()
* refresh()
* Two additional methods allow using µs rather than angle in ° :
* write_us()
* read_us()
* Methods for version management:
* LibVersion
* LibRevision
* LibTextVersionRevision
* Synchronization:
* By giving **_1_** or **_true_** as optional argument for the **SoftRcPulseOut::refresh()** method, the pulses are refreshed immediately (without waiting for the usual 20ms).
* the **SoftRcPulseOut::refresh()** method returns **_1_** or **_true_** when the pulses have been refreshed. Testing this return value provides a 20ms timer.
Design considerations:
---------------------
The **SoftRcPulseOut** library relies on a 8 bit timer. This allows using it even on little MCU (such as ATtiny85) which do not have any 16 bit timer.
Whereas a 8 bit timer is used for pulse generation, the jitter is limited by using anticipated interrupt masking.
Interrups are only masked during rising and falling edges of the pulse signals.
CAUTION:
-------
The end user shall also use asynchronous programmation method in the loop() function (not too long blocking functions such as delay(1000): the **SoftRcPulseOut::refresh()** method shall be called at least every 50ms).
Contact
-------
If you have some ideas of enhancement, please contact me by clicking on: [RC Navy](http://p.loussouarn.free.fr/contact.html).

View File

@ -1,13 +1,15 @@
#include "SoftRcPulseOut.h"
/*
Update 01/03/2013: add support for DigiSpark (http://digistump.com): automatic Timer selection (RC Navy: p.loussouarn.free.fr)
Update 01/03/2013: add support for Digispark (http://digistump.com): automatic Timer selection (RC Navy: p.loussouarn.free.fr)
Update 19/08/2014: usage with write_us and read_us fixed
English: by RC Navy (2012)
=======
<SoftRcPulseOut>: a library mainly based on the <SoftwareServo> library, but with a better pulse generation to limit jitter.
It supports the same methods as <SoftwareServo>.
It also support Pulse Width order given in microseconds. The current Pulse Width can also be read in microseconds.
The refresh method can admit an optionnal argument (force). If SoftRcPulseOut::refresh(1) is called, the refresh is forced even if 20 ms are not elapsed.
The refresh() method returns 1 if refresh done (can be used for synchro and/or for 20ms timer).
http://p.loussouarn.free.fr
Francais: par RC Navy (2012)
@ -16,6 +18,7 @@
Elle supporte les memes methodes que <SoftwareServo>.
Elle supporte egalement une consigne de largeur d'impulsion passee en microseconde. La largeur de l'impulsion courante peut egalement etre lue en microseconde.
La methode refresh peut admettre un parametre optionnel (force). Si SoftRcPulseOut::resfresh(1) est appelee, le refresh est force meme si 20 ms ne se sont pas ecoulee.
La methode refresh() retourne 1 si refresh effectue (peut etre utilise pour synhro et/ou 20ms timer).
http://p.loussouarn.free.fr
*/
@ -26,7 +29,7 @@
#if (TIMER_TO_USE_FOR_MILLIS==1)
#define SOFT_RC_PULSE_OUT_TCNT TCNT1 //For example for ATtiny85
#else
#define SOFT_RC_PULSE_OUT_TCNT TCNT0 //For example for ATtiny84
#define SOFT_RC_PULSE_OUT_TCNT TCNT0 //For example for ATtiny84 and ATtiny167
#endif
#endif
@ -34,60 +37,69 @@ SoftRcPulseOut *SoftRcPulseOut::first;
#define NO_ANGLE (0xff)
SoftRcPulseOut::SoftRcPulseOut() : pin(0),angle(NO_ANGLE),pulse0(0),min16(34),max16(150),next(0)
SoftRcPulseOut::SoftRcPulseOut() : pin(0), angle(NO_ANGLE), pulse0(0), min16(34), max16(150), next(0)
{}
void SoftRcPulseOut::setMinimumPulse(uint16_t t)
{
min16 = t/16;
min16 = t / 16;
}
void SoftRcPulseOut::setMaximumPulse(uint16_t t)
{
max16 = t/16;
max16 = t / 16;
}
uint8_t SoftRcPulseOut::attach(int pinArg)
{
pin = pinArg;
angle = NO_ANGLE;
pin = pinArg;
angle = NO_ANGLE;
pulse0 = 0;
next = first;
first = this;
digitalWrite(pin,0);
pinMode(pin,OUTPUT);
return 1;
next = first;
first = this;
digitalWrite(pin, LOW);
pinMode(pin, OUTPUT);
return (1);
}
void SoftRcPulseOut::detach()
{
for ( SoftRcPulseOut **p = &first; *p != 0; p = &((*p)->next) ) {
if ( *p == this) {
*p = this->next;
this->next = 0;
return;
}
for ( SoftRcPulseOut **p = &first; *p != 0; p = &((*p)->next) )
{
if ( *p == this )
{
*p = this->next;
this->next = 0;
return;
}
}
}
void SoftRcPulseOut::write(int angleArg)
{
if ( angleArg < 0) angleArg = 0;
if ( angleArg < 0) angleArg = 0;
if ( angleArg > 180) angleArg = 180;
angle = angleArg;
// bleh, have to use longs to prevent overflow, could be tricky if always a 16MHz clock, but not true
// That 64L on the end is the TCNT0 prescaler, it will need to change if the clock's prescaler changes,
// but then there will likely be an overflow problem, so it will have to be handled by a human.
#ifdef TIMER0_TICK_EVERY_X_CYCLES
pulse0 = (min16*16L*clockCyclesPerMicrosecond() + (max16-min16)*(16L*clockCyclesPerMicrosecond())*angle/180L)/TIMER0_TICK_EVERY_X_CYCLES;
#ifdef MS_TIMER_TICK_EVERY_X_CYCLES
pulse0 = (min16 * 16L * clockCyclesPerMicrosecond() + (max16 - min16) * (16L * clockCyclesPerMicrosecond()) * angle / 180L) / MS_TIMER_TICK_EVERY_X_CYCLES;
#else
pulse0 = (min16*16L*clockCyclesPerMicrosecond() + (max16-min16)*(16L*clockCyclesPerMicrosecond())*angle/180L)/64L;
pulse0 = (min16 * 16L * clockCyclesPerMicrosecond() + (max16 - min16) * (16L * clockCyclesPerMicrosecond()) * angle / 180L) / 64L;
#endif
}
void SoftRcPulseOut::write_us(int PulseWidth_us)
void SoftRcPulseOut::write_us(uint16_t PulseWidth_us)
{
SoftRcPulseOut::write(map(PulseWidth_us,min16*16,max16*16,0,180));
if ( PulseWidth_us < (min16 * 16)) PulseWidth_us = (min16 * 16);
if ( PulseWidth_us > (max16 * 16)) PulseWidth_us = (max16 * 16);
#ifdef MS_TIMER_TICK_EVERY_X_CYCLES
pulse0 = (PulseWidth_us * clockCyclesPerMicrosecond()) / MS_TIMER_TICK_EVERY_X_CYCLES;
#else
pulse0 = (PulseWidth_us * clockCyclesPerMicrosecond()) / 64L;
#endif
angle = map(PulseWidth_us, min16 * 16, max16 * 16, 0, 180);
}
uint8_t SoftRcPulseOut::read()
@ -95,114 +107,123 @@ uint8_t SoftRcPulseOut::read()
return angle;
}
uint8_t SoftRcPulseOut::read_us()
uint16_t SoftRcPulseOut::read_us()
{
return map(angle,0,180,min16*16,max16*16);
#ifdef MS_TIMER_TICK_EVERY_X_CYCLES
return((pulse0 * MS_TIMER_TICK_EVERY_X_CYCLES) / clockCyclesPerMicrosecond());
#else
return((pulse0 * 64L) / clockCyclesPerMicrosecond());
#endif
}
uint8_t SoftRcPulseOut::attached()
{
for ( SoftRcPulseOut *p = first; p != 0; p = p->next ) {
if ( p == this) return 1;
}
return 0;
for ( SoftRcPulseOut *p = first; p != 0; p = p->next )
{
if ( p == this) return (1);
}
return (0);
}
uint8_t SoftRcPulseOut::refresh(bool force /* = false */)
{
uint8_t RefreshDone=0;
uint8_t count = 0, i = 0;
uint16_t base = 0;
SoftRcPulseOut *p;
static unsigned long lastRefresh = 0;
unsigned long m = millis();
if(!force)
{
// if we haven't wrapped millis, and 20ms have not passed, then don't do anything
if ( m >= lastRefresh && m < lastRefresh + 20) return(RefreshDone);
}
RefreshDone=1; //Ok: Refresh will be performed
lastRefresh = m;
uint8_t RefreshDone = 0;
uint8_t count = 0, i = 0;
uint16_t base = 0;
SoftRcPulseOut *p;
static uint32_t lastRefresh = 0;
uint32_t m = millis();
if(!force)
{
// if we haven't wrapped millis, and 20ms have not passed, then don't do anything
if ( (m - lastRefresh) < 20UL ) return(RefreshDone);
}
RefreshDone = 1; //Ok: Refresh will be performed
lastRefresh = m;
for ( p = first; p != 0; p = p->next ) if ( p->pulse0) count++;
if ( count == 0) return(RefreshDone);
for ( p = first; p != 0; p = p->next ) if ( p->pulse0 ) count++;
if ( count == 0 ) return(RefreshDone);
// gather all the SoftRcPulseOuts in an array
SoftRcPulseOut *s[count];
for ( p = first; p != 0; p = p->next ) if ( p->pulse0) s[i++] = p;
// gather all the SoftRcPulseOuts in an array
SoftRcPulseOut *s[count];
for ( p = first; p != 0; p = p->next ) if ( p->pulse0) s[i++] = p;
// bubblesort the SoftRcPulseOuts by pulse time, ascending order
s[0]->ItMasked=0;
for(;;)
{
uint8_t moved = 0;
for ( i = 1; i < count; i++)
{
s[i]->ItMasked=0;
if ( s[i]->pulse0 < s[i-1]->pulse0)
{
SoftRcPulseOut *t = s[i];
s[i] = s[i-1];
s[i-1] = t;
moved = 1;
}
}
if ( !moved) break;
// bubblesort the SoftRcPulseOuts by pulse time, ascending order
s[0]->ItMasked = 0;
for(;;)
{
uint8_t moved = 0;
for ( i = 1; i < count; i++ )
{
s[i]->ItMasked = 0;
if ( s[i]->pulse0 < s[i - 1]->pulse0 )
{
SoftRcPulseOut *t = s[i];
s[i] = s[i - 1];
s[i - 1] = t;
moved = 1;
}
}
for ( i = 1; i < count; i++)
if ( !moved ) break;
}
for ( i = 1; i < count; i++ )
{
if ( abs(s[i]->pulse0 - s[i - 1]->pulse0) <= 5)
{
s[i]->ItMasked = 1; /* 2 consecutive Pulses are close each other, so do not unmask interrupts between Pulses */
}
}
// turn on all the pins
// Note the timing error here... when you have many SoftwareServos going, the
// ones at the front will get a pulse that is a few microseconds too long.
// Figure about 4uS/SoftRcPulseOut after them. This could be compensated, but I feel
// it is within the margin of error of software SoftRcPulseOuts that could catch
// an extra interrupt handler at any time.
noInterrupts();
for ( i = 0; i < count; i++ ) digitalWrite( s[i]->pin, 1);
interrupts();
uint8_t start = SOFT_RC_PULSE_OUT_TCNT;
uint8_t now = start;
uint8_t last = now;
// Now wait for each pin's time in turn..
for ( i = 0; i < count; i++ )
{
uint16_t go = start + s[i]->pulse0;
#ifndef MS_TIMER_TICK_EVERY_X_CYCLES
uint16_t it = go - 4; /* 4 Ticks is OK for UNO @ 16MHz with default prescaler*/ /* Mask Interruptions just before setting down the pin */
#else
uint16_t it = go - max(4, (256 / MS_TIMER_TICK_EVERY_X_CYCLES)); /* 4 Ticks is OK for UNO @ 16MHz */ /* Mask Interruptions just before setting down the pin */
#endif
// loop until we reach or pass 'go' time: this is a blocking loop (max 2400us) except for non masked ISR (between edges)
for (;;)
{
now = SOFT_RC_PULSE_OUT_TCNT;
if ( now < last ) base += 256;
last = now;
if( !s[i]->ItMasked )
{
if( base + now > it)
{
if ( abs(s[i]->pulse0 - s[i-1]->pulse0)<=5)
{
s[i]->ItMasked=1; /* 2 consecutive Pulses are close each other, so do not unmask interrupts between Pulses */
}
noInterrupts();
s[i]->ItMasked = 1;
}
// turn on all the pins
// Note the timing error here... when you have many SoftwareServos going, the
// ones at the front will get a pulse that is a few microseconds too long.
// Figure about 4uS/SoftRcPulseOut after them. This could be compensated, but I feel
// it is within the margin of error of software SoftRcPulseOuts that could catch
// an extra interrupt handler at any time.
noInterrupts();
for ( i = 0; i < count; i++) digitalWrite( s[i]->pin, 1);
interrupts();
uint8_t start = SOFT_RC_PULSE_OUT_TCNT;
uint8_t now = start;
uint8_t last = now;
// Now wait for each pin's time in turn..
for ( i = 0; i < count; i++)
}
if ( base + now > go )
{
digitalWrite( s[i]->pin, 0);
if( (i + 1) < count )
{
uint16_t go = start + s[i]->pulse0;
uint16_t it = go - 4; /* 4 Ticks is OK for UNO @ 16MHz */ /* Mask Interruptions just before setting down the pin */
// loop until we reach or pass 'go' time
for (;;)
{
now = SOFT_RC_PULSE_OUT_TCNT;
if ( now < last) base += 256;
last = now;
if(!s[i]->ItMasked)
{
if( base + now > it)
{
noInterrupts();
s[i]->ItMasked=1;
}
}
if ( base + now > go)
{
digitalWrite( s[i]->pin,0);
if((i+1)<count)
{
if(!s[i+1]->ItMasked)
{
interrupts();
}
}else interrupts();
break;
}
}
}
return(RefreshDone);
if( !s[i + 1]->ItMasked )
{
interrupts();
}
}else interrupts();
break;
}
}
}
return(RefreshDone);
}

View File

@ -2,14 +2,16 @@
#define SoftRcPulseOut_h
/*
Update 01/03/2013: add support for DigiSpark (http://digistump.com): automatic Timer selection (RC Navy: p.loussouarn.free.fr)
Update 01/03/2013: add support for Digispark (http://digistump.com): automatic Timer selection (RC Navy: p.loussouarn.free.fr)
Update 19/08/2014: usage with write_us and read_us fixed and optimized for highest resolution
English: by RC Navy (2012)
=======
<SoftRcPulseOut>: a library mainly based on the <SoftwareServo> library, but with a better pulse generation to limit jitter.
It supports the same methods as <SoftwareServo>.
It also support Pulse Width order given in microseconds. The current Pulse Width can also be read in microseconds.
The refresh method can admit an optionnal argument (force). If SoftRcPulseOut::refresh(1) is called, the refresh is forced even if 20 ms are not elapsed.
The refresh() method returns 1 if refresh done (can be used for synchro and/or for 20ms timer).
http://p.loussouarn.free.fr
Francais: par RC Navy (2012)
@ -18,6 +20,7 @@
Elle supporte les memes methodes que <SoftwareServo>.
Elle supporte egalement une consigne de largeur d'impulsion passee en microseconde. La largeur de l'impulsion courante peut egalement etre lue en microseconde.
La methode refresh peut admettre un parametre optionnel (force). Si SoftRcPulseOut::resfresh(1) est appelee, le refresh est force meme si 20 ms ne se sont pas ecoulee.
La methode refresh() retourne 1 si refresh effectue (peut etre utilise pour synhro et/ou 20ms timer).
http://p.loussouarn.free.fr
*/
@ -32,28 +35,40 @@
class SoftRcPulseOut
{
private:
boolean ItMasked;
uint8_t pin;
uint8_t angle; // in degrees
uint16_t pulse0; // pulse width in TCNT0 counts
uint8_t min16; // minimum pulse, 16uS units (default is 34)
uint8_t max16; // maximum pulse, 16uS units, 0-4ms range (default is 150)
class SoftRcPulseOut *next;
static SoftRcPulseOut* first;
boolean ItMasked;
uint8_t pin;
uint8_t angle; // in degrees
uint16_t pulse0; // pulse width in TCNT0 counts
uint8_t min16; // minimum pulse, 16uS units (default is 34)
uint8_t max16; // maximum pulse, 16uS units, 0-4ms range (default is 150)
class SoftRcPulseOut *next;
static SoftRcPulseOut *first;
public:
SoftRcPulseOut();
uint8_t attach(int); // attach to a pin, sets pinMode, returns 0 on failure, won't
// position the servo until a subsequent write() happens
void detach();
void write(int); // specify the angle in degrees, 0 to 180
void write_us(int); // specify the angle in microseconds, 500 to 2500
uint8_t read(); // return the current angle
uint8_t read_us(); // return the current pulse with in microseconds
uint8_t attached();
void setMinimumPulse(uint16_t); // pulse length for 0 degrees in microseconds, 540uS default
void setMaximumPulse(uint16_t); // pulse length for 180 degrees in microseconds, 2400uS default
static uint8_t refresh(bool force = false); // must be called at least every 50ms or so to keep servo alive
// you can call more often, it won't happen more than once every 20ms
uint8_t attach(int); // attach to a pin, sets pinMode, returns 0 on failure, won't
// position the servo until a subsequent write() happens
void detach();
void write(int); // specify the angle in degrees, 0 to 180
void write_us(uint16_t); // specify the angle in microseconds, 500 to 2500
uint8_t read(); // return the current angle
uint16_t read_us(); // return the current pulse with in microseconds
uint8_t attached();
void setMinimumPulse(uint16_t); // pulse length for 0 degrees in microseconds, 540uS default
void setMaximumPulse(uint16_t); // pulse length for 180 degrees in microseconds, 2400uS default
static uint8_t refresh(bool force = false);// must be called at least every 50ms or so to keep servo alive
// you can call more often, it won't happen more than once every 20ms
};
/* Methodes en Francais English native methods */
#define attache attach
#define detache detach
#define ecrit write
#define ecrit_us write_us
#define lit read
#define lit_us read_us
#define estAttache attached
#define definitImpulsionMinimum setMinimumPulse
#define definitImpulsionMaximum setMaximumPulse
#define rafraichit refresh
#endif

View File

@ -19,6 +19,8 @@ SoftRcPulseOut myservo; // create servo object to control a servo
#define REFRESH_PERIOD_MS 20
#define NOW 1
int val; // variable to read the value from the analog pin
void setup()
@ -32,6 +34,6 @@ void loop()
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180)
myservo.write(val); // sets the servo position according to the scaled value
delay(REFRESH_PERIOD_MS); // waits for the servo to get there
SoftRcPulseOut::refresh(); // generates the servo pulse
SoftRcPulseOut::refresh(NOW); // generates the servo pulse Now
}

View File

@ -52,7 +52,7 @@ boolean LedState=HIGH;
void setup()
{
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__)
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny167__)
Serial.begin(9600);
Serial.print("SoftRcPulseIn library V");Serial.print(SoftRcPulseIn::LibTextVersionRevision());Serial.print(" demo"); /* For arduino UNO which has an hardware UART, display the library version in the console */
#endif
@ -74,8 +74,8 @@ static uint16_t Width_us=NEUTRAL_US; /* Static to keep the value at the next loo
ServoMotor[INVERTED].write_us((NEUTRAL_US*2)-Width_us); /* Inverted Signal */
SoftRcPulseOut::refresh(NOW); /* NOW argument (=1) allows to synchronize outgoing pulses with incoming pulses */
RxPulseStartMs=millis(); /* Restart the Chrono for Pulse */
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__)
Serial.print("Pulse=");Serial.println(Largeur_us); /* For arduino UNO which has an hardware UART, display the library version in the console */
#if !defined(__AVR_ATtiny24__) && !defined(__AVR_ATtiny44__) && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny25__) && !defined(__AVR_ATtiny45__) && !defined(__AVR_ATtiny85__) && !defined(__AVR_ATtiny167__)
Serial.print("Pulse=");Serial.println(Width_us); /* For arduino UNO which has an hardware UART, display the library version in the console */
#endif
}
else

View File

@ -12,6 +12,8 @@ SoftRcPulseOut myservo; // create servo object to control a servo
#define REFRESH_PERIOD_MS 20
#define NOW 1
int pos = 0; // variable to store the servo position
void setup()
@ -26,12 +28,12 @@ void loop()
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(REFRESH_PERIOD_MS); // waits 20ms for refresh period
SoftRcPulseOut::refresh(1); // generates the servo pulse
SoftRcPulseOut::refresh(NOW); // generates the servo pulse Now
}
for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(REFRESH_PERIOD_MS); // waits 20ms for for refresh period
SoftRcPulseOut::refresh(1); // generates the servo pulse
SoftRcPulseOut::refresh(NOW); // generates the servo pulse Now
}
}

View File

@ -0,0 +1,37 @@
// SweepNoDelay
// by RC Navy (http://p.loussouarn.free.fr/arduino/arduino.html>)
// This sketch can work with ATtiny and Arduino UNO, MEGA, etc...
// This example code is in the public domain.
#include <SoftRcPulseOut.h>
SoftRcPulseOut myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created
#define SERVO_PIN 0
#define UP_DIRECTION +1
#define DOWN_DIRECTION -1
#define POS_MIN 0
#define POS_MAX 180
int pos = POS_MIN; // variable to store the servo position
int step = UP_DIRECTION;
void setup()
{
myservo.attach(SERVO_PIN); // attaches the servo on pin defined by SERVO_PIN to the servo object
myservo.write(pos);
}
void loop()
{
if (SoftRcPulseOut::refresh()) // refresh() returns 1 every 20ms (after pulse update)
{
// We arrive here every 20ms
pos += step;
if(pos >= POS_MAX) step = DOWN_DIRECTION; //180 degrees reached -> Change direction
if(pos <= POS_MIN) step = UP_DIRECTION; // 0 degrees reached -> Change direction
myservo.write(pos);
}
}

View File

@ -19,6 +19,7 @@ SoftRcPulseOut myservo; // create servo object to control a servo
#define REFRESH_PERIOD_MS 20
#define NOW 1
#define MOY_SUR_1_VALEUR 0
#define MOY_SUR_2_VALEURS 1
@ -46,8 +47,8 @@ static int ValMoyennee;
val = analogRead(POT_PIN); // reads the value of the potentiometer (value between 0 and 1023)
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180)
MOYENNE(ValMoyennee,val,TAUX_DE_MOYENNAGE);//If there is lots of noise: average with TAUX_DE_MOYENNAGE
myservo.write(ValMoyennee); // sets the servo position according to the scaled value
myservo.write(ValMoyennee); // sets the servo position according to the scaled value
delay(REFRESH_PERIOD_MS); // waits for the servo to get there
SoftRcPulseOut::refresh(); // generates the servo pulse
SoftRcPulseOut::refresh(NOW); // generates the servo pulse
}

View File

@ -6,21 +6,31 @@
# Datatypes (KEYWORD1)
#######################################
SoftRcPulseOut KEYWORD1
SoftRcPulseOut KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
attach KEYWORD2
detach KEYWORD2
write KEYWORD2
write_us KEYWORD2
read KEYWORD2
read_us KEYWORD2
attached KEYWORD2
setMinimumPulse KEYWORD2
setMaximumPulse KEYWORD2
refresh KEYWORD2
attach KEYWORD2
attache KEYWORD2
detach KEYWORD2
detache KEYWORD2
write KEYWORD2
ecrit KEYWORD2
write_us KEYWORD2
ecrit_us KEYWORD2
read KEYWORD2
lit KEYWORD2
read_us KEYWORD2
lit_us KEYWORD2
attached KEYWORD2
estAttache KEYWORD2
setMinimumPulse KEYWORD2
definitImpulsionMinimum KEYWORD2
setMaximumPulse KEYWORD2
definitImpulsionMaximum KEYWORD2
refresh KEYWORD2
rafraichit KEYWORD2
#######################################
# Constants (LITERAL1)

View File

@ -0,0 +1,64 @@
SoftSerial library
==================
The **SoftSerial** library is exactly the same as the **SoftwareSerial** library but used with the **TinyPinChange** library which allows to share
the "Pin Change Interrupt" Vector.
**SoftwareSerial** monopolizes the Pin Change Interrupt Vector and do not allow sharing.
With **SoftSerial**, it's possible. Don't forget to #include **TinyPinChange** in your sketch!
Additionally, for small devices such as **ATtiny85** (Digispark), it's possible to declare **the same pin for TX and RX**.
Data direction is set by using the new **txMode()** and **rxMode()** methods.
Some examples of use cases:
-------------------------
* **half-duplex bi-directional serial port on a single wire for debuging purpose**
* **half-duplex serial port to interface with Bluetooth module**
* **half-duplex serial port to interconnect an arduino with another one**
Supported Arduinos:
------------------
* **ATmega368 (UNO)**
* **ATmega2560 (MEGA)**
* **ATtiny84 (Standalone)**
* **ATtiny85 (Standalone or Digispark)**
* **ATtiny167 (Digispark pro)**
Tip and Tricks:
--------------
Develop your project on an arduino UNO or MEGA, and then shrink it by loading the sketch in an ATtiny or Digispark (pro).
API/methods:
-----------
* The **SoftSerial** library uses the same API as the regular **SoftwareSerial** library:
* begin()
* end()
* available()
* read()
* listen()
* isListening()
* overflow()
* flush()
* Two additional methods are used to manage the serial port on a single pin:
* txMode()
* rxMode()
Design considerations:
---------------------
The **SoftSerial** library relies the **TinyPinChange** library **for the RX pin**. This one shall be included in the sketch as well.
On the arduino MEGA, as all the pins do not support "pin change interrupt", only the following pins are supported **for the RX pin**:
* 10 -> 15
* 50 -> 53
* A8 -> A15
On other devices (ATmega328, ATtiny84, ATtiny85 and ATtiny167), all the pins are usable.
Contact
-------
If you have some ideas of enhancement, please contact me by clicking on: [RC Navy](http://p.loussouarn.free.fr/contact.html).

View File

@ -4,8 +4,8 @@ the Pin Change Interrupt Vector.
<SoftwareSerial> monopolizes the Pin Change Interrupt Vector and don't allow sharing.
With <SoftSerial>, it's possible. Don't forget to #include <TinyPinChange> in your sketch!
Additionally, for small devices such as ATtiny85 (Digispark), it's possible to declare the same pin for TX and RX.
Data direction is set by using the new txMode() and rxMode methods.
RC Navy (2012-2013): http://p.loussouarn.free.fr
Data direction is set by using the new txMode() and rxMode() methods.
RC Navy (2012-2015): http://p.loussouarn.free.fr
SoftwareSerial.cpp (formerly NewSoftSerial.cpp) -
Multi-instance software serial library for Arduino/Wiring
@ -41,8 +41,15 @@ http://arduiniana.org.
// oscilloscope or logic analyzer. Beware: it also slightly modifies
// the bit times, so don't rely on it too much at high baud rates
#define _DEBUG 0
#define _DEBUG_PIN1 11
#define _DEBUG_PIN2 13
#define _DEBUG_PIN1 0//11
#define _DEBUG_PIN2 1//13
#define FAST_DEBUG //less intrusive
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
//
// Includes
//
@ -67,46 +74,41 @@ typedef struct _DELAY_TABLE
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 17, 17, 12, },
{ 57600, 10, 37, 37, 33, },
{ 38400, 25, 57, 57, 54, },
{ 31250, 31, 70, 70, 68, },
{ 28800, 34, 77, 77, 74, },
{ 19200, 54, 117, 117, 114, },
{ 14400, 74, 156, 156, 153, },
{ 9600, 114, 236, 236, 233, },
{ 115200, 0, 14, 14, 12, },
{ 57600, 5, 34, 34, 32, },
{ 38400, 15, 54, 54, 52, },
{ 31250, 23, 67, 67, 65, },/* By interpolation */
{ 28800, 26, 74, 74, 72, },/* By interpolation */
{ 19200, 44, 113, 113, 112, },
{ 14400, 74, 156, 153, 153, },/* By interpolation */
{ 9600, 114, 234, 234, 233, },
{ 4800, 233, 474, 474, 471, },
{ 2400, 471, 950, 950, 947, },
{ 1200, 947, 1902, 1902, 1899, },
{ 2400, 471, 940, 940, 945, },
{ 1200, 947, 1902, 1902, 1895, },
{ 300, 3804, 7617, 7617, 7614, },
};
const int XMIT_START_ADJUSTMENT = 5;
//PL{ table from http://digistump.com/board/index.php/topic,212.msg1214/topicseen.html#msg1214
const int XMIT_START_ADJUSTMENT = 0;
#elif F_CPU == 16500000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 18, 18, 12, },
{ 57600, 10, 38, 38, 34, },
{ 38400, 26, 59, 59, 56, },
{ 31250, 32, 72, 72, 70, },
{ 28800, 35, 79, 79, 76, },
{ 19200, 56, 121, 121, 118, },
{ 14400, 76, 161, 161, 158, },
{ 9600, 118, 243, 243, 240, },
{ 4800, 240, 489, 489, 486, },
{ 2400, 486, 980, 980, 977, },
{ 1200, 977, 1961, 1961, 1958, },
{ 115200, 0, 15, 15, 13, },
{ 57600, 3, 35, 35, 33, },
{ 38400, 12, 56, 56, 54, },
{ 31250, 32, 72, 72, 70, },/* By interpolation */
{ 28800, 35, 79, 79, 76, },/* By interpolation */
{ 19200, 52, 118, 118, 116, },
{ 14400, 76, 161, 161, 158, },/* By interpolation */
{ 9600, 118, 241, 241, 238, },
{ 4800, 240, 487, 487, 485, },
{ 2400, 486, 976, 976, 974, },
{ 1200, 977, 1961, 1961, 1956, },
{ 600, 1961, 3923, 3923, 3919, },
{ 300, 3923, 7855, 7855, 7852, },
};
const int XMIT_START_ADJUSTMENT = 5;
//PL}
const int XMIT_START_ADJUSTMENT = 0;
#elif F_CPU == 8000000
static const DELAY_TABLE table[] PROGMEM =
@ -154,7 +156,7 @@ const int XMIT_START_ADJUSTMENT = 6;
#else
#error This version of SoftSerial supports only 20, 16 and 8MHz processors
#error This version of SoftSerial supports only 20, 16, 16.5 and 8MHz processors
#endif
@ -171,9 +173,12 @@ volatile uint8_t SoftSerial::_receive_buffer_head = 0;
//
// This function generates a brief pulse
// for debugging or measuring on an oscilloscope.
#if _DEBUG
#if defined(FAST_DEBUG)
#define DebugPulse(a, bit) sbi(PINB, bit)
#else
inline void DebugPulse(uint8_t pin, uint8_t count)
{
#if _DEBUG
volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin));
uint8_t val = *pport;
@ -182,9 +187,14 @@ inline void DebugPulse(uint8_t pin, uint8_t count)
*pport = val | digitalPinToBitMask(pin);
*pport = val;
}
#endif
}
#endif
#else
// no debug
inline void DebugPulse(uint8_t pin, uint8_t count)
{
}
#endif
//
// Private methods
//
@ -369,7 +379,6 @@ SoftSerial::SoftSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_log
_buffer_overflow(false),
_inverse_logic(inverse_logic)
{
// setTX(transmitPin);
setRX(receivePin);
setTX(transmitPin);
TinyPinChange_RegisterIsr(receivePin, SoftSerial::handle_interrupt);

View File

@ -3,7 +3,7 @@
the Pin Change Interrupt Vector.
<SoftwareSerial> monopolizes the Pin Change Interrupt Vector and don't allow sharing.
With <SoftSerial>, it's possible. Don't forget to #include <TinyPinChange> in your sketch!
RC Navy (2012): http://p.loussouarn.free.fr
RC Navy (2012-2015): http://p.loussouarn.free.fr
SoftwareSerial.h (formerly NewSoftSerial.h) -
Multi-instance software serial library for Arduino/Wiring

View File

@ -43,10 +43,12 @@
#define DEBUG_TX_RX_PIN 2 //Adjust here your Tx/Rx debug pin
SoftSerial MyDbgSerial(DEBUG_TX_RX_PIN, DEBUG_TX_RX_PIN, true); //true allows to connect to a regular RS232 without RS232 line driver
#define SERIAL_BAUD_RATE 57600 //Adjust here the serial rate (57600 is the higher supported rate)
void setup()
{
MyDbgSerial.begin(38400); //After MyDbgSerial.begin(), the serial port is in rxMode by default
MyDbgSerial.begin(SERIAL_BAUD_RATE); //After MyDbgSerial.begin(), the serial port is in rxMode by default
MyDbgSerial.txMode(); //Before sending a message, switch to txMode
MyDbgSerial.println(F("\nDebug enabled"));
MyDbgSerial.rxMode(); //switch to rxMode to be ready to receive some commands

View File

@ -0,0 +1,60 @@
TinyPinChange library
=====================
**TinyPinChange** is an asynchronous (interrupt-driven) library designed to detect event (rising or falling edege) on pins.
Very often in the arduino world, users complain about conflicts between libraries.
This **TinyPinChange** library allows to share the "pin change interrupt" vector for several "clients".
For example, it's possible to use the **SoftRcPulseIn** library whilst using the **SoftSerial** library: both libraries rely on the **TinyPinChange** library.
Some examples of use cases:
-------------------------
* **Event detector** (on pins)
* **Frequency meter**
* **Pulse width meter**
* **Tachometer**
* **Duty cycle measurement**
* **Software serial port** (see **SoftSerial** library which relies on TinyPinChange)
Supported Arduinos:
------------------
* **ATmega368 (UNO)**
* **ATmega2560 (MEGA)**
* **ATtiny84 (Standalone)**
* **ATtiny85 (Standalone or Digispark)**
* **ATtiny167 (Digispark pro)**
Tip and Tricks:
--------------
Develop your project on an arduino UNO or MEGA, and then shrink it by loading the sketch in an ATtiny or Digispark (pro).
API/methods:
-----------
* TinyPinChange_Init()
* TinyPinChange_RegisterIsr()
* TinyPinChange_EnablePin()
* TinyPinChange_DisablePin()
* TinyPinChange_GetPortEvent()
* TinyPinChange_GetCurPortSt()
* TinyPinChange_PinToMsk()
* TinyPinChange_Edge()
* TinyPinChange_RisingEdge
* TinyPinChange_FallingEdge
Design considerations:
---------------------
On the arduino MEGA, as all the pins do not support "pin change interrupt", only the following pins are supported:
* 10 -> 15
* 50 -> 53
* A8 -> A15
On other devices (ATmega328, ATtiny84, ATtiny85 and ATtiny167), all the pins are usable.
Contact
-------
If you have some ideas of enhancement, please contact me by clicking on: [RC Navy](http://p.loussouarn.free.fr/contact.html).

View File

@ -70,7 +70,7 @@ void setup()
{
TinyPinChange_Init();
MySerial.begin(38400); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
MySerial.begin(57600); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
VirtualPortNb = TinyPinChange_RegisterIsr(FIRST_INPUT, InterruptFunctionToCall);
VirtualPortNb_ = TinyPinChange_RegisterIsr(SECOND_INPUT, InterruptFunctionToCall);
@ -118,7 +118,7 @@ uint16_t LocalFirstInputChangeCount;
uint16_t LocalSecondInputChangeCount;
/* Blink the built-in LED */
if(millis() - LedStartMs >= 500)
if(millis() - LedStartMs >= 500UL)
{
LedStartMs = millis();
digitalWrite(LED_PIN, State);
@ -141,7 +141,7 @@ uint16_t LocalSecondInputChangeCount;
}
/* Diplay Transition numbers every second */
if((millis() - DisplayStartMs >= 1000) && DisplayEnabled)
if((millis() - DisplayStartMs >= 1000UL) && DisplayEnabled)
{
DisplayStartMs = millis();
noInterrupts(); /* Mandatory since counters are 16 bits */

View File

@ -70,7 +70,7 @@ void setup()
{
TinyPinChange_Init();
MySerial.begin(38400); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
MySerial.begin(57600); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
VirtualPortNb = TinyPinChange_RegisterIsr(FIRST_INPUT, InterruptFunctionToCall);
VirtualPortNb_ = TinyPinChange_RegisterIsr(SECOND_INPUT, InterruptFunctionToCall);
@ -118,7 +118,7 @@ uint16_t LocalFirstInputChangeCount;
uint16_t LocalSecondInputChangeCount;
/* Blink the built-in LED */
if(millis() - LedStartMs >= 500)
if(millis() - LedStartMs >= 500UL)
{
LedStartMs = millis();
digitalWrite(LED_PIN, State);
@ -141,7 +141,7 @@ uint16_t LocalSecondInputChangeCount;
}
/* Diplay Transition numbers every second */
if((millis() - DisplayStartMs >= 1000) && DisplayEnabled)
if((millis() - DisplayStartMs >= 1000UL) && DisplayEnabled)
{
DisplayStartMs = millis();
noInterrupts(); /* Mandatory since counters are 16 bits */

View File

@ -70,7 +70,7 @@ void setup()
{
TinyPinChange_Init();
MySerial.begin(38400); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
MySerial.begin(57600); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
VirtualPortNb = TinyPinChange_RegisterIsr(FIRST_INPUT, InterruptFunctionToCall);
VirtualPortNb_ = TinyPinChange_RegisterIsr(SECOND_INPUT, InterruptFunctionToCall);
@ -118,7 +118,7 @@ uint16_t LocalFirstInputChangeCount;
uint16_t LocalSecondInputChangeCount;
/* Blink the built-in LED */
if(millis() - LedStartMs >= 500)
if(millis() - LedStartMs >= 500UL)
{
LedStartMs = millis();
digitalWrite(LED_PIN, State);
@ -141,7 +141,7 @@ uint16_t LocalSecondInputChangeCount;
}
/* Diplay Transition numbers every second */
if((millis() - DisplayStartMs >= 1000) && DisplayEnabled)
if((millis() - DisplayStartMs >= 1000UL) && DisplayEnabled)
{
DisplayStartMs = millis();
noInterrupts(); /* Mandatory since counters are 16 bits */

View File

@ -0,0 +1,440 @@
/*
_____ ____ __ _ ____ _ _ _ _
| __ \ / __ \ | \ | | / __ \ | | | | | | | |
| |__| | | / \_| | . \ | | / / \ \ | | | | \ \ / /
| _ / | | _ | |\ \| | | |__| | | | | | \ ' /
| | \ \ | \__/ | | | \ ' | | __ | \ \/ / | |
|_| \_\ \____/ |_| \__| |_| |_| \__/ |_| 2013/2014
http://p.loussouarn.free.fr
*******************************************************
* <TinyPinChange> library Demo *
* with display capabilities using *
* <SoftSerial> object as single wire serial interface *
*******************************************************
This "Tiny RC Scope" sketch demonstrates how to use <TinyPinChange> and <SoftSerial> libraries.
"Tiny RC Scope" acts as a simple real time ASCII oscilloscope for displaying one RC Channel in the serial console.
The displayed measurement (in µs) are: pulse width and RC period.
Trick: By connecting Pin#1 to Pin#0, through a 1K resistor, you can measure the RC Signal provided by the built-in RC generator for testing purpose.
Output results are sent to a software serial port. If a real RC signal is connected to Pin0, the trace is displayed in real time in the terminal.
And the great thing is: using a <SoftSerial> object as a bi-directionnal software serial port (half-duplex) on a single pin to communicate with the outside world!
To display the sketch results on a PC (in a Terminal):
1) Build the "Serial One Wire Debug Cable" and plug it to the regular RS232 port as depicted below.
2) Open your favorite Terminal at 57600,n,8,1: HyperTerminal, Teraterm (Windows) or Minicom, GtkTerm (Linux) and CoolTerm (MAC) does the trick.
3) You can also use the Serial Monitor of the arduino IDE: Tools->Serial Port and select your RS232 port (may be an USB virtual port), Rate=57600.
4) To test "Tiny RC Scope", connect Pin1 to Pin0, and look at the Terminal (57600,n,8,1) connected to Pin2 through a debug cable ()
5) The wave form should be displayed in the Terminal,
6) Type - to decrease the pulse width (-10us),
7) Type + to increase the pulse width (+10us),
8) Type m to set the pulse width to its minimum (500us),
9) Type n or N to set the pulse width to its Neutral value (1500us),
10) Type M to set the pulse width to its Maximum (2500us),
11) To measure real RC signals, disconnect the Pin1 from Pin0 and connect a RC receiver output to Pin0,
12) The Terminal will display in real time the pulse width of the connected RC channel.
13) If the channel is not connected, a flat line is displayed.
SERIAL ONE WIRE
DEBUGGING CABLE
_______________ ________________
/ \___/\___/ \
____
.--------. | \
| GND |--------------------------------+---o5 \
| | 47K | | 9o |
| | .--###--' | o4 |
| DEBUG | 4.7K | | 8o |
| TX_RX |-------------------###--+--|<|------o3 | ---> To regular RS232 SubD 9 pins Male of PC or Serial/USB adapter
| PIN | ^ | 1N4148 | 7o |
| | | '-----------o2 |
'--------' | | 6o |
ATtiny85 Single | o1 /
(Digispark) I/O |____/
(pro) SubD 9 pins
Female
*/
#include <TinyPinChange.h>
#include <SoftSerial.h>
#define RC_CHANNEL_PIN 0 /* RC Channel is connected to pin 0 */
#define RC_GEN_PIN 1 /* Pin used as internal RC generator for test purpose */
#define RC_PINS_MSK (_BV(RC_CHANNEL_PIN) | _BV(RC_GEN_PIN))
#define DEBUG_TX_RX_PIN 2
#define DEF_TEST_RC_CH_WIDTH_US 1500 /* This value can be change via the Terminal */
#define TEST_RC_PERIOD_US 20000
#define PULSE_MAX_US 2500
#define PULSE_MIN_US 500
#define STEP_US 10
#define ERR_MARGIN_US 150
#define SERIAL_BAUD_RATE 57600 /* 57600 is the maximum for Receiving commands from the serial port: 1 char -> #200us */
SoftSerial MySerial(DEBUG_TX_RX_PIN, DEBUG_TX_RX_PIN, true); /* Tx/Rx on a single Pin !!! (Pin#2) */
#define ONE_CHAR_TX_TIME_US 200 /* @ 57600 bauds */
enum {PULSE_SRC_INTERNAL, PULSE_SRC_EXTERNAL};
typedef struct {
uint32_t RisingStartUs;
uint32_t RcWidth_us;
uint32_t LastRxPulseMs;
boolean FallingEdgeFound;
}RcChSt_t;
volatile RcChSt_t Ch; /* volatile, since value are used in ISR and in the loop() */
volatile uint32_t RcPeriod_us = TEST_RC_PERIOD_US;
uint32_t TestRcWidth_us = DEF_TEST_RC_CH_WIDTH_US;
/* The different states of the display state machine */
enum {DISP_COMPUTE, DISP_FIRST_LINE, DISP_PREP_SEC_LINE, DISP_SECOND_LINE, DISP_PREP_THIRD_LINE, DISP_THIRD_LINE, DISP_WAIT};
#define LINE_LEN 38
typedef struct{
char Line[LINE_LEN];
uint8_t Idx;
uint8_t State;
uint8_t HighNb;
}DispSt_t;
static DispSt_t Disp;
volatile uint8_t IntRcSynch = 0;
uint8_t VirtualPortIdx;
/*
RC Signal
____ ____
_| |____________________________| |_
<---->
Width_us
<-------------------------------->
Period_us
*/
void setup()
{
TinyPinChange_Init();
MySerial.begin(SERIAL_BAUD_RATE); /* Trick: use a "high" data rate (less time wasted in ISR and for transmitting each character) */
Disp.State = DISP_COMPUTE;
VirtualPortIdx = TinyPinChange_RegisterIsr(RC_CHANNEL_PIN, InterruptFunctionToCall); /* As all pins are on the same port, a single ISR is needed */
pinMode(RC_CHANNEL_PIN, INPUT);
digitalWrite(RC_CHANNEL_PIN, HIGH); /* Enable Pull-up to avoid floating inputs in case of nothing connected to them */
TinyPinChange_EnablePin(RC_CHANNEL_PIN);
Ch.RcWidth_us = 0;
Ch.FallingEdgeFound = 0;
MySerial.txMode();
MySerial.println(F("\n -- Tiny RC Scope V1.0 (C) RC Navy 2014 --\n"));
MySerial.rxMode(); /* Switch to Rx Mode */
pinMode(RC_GEN_PIN, OUTPUT);
}
void loop()
{
uint32_t RcGeneStartUs = micros();
static uint32_t ProcessStartUs = micros();
static uint32_t DisplayStartMs = millis();
uint32_t ProcessDurationUs;
uint32_t HalfRemaingLowUs;
char RxChar;
/* Blink the built-in LED (Built-in RC Signal generator) */
if(IsInternalRcSrc())
{
RcGeneStartUs = micros();
digitalWrite(RC_GEN_PIN, HIGH);
while(micros() - RcGeneStartUs < TestRcWidth_us);
digitalWrite(RC_GEN_PIN, LOW);
}
/********************/
/* Start of process */
/********************/
ProcessStartUs = micros();
DisplayRcMeasurement(5000U);/* Gives 5000 us to display a part of the trace */
/* Get command from single wire SoftSerial (to tune the built-in generator) */
if(MySerial.available() > 0)
{
RxChar = MySerial.read();MySerial.txMode();MySerial.println("");MySerial.rxMode(); /* Carriage return after the echo */
switch(RxChar)
{
case '-': /* Decrease Built-in RC Pulse */
if((TestRcWidth_us - STEP_US) >= PULSE_MIN_US)
{
TestRcWidth_us -= STEP_US;
}
break;
case '+': /* Increase Built-in RC Pulse */
if(TestRcWidth_us + STEP_US <= PULSE_MAX_US)
{
TestRcWidth_us += STEP_US;
}
break;
case 'm': /* Set Built-in RC Pulse o min value: 500 */
TestRcWidth_us = PULSE_MIN_US;
break;
case 'N': /* Set Built-in RC Pulse to Neutral: 1500 */
case 'n':
TestRcWidth_us = DEF_TEST_RC_CH_WIDTH_US;
break;
case 'M': /* Set Built-in RC Pulse to Max value: 2500 */
TestRcWidth_us = PULSE_MAX_US;
break;
default:
/* Ignore */
break;
}
}
/********************/
/* End of process */
/********************/
ProcessDurationUs = micros() - ProcessStartUs; //Compute how many us took the previous instructions
if(IsInternalRcSrc())
{
HalfRemaingLowUs = TEST_RC_PERIOD_US - (ProcessDurationUs + TestRcWidth_us + 100UL);
RcGeneStartUs = micros();
while((micros() - RcGeneStartUs) < HalfRemaingLowUs);
}
}
/*
RC Signal
____ ____
_| |____________________________| |_
<---->
Width_us
<-------------------------------->
Period_us
<-------------------->
Display is processed here
Explanation: the display of the trace is performed just after the falling edge of the RC pulse
As the duration between 2 pulses is too short to display the full trace, the trace is displayed part by part.
A full trace is composed of around 100 characters:
At 57600 bauds, one character takes (1/57600) * 10 = 174 us. As there are some overhead, it is closer than 200us.
So, the full trace takes 100 x 200 = 20000 us = 20 ms. It's impossible to display the full trace between 2 consecutive pulses.
DisplayRcMeasurement() function is the very tricky part of this sketch: it has as argument an amount of time and exits before
it exceeds it. A state machine is used to memorize where the display was arrived.
*/
uint8_t DisplayRcMeasurement(uint16_t FreetimeUs)
{
static uint32_t LocalRcWidth_us;
static uint32_t LocalRcPeriod_us;
static uint32_t StartWaitMs;
char *Ptr;
uint32_t ProcessStart_us;
uint32_t Elapsed_us;
uint8_t StartIdx, Idx;
uint8_t Ret = 0;
switch(Disp.State)
{
case DISP_COMPUTE:
DispCompute:
if(Ch.FallingEdgeFound)
{
DispComputeNoSignal:
ProcessStart_us = micros();
Ch.FallingEdgeFound = 0;
noInterrupts(); /* Mandatory since RcWidth_us and RcPeriod_us are 32 bits */
LocalRcWidth_us = Ch.RcWidth_us;
LocalRcPeriod_us = RcPeriod_us;
interrupts();
if(LocalRcWidth_us < (PULSE_MIN_US - ERR_MARGIN_US) || LocalRcWidth_us > (PULSE_MAX_US + ERR_MARGIN_US)) LocalRcWidth_us = 0; /* Out of Range */
Disp.HighNb = (LocalRcWidth_us + 50UL) / 100;
strcpy_P(Disp.Line, PSTR("Ch(P0)__"));
if(LocalRcWidth_us)
{
Ptr = Disp.Line + 8;
for(Idx = 0; Idx < Disp.HighNb; Idx++) *Ptr++='_';*Ptr++='\n';*Ptr=0;
}
else Disp.Line[6] = 0;
Elapsed_us = micros() - ProcessStart_us;
FreetimeUs -= (uint16_t)Elapsed_us;
Disp.Idx=0;
Disp.State = DISP_FIRST_LINE;
if(FreetimeUs >= ONE_CHAR_TX_TIME_US)
{
goto DispFirstLine;
}
}
else
{
if(millis() - StartWaitMs >= 1000UL)
{
LocalRcWidth_us = 0;
goto DispComputeNoSignal;
}
}
break;
case DISP_PREP_SEC_LINE:
DispPrepSecLine:
/* Prepare second line */
ProcessStart_us = micros();
if(LocalRcWidth_us)
{
strcpy_P(Disp.Line, PSTR("_____/"));
for(Idx = 0; Idx < (2 + Disp.HighNb); Idx++) Disp.Line[6 + Idx] = ' ';
itoa(LocalRcWidth_us, Disp.Line + 6, 10);StartIdx = 9; if(LocalRcWidth_us >= 1000) StartIdx++;Disp.Line[StartIdx++] = 'u';Disp.Line[StartIdx++] = 's';
StartIdx = 6 + 2 + Disp.HighNb;Disp.Line[StartIdx++] = '\\';
}
else StartIdx = 0;
for(Idx = 0; (StartIdx + Idx ) < (LINE_LEN - 2); Idx++) Disp.Line[StartIdx + Idx] = '_';Disp.Line[StartIdx + Idx] = '\n';Disp.Line[++StartIdx + Idx] = 0;
Elapsed_us = micros() - ProcessStart_us;
FreetimeUs -= (uint16_t)Elapsed_us;
Disp.Idx=0;
Disp.State = DISP_SECOND_LINE;
if(FreetimeUs >= ONE_CHAR_TX_TIME_US)
{
goto DispSecondLine;
}
break;
case DISP_PREP_THIRD_LINE:
DispPrepThirdLine:
/* Prepare third line */
ProcessStart_us = micros();
strcpy_P(Disp.Line, PSTR(" RC Period: "));
if(LocalRcWidth_us)
{
itoa(LocalRcPeriod_us, Disp.Line + 20, 10);
strcat_P(Disp.Line, PSTR("us"));
}
else strcat_P(Disp.Line, PSTR("???"));
Elapsed_us = micros() - ProcessStart_us;
FreetimeUs -= (uint16_t)Elapsed_us;
Disp.Idx=0;
Disp.State = DISP_THIRD_LINE;
if(FreetimeUs >= ONE_CHAR_TX_TIME_US)
{
goto DispThirdLine;
}
break;
case DISP_FIRST_LINE:
case DISP_SECOND_LINE:
case DISP_THIRD_LINE:
DispFirstLine:
DispSecondLine:
DispThirdLine:
ProcessStart_us = micros();
MySerial.txMode();
while(1)
{
if(Disp.Line[Disp.Idx])
{
MySerial.print(Disp.Line[Disp.Idx++]);
if (micros() - ProcessStart_us >= (FreetimeUs - ONE_CHAR_TX_TIME_US))
{
MySerial.rxMode();
break; /* exit while(1) and stay in the current state */
}
}
else
{
switch(Disp.State)
{
case DISP_FIRST_LINE:
/* First line is fully displayed */
Disp.State = DISP_PREP_SEC_LINE;
if(FreetimeUs - Elapsed_us >= 200)
{
FreetimeUs -= (uint16_t)Elapsed_us;
goto DispPrepSecLine;
}
else
{
/* Not enough time: just change state */
MySerial.rxMode();
}
break;
case DISP_SECOND_LINE:
/* Second line is fully displayed */
Disp.State = DISP_PREP_THIRD_LINE;
if(FreetimeUs - Elapsed_us >= 200)
{
FreetimeUs -= (uint16_t)Elapsed_us;
goto DispPrepThirdLine;
}
else
{
/* Not enough time: just change state */
MySerial.rxMode();
}
break;
case DISP_THIRD_LINE:
/* Third line is fully displayed */
MySerial.print('\n');
StartWaitMs = millis();
Disp.State = DISP_WAIT;
MySerial.rxMode();
break;
}
break; /* exit while(1) */
}
}
break;
case DISP_WAIT:
if(millis() - StartWaitMs >= 500UL) Disp.State = DISP_COMPUTE; /* Give some time to enter commands via the terminal (in internal source mode) */
else Ch.FallingEdgeFound = false;
break;
}
return(Ret);
}
/* The following function checks if the received signal is the image of the one of the built-in RC generator */
uint8_t IsInternalRcSrc()
{
uint8_t Ret;
static uint32_t SampleStartMs = millis();
if(millis() - Ch.LastRxPulseMs >= 300UL) IntRcSynch = 100; /* Kick off Internal RC generator */
Ret = (IntRcSynch >= 100);
if(millis() - SampleStartMs >= 10UL)
{
SampleStartMs = millis();
IntRcSynch = 99;
}
return(Ret);
}
/* Function called in interruption in case of change on RC pins: pulse width and RC period measurement */
void InterruptFunctionToCall(void)
{
if(TinyPinChange_RisingEdge(VirtualPortIdx, RC_CHANNEL_PIN)) /* Check for RC Channel rising edge */
{
RcPeriod_us = micros() - Ch.RisingStartUs;
Ch.RisingStartUs = micros();
}
else
{
Ch.RcWidth_us = micros() - Ch.RisingStartUs;
Ch.FallingEdgeFound = true;
Ch.LastRxPulseMs = millis();
if(!(PINB & RC_PINS_MSK)) /* Check if RC_CHANNEL_PIN and RC_GEN_PIN are both to 0 */
{
if(IntRcSynch < 100) IntRcSynch++; /* if IntRcSynch reaches 100, it means the RC source is internal (synchronized) */
}else IntRcSynch = 0; /* Not synchronized */
}
}

View File

@ -0,0 +1,39 @@
TinySoftPwm library
===================
**TinySoftPwm** is a library designed to generate PWM signals by software.
Some examples of use cases:
-------------------------
* **RGB strip LED Controller**
* **DC Motor controller**
* **Digital to Analog converter**
Supported Arduinos:
------------------
* **ATtiny85 (Standalone or Digispark)** (up to 6 software PWM supported)
* **ATtiny167 (Digispark pro)** (up to 13 software PWM supported)
API/methods:
-----------
* TinySoftPwm_begin()
* TinySoftPwm_analogWrite()
* TinySoftPwm_process()
Design considerations:
---------------------
In order to reduce **program** and **RAM** memories, PWM pins shall be declared in the **TinySoftPwm.h** file. All the required amount of **program** and **RAM** memories are allocated at compilation time.
The **TinySoftPwm_process()** method shall be called periodically:
* using micros() in the loop(): in this case, asynchronous programmation shall be used: no call to blocking functions such as delay() is permitted.
* or better using periodic interruption.
In order to reduce the memory footprint (programm and RAM), try to use the PWM on pins which are part of the same port: PORTA or PORTB.
Contact
-------
If you have some ideas of enhancement, please contact me by clicking on: [RC Navy](http://p.loussouarn.free.fr/contact.html).

View File

@ -2,46 +2,111 @@
// Only resources RAM/Program Memory of used pins are declared in the code at compilation time.
// based largely on Atmel's AVR136: Low-Jitter Multi-Channel Software PWM Application Note:
// http://www.atmel.com/dyn/resources/prod_documents/doc8020.pdf
// RC Navy 2013
// RC Navy 2013-2015
// http://p.loussouarn.free.fr
// 11/01/2015: Multi port support added for ATtiny167
#include <TinySoftPwm.h>
#define TINY_SOFT_PWM_PORT PORTB
#define TINY_SOFT_PWM_DDR DDRB
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) (PortPwmTo1 &= GET_INV_PIN_MSK(RamIdx))
#if (TINY_SOFT_PWM_CH_MAX == 0)
#error At least one PWM pin shall be declared in TinySoftPwm.h
#endif
#define TINY_SOFT_PWM_DECLARE_PIN(Px) TINY_SOFT_PWM_DDR |= (1<<(Px)); PortPwmMask |= (1<<(Px))
#if defined(TINY_SOFT_PWM_DDR1) && defined(TINY_SOFT_PWM_DDR0)
#define TINY_SOFT_PWM_DECLARE_PIN(Pin) do{ \
if(digitalPinToPortIdx(Pin)) \
{ \
TINY_SOFT_PWM_DDR1 |= (1 << digitalPinToPortBit(Pin)); \
Port1_PwmMask |= (1 << digitalPinToPortBit(Pin)); \
} \
else \
{ \
TINY_SOFT_PWM_DDR0 |= (1 << digitalPinToPortBit(Pin)); \
Port0_PwmMask |= (1 << digitalPinToPortBit(Pin)); \
} \
}while(0)
#define GET_INV_PIN_MSK(RamIdx) ((uint8_t)pgm_read_byte(&RamIdxToInvPinMsk[(RamIdx)]))
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) GET_PWM_PIN_PORT(RamIdx) \
? (Port1_PwmTo1 &= GET_PWM_INV_MSK(RamIdx)) \
: (Port0_PwmTo1 &= GET_PWM_INV_MSK(RamIdx))
#else
#if defined(TINY_SOFT_PWM_DDR1)
#define TINY_SOFT_PWM_DECLARE_PIN(Pin) TINY_SOFT_PWM_DDR1 |= (1 << digitalPinToPortBit(Pin)); Port1_PwmMask |= (1 << digitalPinToPortBit(Pin))
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) Port1_PwmTo1 &= GET_PWM_INV_MSK(RamIdx)
#else
#define TINY_SOFT_PWM_DECLARE_PIN(Pin) TINY_SOFT_PWM_DDR0 |= (1 << digitalPinToPortBit(Pin)); Port0_PwmMask |= (1 << digitalPinToPortBit(Pin))
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) Port0_PwmTo1 &= GET_PWM_INV_MSK(RamIdx)
#endif
#endif
uint8_t RamIdxToInvPinMsk[] PROGMEM ={
#if (TINY_SOFT_PWM_USES_P0 == 1)
~(1<<0),
#define GET_PWM_PIN_ID(RamIdx) ((uint8_t)pgm_read_byte(&PwmPin[(RamIdx)].Id))
#define GET_PWM_PIN_PORT(RamIdx) ((uint8_t)pgm_read_byte(&PwmPin[(RamIdx)].Port))
#define GET_PWM_INV_MSK(RamIdx) ((uint8_t)pgm_read_byte(&PwmPin[(RamIdx)].InvMsk))
typedef struct {
uint8_t Id;
uint8_t Port;
uint8_t InvMsk;
}SoftPwmPinSt_t;
const SoftPwmPinSt_t PwmPin[] PROGMEM ={
#if (TINY_SOFT_PWM_USES_PIN0 == 1)
{0, digitalPinToPortIdx(0), ~(1 << digitalPinToPortBit(0))},
#endif
#if (TINY_SOFT_PWM_USES_P1 == 1)
~(1<<1),
#if (TINY_SOFT_PWM_USES_PIN1 == 1)
{1, digitalPinToPortIdx(1), ~(1 << digitalPinToPortBit(1))},
#endif
#if (TINY_SOFT_PWM_USES_P2 == 1)
~(1<<2),
#if (TINY_SOFT_PWM_USES_PIN2 == 1)
{2, digitalPinToPortIdx(2), ~(1 << digitalPinToPortBit(2))},
#endif
#if (TINY_SOFT_PWM_USES_P3 == 1)
~(1<<3),
#if (TINY_SOFT_PWM_USES_PIN3 == 1)
{3, digitalPinToPortIdx(3), ~(1 << digitalPinToPortBit(3))},
#endif
#if (TINY_SOFT_PWM_USES_P4 == 1)
~(1<<4),
#if (TINY_SOFT_PWM_USES_PIN4 == 1)
{4, digitalPinToPortIdx(4), ~(1 << digitalPinToPortBit(4))},
#endif
#if (TINY_SOFT_PWM_USES_P5 == 1)
~(1<<5),
#if (TINY_SOFT_PWM_USES_PIN5 == 1)
{5, digitalPinToPortIdx(5), ~(1 << digitalPinToPortBit(5))},
#endif
#if defined (__AVR_ATtiny167__)
#if (TINY_SOFT_PWM_USES_PIN6 == 1)
{6, digitalPinToPortIdx(6), ~(1 << digitalPinToPortBit(6))},
#endif
};
#if (TINY_SOFT_PWM_USES_PIN7 == 1)
{7, digitalPinToPortIdx(7), ~(1 << digitalPinToPortBit(7))},
#endif
#if (TINY_SOFT_PWM_USES_PIN8 == 1)
{8, digitalPinToPortIdx(8), ~(1 << digitalPinToPortBit(8))},
#endif
#if (TINY_SOFT_PWM_USES_PIN9 == 1)
{9, digitalPinToPortIdx(9), ~(1 << digitalPinToPortBit(9))},
#endif
#if (TINY_SOFT_PWM_USES_PIN10 == 1)
{10, digitalPinToPortIdx(10), ~(1 << digitalPinToPortBit(10))},
#endif
#if (TINY_SOFT_PWM_USES_PIN11 == 1)
{11, digitalPinToPortIdx(11), ~(1 << digitalPinToPortBit(11))},
#endif
#if (TINY_SOFT_PWM_USES_PIN12 == 1)
{12, digitalPinToPortIdx(12), ~(1 << digitalPinToPortBit(12))},
#endif
#endif
};
static uint8_t Compare[TINY_SOFT_PWM_CH_MAX];
volatile uint8_t PwmOrder[TINY_SOFT_PWM_CH_MAX];
static uint8_t PortPwmMask=0;
volatile uint8_t PortPwmTo1=0x00;
volatile uint8_t PortPwmTo0=0xFF;
static uint8_t _TickMax=255;
#ifdef TINY_SOFT_PWM_PORT0
static uint8_t Port0_PwmMask = 0;
volatile uint8_t Port0_PwmTo1 = 0x00;
volatile uint8_t Port0_PwmTo0 = 0xFF;
#endif
#ifdef TINY_SOFT_PWM_PORT1
static uint8_t Port1_PwmMask = 0;
volatile uint8_t Port1_PwmTo1 = 0x00;
volatile uint8_t Port1_PwmTo0 = 0xFF;
#endif
static uint8_t _TickMax = 255;
static uint8_t PwmToPwmMax(uint8_t Pwm);
@ -50,44 +115,70 @@ void TinySoftPwm_begin(uint8_t TickMax, uint8_t PwmInit)
uint8_t oldSREG = SREG;
cli();
// set the direction of the used ports and update PortPwmMask
#if (TINY_SOFT_PWM_USES_P0 == 1)
TINY_SOFT_PWM_DECLARE_PIN(PB0);
#if (TINY_SOFT_PWM_USES_PIN0 == 1)
TINY_SOFT_PWM_DECLARE_PIN(0);
#endif
#if (TINY_SOFT_PWM_USES_P1 == 1)
TINY_SOFT_PWM_DECLARE_PIN(PB1);
#if (TINY_SOFT_PWM_USES_PIN1 == 1)
TINY_SOFT_PWM_DECLARE_PIN(1);
#endif
#if (TINY_SOFT_PWM_USES_P2 == 1)
TINY_SOFT_PWM_DECLARE_PIN(PB2);
#if (TINY_SOFT_PWM_USES_PIN2 == 1)
TINY_SOFT_PWM_DECLARE_PIN(2);
#endif
#if (TINY_SOFT_PWM_USES_P3 == 1)
TINY_SOFT_PWM_DECLARE_PIN(PB3);
#if (TINY_SOFT_PWM_USES_PIN3 == 1)
TINY_SOFT_PWM_DECLARE_PIN(3);
#endif
#if (TINY_SOFT_PWM_USES_P4 == 1)
TINY_SOFT_PWM_DECLARE_PIN(PB4);
#if (TINY_SOFT_PWM_USES_PIN4 == 1)
TINY_SOFT_PWM_DECLARE_PIN(4);
#endif
#if (TINY_SOFT_PWM_USES_P5 == 1)
TINY_SOFT_PWM_DECLARE_PIN(PB5);
#if (TINY_SOFT_PWM_USES_PIN5 == 1)
TINY_SOFT_PWM_DECLARE_PIN(5);
#endif
#if (TINY_SOFT_PWM_USES_PIN6 == 1)
TINY_SOFT_PWM_DECLARE_PIN(6);
#endif
#if (TINY_SOFT_PWM_USES_PIN7 == 1)
TINY_SOFT_PWM_DECLARE_PIN(7);
#endif
#if (TINY_SOFT_PWM_USES_PIN8 == 1)
TINY_SOFT_PWM_DECLARE_PIN(8);
#endif
#if (TINY_SOFT_PWM_USES_PIN9 == 1)
TINY_SOFT_PWM_DECLARE_PIN(9);
#endif
#if (TINY_SOFT_PWM_USES_PIN10 == 1)
TINY_SOFT_PWM_DECLARE_PIN(10);
#endif
#if (TINY_SOFT_PWM_USES_PIN11 == 1)
TINY_SOFT_PWM_DECLARE_PIN(11);
#endif
#if (TINY_SOFT_PWM_USES_PIN12 == 1)
TINY_SOFT_PWM_DECLARE_PIN(12);
#endif
_TickMax = TickMax;
#ifdef TINY_SOFT_PWM_PORT0
Port0_PwmTo1 = Port0_PwmMask;
#endif
#ifdef TINY_SOFT_PWM_PORT1
Port1_PwmTo1 = Port1_PwmMask;
#endif
_TickMax=TickMax;
PortPwmTo1=PortPwmMask;
// initialise all channels
for(uint8_t i=0 ; i<TINY_SOFT_PWM_CH_MAX ; i++)
for(uint8_t i = 0; i < TINY_SOFT_PWM_CH_MAX; i++)
{
Compare[i] = PwmToPwmMax(PwmInit); // set default PWM values
Compare[i] = PwmToPwmMax(PwmInit); // set default PWM values
PwmOrder[i] = Compare[i]; // set default PWM values
}
SREG = oldSREG;
}
void TinySoftPwm_analogWrite(uint8_t PinIdx, uint8_t Pwm)
void TinySoftPwm_analogWrite(uint8_t Pin, uint8_t Pwm)
{
uint8_t RamIdx;
for(RamIdx=0;RamIdx<TINY_SOFT_PWM_CH_MAX;RamIdx++)
for(RamIdx = 0; RamIdx < TINY_SOFT_PWM_CH_MAX; RamIdx++)
{
if(GET_INV_PIN_MSK(RamIdx)==((1<<PinIdx)^0xFF)) break;
if(GET_PWM_PIN_ID(RamIdx) == Pin) break;
}
if(RamIdx<TINY_SOFT_PWM_CH_MAX)
if(RamIdx < TINY_SOFT_PWM_CH_MAX)
{
PwmOrder[RamIdx] = PwmToPwmMax(Pwm);
}
@ -95,16 +186,26 @@ uint8_t RamIdx;
static uint8_t PwmToPwmMax(uint8_t Pwm)
{
return(map(Pwm, 0, 255, 0, _TickMax));
uint16_t Pwm16;
Pwm16 = map(Pwm, 0, 255, 0, _TickMax);
Pwm16 = constrain(Pwm16, 0, _TickMax);
return((uint8_t)Pwm16);
}
void TinySoftPwm_process(void)
{
static uint8_t OvfCount=0xFF;
PortPwmTo0 = (~PortPwmTo1)^PortPwmMask;
TINY_SOFT_PWM_PORT |= PortPwmTo1; // update ONLY used outputs to 1 without disturbing the others
TINY_SOFT_PWM_PORT &= PortPwmTo0; // update ONLY used outputs to 0 without disturbing the others
#ifdef TINY_SOFT_PWM_PORT0
Port0_PwmTo0 = (~Port0_PwmTo1) ^ Port0_PwmMask;
TINY_SOFT_PWM_PORT0 |= Port0_PwmTo1; // update ONLY used outputs to 1 without disturbing the others
TINY_SOFT_PWM_PORT0 &= Port0_PwmTo0; // update ONLY used outputs to 0 without disturbing the others
#endif
#ifdef TINY_SOFT_PWM_PORT1
Port1_PwmTo0 = (~Port1_PwmTo1) ^ Port1_PwmMask;
TINY_SOFT_PWM_PORT1 |= Port1_PwmTo1; // update ONLY used outputs to 1 without disturbing the others
TINY_SOFT_PWM_PORT1 &= Port1_PwmTo0; // update ONLY used outputs to 0 without disturbing the others
#endif
if(++OvfCount == _TickMax)
{ // increment modulo 256 counter and update
// the compare values only when counter = 0.
@ -127,7 +228,33 @@ static uint8_t OvfCount=0xFF;
#if (TINY_SOFT_PWM_CH_MAX >= 6)
Compare[5] = PwmOrder[5];
#endif
PortPwmTo1 = PortPwmMask; // set all port used pins high
#if (TINY_SOFT_PWM_CH_MAX >= 7)
Compare[6] = PwmOrder[6]; // verbose code for speed
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 8)
Compare[7] = PwmOrder[7];
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 9)
Compare[8] = PwmOrder[8];
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 10)
Compare[9] = PwmOrder[9];
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 11)
Compare[10] = PwmOrder[10];
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 12)
Compare[11] = PwmOrder[11];
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 13)
Compare[12] = PwmOrder[12];
#endif
#ifdef TINY_SOFT_PWM_PORT0
Port0_PwmTo1 = Port0_PwmMask; // set all port used pins high
#endif
#ifdef TINY_SOFT_PWM_PORT1
Port1_PwmTo1 = Port1_PwmMask; // set all port used pins high
#endif
}
// clear port pin on compare match (executed on next interrupt)
#if (TINY_SOFT_PWM_CH_MAX >= 1)
@ -148,4 +275,25 @@ static uint8_t OvfCount=0xFF;
#if (TINY_SOFT_PWM_CH_MAX >= 6)
if(Compare[5] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(5);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 7)
if(Compare[6] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(6);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 8)
if(Compare[7] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(7);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 9)
if(Compare[8] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(8);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 10)
if(Compare[9] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(9);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 11)
if(Compare[10] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(10);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 12)
if(Compare[11] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(11);
#endif
#if (TINY_SOFT_PWM_CH_MAX >= 13)
if(Compare[12] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(12);
#endif
}

View File

@ -5,8 +5,9 @@
// Only resources RAM/Program Memory of used pins are declared in the code at compilation time.
// based largely on Atmel's AVR136: Low-Jitter Multi-Channel Software PWM Application Note:
// http://www.atmel.com/dyn/resources/prod_documents/doc8020.pdf
// RC Navy 2013
// RC Navy 2013-2015
// http://p.loussouarn.free.fr
// 11/01/2015: Automated multi port support (at compilation time) added for ATtiny167
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
@ -20,67 +21,172 @@
/* Define here the PIN to use with Tiny Soft PWM */
/* Unused Pin(s) SHALL be commented */
/*************************************************/
#define TINY_SOFT_PWM_USES_P0
#define TINY_SOFT_PWM_USES_P1
#define TINY_SOFT_PWM_USES_P2
//#define TINY_SOFT_PWM_USES_P3 /* /!\ used for USB on DigiSpark: do not use it for PWM if DigiUSB is also used /!\ */
//#define TINY_SOFT_PWM_USES_P4 /* /!\ used for USB on DigiSpark: do not use it for PWM if DigiUSB is also used /!\ */
//#define TINY_SOFT_PWM_USES_P5
#define TINY_SOFT_PWM_USES_PIN0
#define TINY_SOFT_PWM_USES_PIN1
#define TINY_SOFT_PWM_USES_PIN2
#define TINY_SOFT_PWM_USES_PIN3 /* /!\ used for USB on DigiSpark (pro): do not use it for PWM if DigiUSB or SerialCDC are also used /!\ */
#define TINY_SOFT_PWM_USES_PIN4 /* /!\ used for USB on DigiSpark (pro): do not use it for PWM if DigiUSB or SerialCDC are also used /!\ */
#define TINY_SOFT_PWM_USES_PIN5
#define TINY_SOFT_PWM_USES_PIN6
#define TINY_SOFT_PWM_USES_PIN7
#define TINY_SOFT_PWM_USES_PIN8
#define TINY_SOFT_PWM_USES_PIN9
#define TINY_SOFT_PWM_USES_PIN10
#define TINY_SOFT_PWM_USES_PIN11
#define TINY_SOFT_PWM_USES_PIN12
/*******************************************************************/
/* Do NOT modify below: it's used to optimize RAM and Program size */
/*******************************************************************/
#ifdef TINY_SOFT_PWM_USES_P0
#undef TINY_SOFT_PWM_USES_P0
#define TINY_SOFT_PWM_USES_P0 1
#else
#define TINY_SOFT_PWM_USES_P0 0
#if defined (__AVR_ATtiny85__)
#undef TINY_SOFT_PWM_USES_PIN6
#undef TINY_SOFT_PWM_USES_PIN7
#undef TINY_SOFT_PWM_USES_PIN8
#undef TINY_SOFT_PWM_USES_PIN9
#undef TINY_SOFT_PWM_USES_PIN10
#undef TINY_SOFT_PWM_USES_PIN11
#undef TINY_SOFT_PWM_USES_PIN12
#endif
#ifdef TINY_SOFT_PWM_USES_P1
#undef TINY_SOFT_PWM_USES_P1
#define TINY_SOFT_PWM_USES_P1 1
#ifdef TINY_SOFT_PWM_USES_PIN0
#undef TINY_SOFT_PWM_USES_PIN0
#define TINY_SOFT_PWM_USES_PIN0 1
#else
#define TINY_SOFT_PWM_USES_P1 0
#define TINY_SOFT_PWM_USES_PIN0 0
#endif
#ifdef TINY_SOFT_PWM_USES_P2
#undef TINY_SOFT_PWM_USES_P2
#define TINY_SOFT_PWM_USES_P2 1
#ifdef TINY_SOFT_PWM_USES_PIN1
#undef TINY_SOFT_PWM_USES_PIN1
#define TINY_SOFT_PWM_USES_PIN1 1
#else
#define TINY_SOFT_PWM_USES_P2 0
#define TINY_SOFT_PWM_USES_PIN1 0
#endif
#ifdef TINY_SOFT_PWM_USES_P3
#undef TINY_SOFT_PWM_USES_P3
#define TINY_SOFT_PWM_USES_P3 1
#ifdef TINY_SOFT_PWM_USES_PIN2
#undef TINY_SOFT_PWM_USES_PIN2
#define TINY_SOFT_PWM_USES_PIN2 1
#else
#define TINY_SOFT_PWM_USES_P3 0
#define TINY_SOFT_PWM_USES_PIN2 0
#endif
#ifdef TINY_SOFT_PWM_USES_P4
#undef TINY_SOFT_PWM_USES_P4
#define TINY_SOFT_PWM_USES_P4 1
#ifdef TINY_SOFT_PWM_USES_PIN3
#undef TINY_SOFT_PWM_USES_PIN3
#define TINY_SOFT_PWM_USES_PIN3 1
#else
#define TINY_SOFT_PWM_USES_P4 0
#define TINY_SOFT_PWM_USES_PIN3 0
#endif
#ifdef TINY_SOFT_PWM_USES_P5
#undef TINY_SOFT_PWM_USES_P5
#define TINY_SOFT_PWM_USES_P5 1
#ifdef TINY_SOFT_PWM_USES_PIN4
#undef TINY_SOFT_PWM_USES_PIN4
#define TINY_SOFT_PWM_USES_PIN4 1
#else
#define TINY_SOFT_PWM_USES_P5 0
#define TINY_SOFT_PWM_USES_PIN4 0
#endif
#define TINY_SOFT_PWM_CH_MAX (TINY_SOFT_PWM_USES_P0 + TINY_SOFT_PWM_USES_P1 + TINY_SOFT_PWM_USES_P2 + TINY_SOFT_PWM_USES_P3 + TINY_SOFT_PWM_USES_P4 + TINY_SOFT_PWM_USES_P5)
#ifdef TINY_SOFT_PWM_USES_PIN5
#undef TINY_SOFT_PWM_USES_PIN5
#define TINY_SOFT_PWM_USES_PIN5 1
#else
#define TINY_SOFT_PWM_USES_PIN5 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN6
#undef TINY_SOFT_PWM_USES_PIN6
#define TINY_SOFT_PWM_USES_PIN6 1
#else
#define TINY_SOFT_PWM_USES_PIN6 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN7
#undef TINY_SOFT_PWM_USES_PIN7
#define TINY_SOFT_PWM_USES_PIN7 1
#else
#define TINY_SOFT_PWM_USES_PIN7 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN8
#undef TINY_SOFT_PWM_USES_PIN8
#define TINY_SOFT_PWM_USES_PIN8 1
#else
#define TINY_SOFT_PWM_USES_PIN8 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN9
#undef TINY_SOFT_PWM_USES_PIN9
#define TINY_SOFT_PWM_USES_PIN9 1
#else
#define TINY_SOFT_PWM_USES_PIN9 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN10
#undef TINY_SOFT_PWM_USES_PIN10
#define TINY_SOFT_PWM_USES_PIN10 1
#else
#define TINY_SOFT_PWM_USES_PIN10 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN11
#undef TINY_SOFT_PWM_USES_PIN11
#define TINY_SOFT_PWM_USES_PIN11 1
#else
#define TINY_SOFT_PWM_USES_PIN11 0
#endif
#ifdef TINY_SOFT_PWM_USES_PIN12
#undef TINY_SOFT_PWM_USES_PIN12
#define TINY_SOFT_PWM_USES_PIN12 1
#else
#define TINY_SOFT_PWM_USES_PIN12 0
#endif
#define TINY_SOFT_PWM_CH_MAX (TINY_SOFT_PWM_USES_PIN0 + TINY_SOFT_PWM_USES_PIN1 + TINY_SOFT_PWM_USES_PIN2 + \
TINY_SOFT_PWM_USES_PIN3 + TINY_SOFT_PWM_USES_PIN4 + TINY_SOFT_PWM_USES_PIN5 + \
TINY_SOFT_PWM_USES_PIN6 + TINY_SOFT_PWM_USES_PIN7 + TINY_SOFT_PWM_USES_PIN8 + \
TINY_SOFT_PWM_USES_PIN9 + TINY_SOFT_PWM_USES_PIN10+ TINY_SOFT_PWM_USES_PIN11+ \
TINY_SOFT_PWM_USES_PIN12)
#if defined (__AVR_ATtiny85__)
#define TINY_SOFT_PWM_USES_PORT0 0
#define TINY_SOFT_PWM_USES_PORT1 1
#ifndef digitalPinToPortIdx
#define digitalPinToPortIdx(p) 1
#endif
#else
#if defined (__AVR_ATtiny167__)
#define TINY_SOFT_PWM_USES_PORT0 (TINY_SOFT_PWM_USES_PIN5 || TINY_SOFT_PWM_USES_PIN6 || TINY_SOFT_PWM_USES_PIN7 || \
TINY_SOFT_PWM_USES_PIN8 || TINY_SOFT_PWM_USES_PIN9 || TINY_SOFT_PWM_USES_PIN10 || \
TINY_SOFT_PWM_USES_PIN11 || TINY_SOFT_PWM_USES_PIN12)
#define TINY_SOFT_PWM_USES_PORT1 (TINY_SOFT_PWM_USES_PIN0 || TINY_SOFT_PWM_USES_PIN1 || TINY_SOFT_PWM_USES_PIN2 || \
TINY_SOFT_PWM_USES_PIN3 || TINY_SOFT_PWM_USES_PIN4)
#ifndef digitalPinToPortIdx
#define digitalPinToPortIdx(p) (((p) >= 5 && (p) <= 12) ? (0) : (1))
#endif
#endif
#endif
#if (TINY_SOFT_PWM_USES_PORT0 == 1)
#undef TINY_SOFT_PWM_USES_PORT0
#define TINY_SOFT_PWM_PORT0 PORTA
#define TINY_SOFT_PWM_DDR0 DDRA
#endif
#if (TINY_SOFT_PWM_USES_PORT1 == 1)
#undef TINY_SOFT_PWM_USES_PORT1
#define TINY_SOFT_PWM_PORT1 PORTB
#define TINY_SOFT_PWM_DDR1 DDRB
#endif
#ifndef digitalPinToPortBit
#define digitalPinToPortBit(p) digitalPinToPCMSKbit(p)
#endif
/* Public Function Prototypes */
void TinySoftPwm_begin(uint8_t TickMax, uint8_t PwmInit);
void TinySoftPwm_analogWrite(uint8_t PinIdx, uint8_t Pwm);
void TinySoftPwm_analogWrite(uint8_t Pin, uint8_t Pwm);
void TinySoftPwm_process(void);
#endif

View File

@ -0,0 +1,75 @@
#include <TinySoftPwm.h>
/*
_____ ____ __ _ ____ _ _ _ _
| __ \ / __ \ | \ | | / __ \ | | | | | | | |
| |__| | | / \_| | . \ | | / / \ \ | | | | \ \ / /
| _ / | | _ | |\ \| | | |__| | | | | | \ ' /
| | \ \ | \__/ | | | \ ' | | __ | \ \/ / | |
|_| \_\ \____/ |_| \__| |_| |_| \__/ |_| 2015
http://p.loussouarn.free.fr
****************************************
* <TinySoftPwm> library Demo *
****************************************
This sketch generates simultaneously PWM signals on 13 pins (Pin 0 to pin 12 of the Digispark pro).
It also increases the luminosity of the built-in LED of the Digispark whilst the duty cycle remains constant for all other pins.
When the luminosity reaches its maximum, the luminosity decreases.
When the luminosity reaches its minimum, the luminosity increases, and so on...
Note:
====
Declare the Pin(s) used in "librarie/TinySoftPwm/TinySoftPwm.h"
In this sketch, #define TINY_SOFT_PWM_USES_PINO to TINY_SOFT_PWM_USES_PIN12 must be enabled (not commented) since it uses the first 13 pins of the DigiSpark pro.
In this basic example, TinySoftPwm_process() is called periodically using micros(), but it is recommanded to call it from a timer ISR
to ensure a better periodicity.
*/
#define BUILT_IN_LED_PIN 1 /* Digispark Model A (Rev2) built-in LED pin number (Change it to 2 for Model B) */
void setup()
{
TinySoftPwm_begin(255, 0); /* 255 x TinySoftPwm_process() calls before overlap (Frequency tuning), 0 = PWM init for all declared pins */
for(uint8_t PinIdx = 0; PinIdx <= 12; PinIdx++)
{
TinySoftPwm_analogWrite(PinIdx, (PinIdx + 1) * 19); /* Low to high duty cycle for pin 0 to 12 */
}
}
void loop()
{
static uint32_t StartUs = micros();
static uint32_t StartMs = millis();
static uint8_t Pwm = 0;
static int8_t Dir = 1;
/***********************************************************/
/* Call TinySoftPwm_process() with a period of 40 us */
/* The PWM frequency = 255 x 40 # 10.2 ms -> F # 100Hz */
/* 255 is the first argument passed to TinySoftPwm_begin() */
/***********************************************************/
if((micros() - StartUs) >= 40)
{
/* We arrived here every 40 microseconds */
StartUs = micros();
TinySoftPwm_process(); /* This function shall be called periodically (like here, based on micros(), or in a timer ISR) */
}
/*************************************************************/
/* Increment/decrement PWM on LED Pin with a period of 10 ms */
/*************************************************************/
if((millis() - StartMs) >= 10)
{
/* We arrived here every 10 milliseconds */
StartMs = millis();
Pwm += Dir; /* increment or decrement PWM depending of sign of Dir */
TinySoftPwm_analogWrite(BUILT_IN_LED_PIN, Pwm); /* Update built-in LED for Digispark */
if(Pwm == 255) Dir = -1; /* if PWM reaches the maximum: change direction */
if(Pwm == 0) Dir = +1; /* if PWM reaches the minimum: change direction */
}
}

View File

@ -0,0 +1,105 @@
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
//THESE MUST BE SET! CHANGE ADDRESS AS YOU UPLOAD TO EACH NODE!
#define TOTAL_NODES 3 //TOTAL NUMBER OF NODES IN MESH
#define NODE_ADDRESS 0 //the sero indexed address of this node - nodes must be numbered 0 thru TOTAL_NODES-1 //also note 255 is broadcast address
//hold the seuqnce numbers to avoid repeating the same messages
uint8_t sequence[TOTAL_NODES];
//setup radio module
RF24 radio(9,12);
//message format - adjust data as needed - the rest is madatory for this mesh to work
struct
{
char toAddress;
char fromAddress;
char fromSequence;
long data;
} msg;
//don't change! read is same for all nodes, write is automatically calculated here
const uint64_t writePipe = 0xF0F0F0F00LL + NODE_ADDRESS;
const uint64_t readPipe = 0xF0F0F0F0D2LL;
void setup() {
// put your setup code here, to run once:
radio.begin();
radio.setDataRate(RF24_250KBPS); //lowest speed = most range
radio.setAutoAck(false); //this is a mesh so we don't want ACKs!
radio.setRetries(15, 15);
radio.setPayloadSize(sizeof(msg));
radio.openWritingPipe(writePipe);
radio.openReadingPipe(1, readPipe);
radio.startListening();
}
long now = 0;
void loop() {
if(readAndRepeat()){ //will repeat messages as needed and return false unless there is packet for this node - CALL FREQUENTLY!
//if this does not return false then we have a packet for THIS node!
//msg.fromAddress is the node that sent it
//msg.data is the data itself
//Do something with it!
//For example display packets coming to this node on a LCD:
/*
NOTE: TO USE THIS ADD THE LCD INCLUDES AND SETUP ROUTINE FROM THE DigisparkLCD example
lcd.clear();
lcd.print("From: ");
lcd.println(msg.fromAddress);
lcd.print("Value: ");
lcd.println(msg.data);
*/
}
if(millis() - now > 10000){ //send a packet from this node to the mesh every 10 seconds but wait in a non-blocking way so that we can still run this loop and repeat things
now = millis(); //set now to millis so we wait another 10 seconds before sending again
//sendToMesh(To_Address,Data_To_Send);
//sendToMesh(0, analogRead(A5)); //send to node 0 the analog read value of pin 5 - could also send a temp sensor value, etc ,etc
//sendToMesh(255, analogRead(A5)); //send to all nodes (255 is the broadcast address) the analog read value of pin 5
}
}
void sendToMesh(uint8_t toAddress, long data){
if(sequence[NODE_ADDRESS]<255)
sequence[NODE_ADDRESS]++; //increment sequence count for this device
else
sequence[NODE_ADDRESS] = 0; //set to zero if last was 255 so we don't overflow - logic for read is built to handle this too
msg.toAddress = toAddress; //set the address of the destination node
msg.fromAddress = NODE_ADDRESS; //set the from as this node - of course
msg.fromSequence = sequence[NODE_ADDRESS]; //set it to the sequence number we just implemented which should be greater than any the nodes have received
radio.stopListening(); //turn of recv so we can transmit
radio.write(&msg, sizeof(msg));
radio.startListening(); //turn recv back on
}
bool readAndRepeat(){
if(radio.read(&msg, sizeof(msg))){ //if we have incoming data
if(msg.fromAddress!=NODE_ADDRESS){ //make sure this node didn't send it
if(sequence[msg.fromAddress] < msg.fromSequence || (sequence[msg.fromAddress] == 255 && msg.fromSequence == 0)){ //make sure we haven't already repeated it or received it
//increment sequence for that address so we don't repeat it twice from this node or receive it twice
sequence[msg.fromAddress] = msg.fromSequence;
if(msg.toAddress==NODE_ADDRESS){ //is it for this node? if so return true so we can use it!
return true;
}
//otherwise repeat it - send it back out
radio.write(&msg, sizeof(msg));
if(msg.toAddress == 255){ //it was a broadcast so return true so we do something with it
return true;
}
}
}
}
return false;
}

View File

@ -20,21 +20,12 @@
* length should be these two bytes plus the number of bytes to read.
****************************************************************************/
#include <avr/interrupt.h>
#if defined (__AVR_ATtiny45__) || defined (__AVR_ATtiny85__)
#define F_CPU 16500000UL
#elif defined (__AVR_ATtiny87__) || defined (__AVR_ATtiny167__)
#define F_CPU 16000000UL // Sets up the default speed for delay.h
#endif
#include <util/delay.h>
#include <avr/io.h>
#include "USI_TWI_Master.h"
unsigned char USI_TWI_Start_Transceiver_With_Data( unsigned char * , unsigned char );
unsigned char USI_TWI_Master_Transfer( unsigned char );
unsigned char USI_TWI_Master_Stop( void );
unsigned char USI_TWI_Master_Start( void );
union USI_TWI_state
@ -255,11 +246,8 @@ unsigned char USI_TWI_Start_Transceiver_With_Data( unsigned char *msg, unsigned
USI_TWI_Master_Transfer( tempUSISR_1bit ); // Generate ACK/NACK.
}
}while( --msgSize) ; // Until all data sent/received.
if (!USI_TWI_Master_Stop())
{
return (FALSE); // Send a STOP condition on the TWI bus.
}
// usually a stop condition is sent here, but TinyWireM needs to choose whether or not to send it
/* Transmission successfully completed*/
return (TRUE);

View File

@ -22,14 +22,6 @@
// Defines controlling timing limits - SCL <= 100KHz.
#if defined (__AVR_ATtiny45__) || defined (__AVR_ATtiny85__)
#define SYS_CLK 16500.0 // [kHz] Default for ATtiny2313
#elif defined (__AVR_ATtiny87__) || defined (__AVR_ATtiny167__)
#define SYS_CLK 16000.0 // [kHz] Default for ATtiny2313
#endif
// For use with _delay_us()
#define T2_TWI 5 // >4,7us
#define T4_TWI 4 // >4,0us
@ -37,7 +29,7 @@
// Defines error code generating
//#define PARAM_VERIFICATION
//#define NOISE_TESTING
#define SIGNAL_VERIFY // This should probably be on always.
//#define SIGNAL_VERIFY // This should probably be on always.
/****************************************************************************
Bit and byte definitions
@ -77,8 +69,8 @@
#endif
#if defined(__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) | \
defined(__AVR_AT90Tiny26__) | defined(__AVR_ATtiny26__) | defined(__AVR_ATtiny167__) | \
defined(__AVR_ATtiny87__)
defined(__AVR_AT90Tiny26__) | defined(__AVR_ATtiny26__) | defined(__AVR_ATtiny87__) | \
defined(__AVR_ATtiny167__)
#define DDR_USI DDRB
#define PORT_USI PORTB
#define PIN_USI PINB
@ -88,6 +80,16 @@
#define PIN_USI_SCL PINB2
#endif
#if defined(__AVR_ATtiny84__) | defined(__AVR_ATtiny44__)
# define DDR_USI DDRA
# define PORT_USI PORTA
# define PIN_USI PINA
# define PORT_USI_SDA PORTA6
# define PORT_USI_SCL PORTA4
# define PIN_USI_SDA PINA6
# define PIN_USI_SCL PINA4
#endif
#if defined(__AVR_AT90Tiny2313__) | defined(__AVR_ATtiny2313__)
#define DDR_USI DDRB
#define PORT_USI PORTB
@ -119,4 +121,5 @@
void USI_TWI_Master_Initialise( void );
unsigned char USI_TWI_Start_Random_Read( unsigned char * , unsigned char );
unsigned char USI_TWI_Start_Read_Write( unsigned char * , unsigned char );
unsigned char USI_TWI_Master_Stop( void );
unsigned char USI_TWI_Get_State_Info( void );

View File

@ -1,7 +1,4 @@
/*
This version has been modified by Digistump to be a drop in replacement for Wire
TinyWireM.cpp - a wrapper class for TWI/I2C Master library for the ATtiny on Arduino
1/21/2011 BroHogan - brohoganx10 at gmail dot com
@ -40,6 +37,9 @@ USI_TWI::USI_TWI(){
// Public Methods //////////////////////////////////////////////////////////////
//int USI_TWI::peek(){}
//void USI_TWI::flush(){}
void USI_TWI::begin(){ // initialize I2C lib
USI_TWI_Master_Initialise();
}
@ -49,23 +49,32 @@ void USI_TWI::beginTransmission(uint8_t slaveAddr){ // setup address & write bit
USI_Buf[USI_BufIdx] = (slaveAddr<<TWI_ADR_BITS) | USI_SEND;
}
void USI_TWI::send(uint8_t data){ // buffers up data to send
if (USI_BufIdx >= USI_BUF_SIZE) return; // dont blow out the buffer
size_t USI_TWI::write(uint8_t data){ // buffers up data to send
if (USI_BufIdx >= USI_BUF_SIZE) return 0; // dont blow out the buffer
USI_BufIdx++; // inc for next byte in buffer
USI_Buf[USI_BufIdx] = data;
return 1;
}
void USI_TWI::write(uint8_t data){ // buffers up data to send
send(data);
uint8_t USI_TWI::endTransmission() {
endTransmission(1);
}
uint8_t USI_TWI::endTransmission(){ // actually sends the buffer
uint8_t USI_TWI::endTransmission(uint8_t stop){ // actually sends the buffer
bool xferOK = false;
uint8_t errorCode = 0;
xferOK = USI_TWI_Start_Read_Write(USI_Buf,USI_BufIdx+1); // core func that does the work
USI_BufIdx = 0;
if (xferOK) return 0;
if (xferOK) {
if (stop) {
errorCode = USI_TWI_Master_Stop();
if (errorCode == 0) {
errorCode = USI_TWI_Get_State_Info();
return errorCode;
}
}
return 0;
}
else { // there was an error
errorCode = USI_TWI_Get_State_Info(); // this function returns the error number
return errorCode;
@ -81,26 +90,33 @@ uint8_t USI_TWI::requestFrom(uint8_t slaveAddr, uint8_t numBytes){ // setup for
USI_Buf[0] = (slaveAddr<<TWI_ADR_BITS) | USI_RCVE; // setup address & Rcve bit
xferOK = USI_TWI_Start_Read_Write(USI_Buf,numBytes); // core func that does the work
// USI_Buf now holds the data read
if (xferOK) return 0;
if (xferOK) {
errorCode = USI_TWI_Master_Stop();
if (errorCode == 0) {
errorCode = USI_TWI_Get_State_Info();
return errorCode;
}
return 0;
}
else { // there was an error
errorCode = USI_TWI_Get_State_Info(); // this function returns the error number
return errorCode;
}
}
uint8_t USI_TWI::receive(){ // returns the bytes received one at a time
int USI_TWI::read(){ // returns the bytes received one at a time
USI_LastRead++; // inc first since first uint8_t read is in USI_Buf[1]
return USI_Buf[USI_LastRead];
}
uint8_t USI_TWI::read(){ // returns the bytes received one at a time
return USI_TWI::receive();
}
uint8_t USI_TWI::available(){ // the bytes available that haven't been read yet
int USI_TWI::available(){ // the bytes available that haven't been read yet
return USI_BytesAvail - (USI_LastRead);
}
bool USI_TWI::writeAvailable(){ // the bytes available that haven't been written yet
if (USI_BufIdx >= USI_BUF_SIZE) return 0;
return 1;
}
// Preinstantiate Objects //////////////////////////////////////////////////////

View File

@ -1,7 +1,4 @@
/*
This version has been modified by Digistump to be a drop in replacement for Wire
TinyWireM.h - a wrapper(+) class for TWI/I2C Master library for the ATtiny on Arduino
1/21/2011 BroHogan - brohoganx10 at gmail dot com
@ -41,10 +38,12 @@ This version has been modified by Digistump to be a drop in replacement for Wire
#define Wire_h
#include <inttypes.h>
#include "Arduino.h"
#define USI_SEND 0 // indicates sending to TWI
#define USI_RCVE 1 // indicates receiving from TWI
#define USI_BUF_SIZE 16 // bytes in message buffer
#define USI_BUF_SIZE 18 // bytes in message buffer
//class USI_TWI : public Stream
class USI_TWI
{
private:
@ -54,19 +53,33 @@ class USI_TWI
static uint8_t USI_BytesAvail; // number of bytes requested but not read
public:
USI_TWI();
void begin();
void beginTransmission(uint8_t);
void send(uint8_t);
void write(uint8_t);
USI_TWI();
void begin();
void beginTransmission(uint8_t);
size_t write(uint8_t);
inline size_t write(uint8_t* d, uint8_t n) { uint16_t i; for (i = 0; i < n; i++) write(d[i]); return (size_t)n; }
inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
void send(uint8_t b) { write(b); }
void send(uint8_t *d, uint8_t n) { write(d, n); }
void send(int n) { write((uint8_t)n); }
uint8_t endTransmission();
uint8_t endTransmission(uint8_t);
uint8_t requestFrom(uint8_t, uint8_t);
uint8_t receive();
uint8_t read();
uint8_t available();
int read();
int available();
bool writeAvailable();
int peek(void);
void flush(void);
uint8_t receive(void) {
int c = read();
if (c < 0) return 0;
return c;
}
};
extern USI_TWI Wire;
#endif

View File

@ -209,7 +209,7 @@ const uint8_t PROGMEM digital_pin_to_timer_PGM[] =
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
TIMER0A,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,

View File

@ -209,7 +209,7 @@ const uint8_t PROGMEM digital_pin_to_timer_PGM[] =
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
TIMER0A,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,

View File

@ -209,7 +209,7 @@ const uint8_t PROGMEM digital_pin_to_timer_PGM[] =
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
TIMER0A,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,