2014-12-19 08:45:50 -08:00
# include "RcSeq.h"
/*
2015-01-14 18:08:45 -08:00
English : by RC Navy ( 2012 - 2015 )
2014-12-19 08:45:50 -08:00
= = = = = = =
< 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 20 ms to not disturb the servo commands )
The Application Programming Interface ( API ) makes < RcSeq > library very easy to use .
< RcSeq > needs 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
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
RC Signals ( receiver outputs ) can be assigned to a control type :
- Stick Positions ( up to 8 , but in practice , 4 is the maximum to manually discriminate each stick position )
2014-12-23 13:07:19 -08:00
- Multi position switch ( 2 pos switch , 3 pos switch , or more , eg . rotactor )
2014-12-19 08:45:50 -08:00
- Keyboard ( < RcSeq > assumes Push - Buttons associated Pulse duration are equidistant )
- Custom Keyboard ( The pulse durations can be defined independently for each Push - Button )
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 .
- 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 )
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
2015-01-14 18:08:45 -08:00
Francais : par RC Navy ( 2012 - 2015 )
2014-12-19 08:45:50 -08:00
= = = = = = = =
< 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 20 ms pour ne pas perturber la commande des servos )
L ' Interface de Programmation d ' Application ( API ) fait que la librairie < RcSeq > est tres facile a utiliser .
< RcSeq > necessite 3 autres librairies ecrites par le meme auteur :
1 ) < TinyPinChange > : une librarie pour capter de maniere asynchrone les changements d ' etat des broches utilisant les interruptions sur changement des pins des AVR
2 ) < SoftRcPulseIn > : une librarie pour capter de maniere asynchrone les impulsions entrantes en utilisant la librairie < TinyPinChange >
3 ) < SoftRcPulseOut > : une librairie majoritairement basee sur la librairie < SoftwareServo > , mais avec une meilleur generation des impulsions pour limiter la gigue
Les signaux RC ( sorties du recepteur ) peuvent etre associes a un type de controle :
- Positions de Manche ( jusqu ' a 8 , mais en pratique , 4 est le maximum pour discriminer manuellement les positions du manche )
2014-12-23 13:07:19 -08:00
- Interrupteur multi - positions ( interrupteur 2 pos , interrupteur 3 pos , ou plus , ex . rotacteur )
2014-12-19 08:45:50 -08:00
- Clavier ( < RcSeq > suppose que les durees d ' impulsion des Bouton - Poussoirs sont equidistantes )
- Clavier " Maison " ( Les durees d ' impulsion peuvent etre definies de manière independante pour chaque Bouton - Poussoir )
Quelques definitions :
- Sequence : est utilisee pour sequencer un ou plusieurs servos ( sequence est definie dans une structure dans le sketch utilisateur : est lancee quand la commande RC est recue )
La table de sequence ( structure ) peut contenir des mouvements de servo et des actions courtes a appeler .
- Action courte : est utilisee pour une action rapide ( action est une fonction courte definie dans le sketch utilsateur : est appelee quand la commande RC est recue )
ATTENTION : l ' utilisateur final doit egalement utiliser la methode de programmation asynchrone dans la fonction loop ( ) ( pas de fonctions bloquantes comme delay ( ) ou pulseIn ( ) ) .
http : //p.loussouarn.free.fr
ASTUCE :
= = = = = =
Il est possible de declarer 8 sequences par manche ( 4 avec la voie du potentiometre vertical et 4 avec la voie du potentiometre horizontal ) .
Il est possible de lancer 2 sequences en meme temps en utilisant les diagonales ( la ou il y a des X dans la figure ci - dessous ) .
POSITION MANCHE SUR EMETTEUR
, - - - - - - - - - - - - - - - - - - - - - . \
| X O X | - - > RC_IMPULSION_NIVEAU_PLUS_2 |
| | | |
| X O X | - - > RC_IMPULSION_NIVEAU_PLUS_1 |
| | | /
| O - - - O - - - O - - - O - - - O | - - > Neutre ( Aucune action ) > 4 sequences possibles avec le manche vertical
| | | \
| X O X | - - > RC_IMPULSION_NIVEAU_MOINS_1 |
| | | |
2015-01-14 18:08:45 -08:00
| X O X | - - > RC_IMPULSION_NIVEAU_MOINS_2 |
2014-12-19 08:45:50 -08:00
' - - - - - - - - - - - - - - - - - - - - - ' /
| | | | |
| | | | | \
| | | | ' - - - - - - > RC_IMPULSION_NIVEAU_PLUS_2 |
| | | | |
| | | ' - - - - - - - - - - > RC_IMPULSION_NIVEAU_PLUS_1 |
| | | /
| | ' - - - - - - - - - - - - - - > Neutre ( Aucune action ) > 4 sequences possibles avec le manche horizontal
| | \
| ' - - - - - - - - - - - - - - - - - - > RC_IMPULSION_NIVEAU_MOINS_1 |
| |
' - - - - - - - - - - - - - - - - - - - - - - > RC_IMPULSION_NIVEAU_MOINS_2 |
/
*/
/*************************************************************************
MACROS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-01-14 18:08:45 -08:00
/* For an easy Library Version Management */
# define RC_SEQ_LIB_VERSION 2
# define RC_SEQ_LIB_REVISION 1
2014-12-19 08:45:50 -08:00
# define STR(s) #s
# define MAKE_TEXT_VER_REV(Ver,Rev) (char*)(STR(Ver)"."STR(Rev))
2014-12-23 13:07:19 -08:00
# define LIB_TEXT_VERSION_REVISION MAKE_TEXT_VER_REV(RC_SEQ_LIB_VERSION,RC_SEQ_LIB_REVISION) /* Make Full version as a string "Ver.Rev" */
2014-12-19 08:45:50 -08:00
/* A Set of Macros for bit manipulation */
# define SET_BIT(Value,BitIdx) (Value)|= (1<<(BitIdx))
# define CLR_BIT(Value,BitIdx) (Value)&=~(1<<(BitIdx))
# define TST_BIT(Value,BitIdx) ((Value)&(1<<(BitIdx)))
/* Servo refresh interval in ms (do not change this value, this one allows "round" values) */
# define REFRESH_INTERVAL_MS 20L
/* A pulse shall be valid during XXXX_PULSE_CHECK_MS before being taken into account */
2014-12-23 13:07:19 -08:00
# define STICK_PULSE_CHECK_MS 150L
# define KBD_PULSE_CHECK_MS 50L
2014-12-19 08:45:50 -08:00
/* Free servo Indicator */
# define NO_SEQ_LINE 255
/* Free Position Indicator */
# define NO_POS 255
/* The macro below computes how many refresh to perform while a duration in ms */
# define REFRESH_NB(DurationMs) ((DurationMs) / REFRESH_INTERVAL_MS)
/* The motion goes from StartInDegrees to EndInDegrees and will take MotionDurationMs in ms */
# define STEP_IN_DEGREES_PER_REFRESH(StartInDegrees,EndInDegrees,MotionDurationMs) (EndInDegrees-StartInDegrees) / REFRESH_NB(MotionDurationMs)
/* A set of Macros to read an (u)int8_t (Byte), an (u)int16_t (Word) in program memory (Flash memory) */
# define PGM_READ_8(FlashAddr) pgm_read_byte(&(FlashAddr))
# define PGM_READ_16(FlashAddr) pgm_read_word(&(FlashAddr))
# define PGM_READ_32(FlashAddr) pgm_read_dword(&(FlashAddr))
/*
2014-12-23 13:07:19 -08:00
STICK TYPE : ( dead zone expected at the middle )
2014-12-19 08:45:50 -08:00
= = = = = = = = = =
Pos 0 1 2 3
| - - - | - | - - - | - - | - - - | - | - - - |
1000u s 2000u s ( Typical Pulse Width values )
2014-12-23 13:07:19 -08:00
MULTI_POS_SW : ( Middle area active as well )
= = = = = = = = = = = =
Pos 0 1 2 3 4
| - - - | - | - - - | - | - - - | - | - - - | - | - - - |
1000u s 2000u s ( Typical Pulse Width values )
2014-12-19 08:45:50 -08:00
*/
2015-01-14 18:08:45 -08:00
# 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)))
2014-12-19 08:45:50 -08:00
typedef struct {
int8_t InProgress ;
int8_t CmdIdx ;
int8_t Pos ;
uint32_t StartChronoMs ;
void * TableOrShortAction ;
uint8_t SequenceLength ;
uint8_t ShortActionMap ;
2015-01-14 18:08:45 -08:00
# ifdef RC_SEQ_CONTROL_SUPPORT
uint8_t ( * Control ) ( uint8_t Action , uint8_t SeqIdx ) ;
# endif
2014-12-19 08:45:50 -08:00
} CmdSequenceSt_t ;
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
typedef struct {
int8_t Idx ;
uint32_t StartChronoMs ;
} PosST_t ;
typedef struct {
2015-01-14 18:08:45 -08:00
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 ;
2014-12-19 08:45:50 -08:00
} RcCmdSt_t ;
# endif
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2014-12-19 08:45:50 -08:00
typedef struct {
SoftRcPulseOut Motor ;
uint16_t RefreshNb ; /* Used to store the number of refresh to perform during a servo motion (if not 0 -> Motion in progress) */
uint8_t SeqLineInProgress ;
} ServoSt_t ;
2014-12-23 13:07:19 -08:00
# endif
2014-12-19 08:45:50 -08:00
/*************************************************************************
GLOBAL VARIABLES
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static uint8_t SeqNb ;
static uint8_t ServoNb ;
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
static uint8_t CmdSignalNb ;
static RcCmdSt_t RcChannel [ RC_CMD_MAX_NB ] ;
# endif
# ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2014-12-19 08:45:50 -08:00
static ServoSt_t Servo [ SERVO_MAX_NB ] ;
2014-12-23 13:07:19 -08:00
# endif
2014-12-19 08:45:50 -08:00
static CmdSequenceSt_t CmdSequence [ SEQUENCE_MAX_NB ] ;
# else
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2015-01-14 18:08:45 -08:00
static ServoSt_t * Servo = NULL ;
2014-12-23 13:07:19 -08:00
# endif
2015-01-14 18:08:45 -08:00
static CmdSequenceSt_t * CmdSequence = NULL ;
2014-12-19 08:45:50 -08:00
# endif
/*************************************************************************
PRIVATE FUNCTION PROTOTYPES
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-01-14 18:08:45 -08:00
static uint8_t ExecuteSequence ( uint8_t CmdIdx , uint8_t Pos ) ;
2014-12-19 08:45:50 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
2015-01-14 18:08:45 -08:00
static int8_t GetPos ( uint8_t ChIdx , uint16_t PulseWidthUs ) ;
2014-12-19 08:45:50 -08:00
# endif
//========================================================================================================================
void RcSeq_Init ( void )
{
2015-01-14 18:08:45 -08:00
SeqNb = 0 ;
ServoNb = 0 ;
2014-12-19 08:45:50 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
2015-01-14 18:08:45 -08:00
for ( uint8_t ChIdx = 0 ; ChIdx < RC_CMD_MAX_NB ; ChIdx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
RcChannel [ ChIdx ] . Pos . Idx = NO_POS ;
2014-12-19 08:45:50 -08:00
}
# endif
# ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
2015-01-14 18:08:45 -08:00
for ( uint8_t SeqIdx = 0 ; SeqIdx < SEQUENCE_MAX_NB ; SeqIdx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
CmdSequence [ SeqIdx ] . InProgress = 0 ;
CmdSequence [ SeqIdx ] . TableOrShortAction = NULL ;
CmdSequence [ SeqIdx ] . SequenceLength = 0 ;
CmdSequence [ SeqIdx ] . ShortActionMap = 0 ;
2014-12-19 08:45:50 -08:00
}
# endif
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
TinyPinChange_Init ( ) ;
# endif
}
//========================================================================================================================
uint8_t RcSeq_LibVersion ( void )
{
2014-12-23 13:07:19 -08:00
return ( RC_SEQ_LIB_VERSION ) ;
2014-12-19 08:45:50 -08:00
}
//========================================================================================================================
uint8_t RcSeq_LibRevision ( void )
{
2014-12-23 13:07:19 -08:00
return ( RC_SEQ_LIB_REVISION ) ;
2014-12-19 08:45:50 -08:00
}
//========================================================================================================================
char * RcSeq_LibTextVersionRevision ( void )
{
return ( LIB_TEXT_VERSION_REVISION ) ;
}
//========================================================================================================================
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2014-12-19 08:45:50 -08:00
void RcSeq_DeclareServo ( uint8_t Idx , uint8_t DigitalPin )
{
# ifdef RC_SEQ_WITH_STATIC_MEM_ALLOC_SUPPORT
2015-01-14 18:08:45 -08:00
if ( Idx < SERVO_MAX_NB )
2014-12-19 08:45:50 -08:00
{
Servo [ Idx ] . Motor . attach ( DigitalPin ) ;
2015-01-14 18:08:45 -08:00
Servo [ Idx ] . SeqLineInProgress = NO_SEQ_LINE ;
if ( ServoNb < ( Idx + 1 ) ) ServoNb = ( Idx + 1 ) ;
2014-12-19 08:45:50 -08:00
}
# else
2015-01-14 18:08:45 -08:00
if ( Idx < SERVO_MAX_NB )
2014-12-19 08:45:50 -08:00
{
ServoNb + + ;
2015-01-14 18:08:45 -08:00
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 ;
2014-12-19 08:45:50 -08:00
}
# endif
}
2015-01-14 18:08:45 -08:00
//========================================================================================================================
void RcSeq_ServoWrite ( uint8_t Idx , uint16_t Angle )
{
if ( Idx < SERVO_MAX_NB )
{
Servo [ Idx ] . Motor . write ( Angle ) ;
}
}
2014-12-23 13:07:19 -08:00
# endif
2014-12-19 08:45:50 -08:00
//========================================================================================================================
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
void RcSeq_DeclareSignal ( uint8_t Idx , uint8_t DigitalPin )
{
2015-01-14 18:08:45 -08:00
if ( Idx < RC_CMD_MAX_NB )
2014-12-19 08:45:50 -08:00
{
RcChannel [ Idx ] . Pulse . attach ( DigitalPin ) ;
CmdSignalNb + + ;
}
}
//========================================================================================================================
2015-01-14 18:08:45 -08:00
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 )
2014-12-19 08:45:50 -08:00
{
RcChannel [ ChIdx ] . Type = Type ;
RcChannel [ ChIdx ] . PosNb = PosNb ;
RcChannel [ ChIdx ] . PulseMinUs = PulseMinUs ;
RcChannel [ ChIdx ] . PulseMaxUs = PulseMaxUs ;
RcChannel [ ChIdx ] . StepUs = STEP ( PulseMinUs , PulseMaxUs , PosNb , Type ) ;
RcChannel [ ChIdx ] . KeyMap = KeyMap ;
}
//========================================================================================================================
2015-01-14 18:08:45 -08:00
void RcSeq_DeclareCustomKeyboard ( uint8_t ChIdx , const KeyMap_t * KeyMapTbl , uint8_t KeyNb )
2014-12-19 08:45:50 -08:00
{
RcSeq_DeclareKeyboardOrStickOrCustom ( ChIdx , RC_CMD_CUSTOM , 0 , 0 , KeyMapTbl , KeyNb ) ;
}
# endif
//========================================================================================================================
2015-01-14 18:08:45 -08:00
# 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
2014-12-19 08:45:50 -08:00
{
uint8_t Idx , ServoIdx ;
uint16_t StartInDegrees ;
uint32_t StartMinMs [ SERVO_MAX_NB ] ;
2015-01-14 18:08:45 -08:00
# 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 + + )
2014-12-19 08:45:50 -08:00
{
if ( ! CmdSequence [ Idx ] . TableOrShortAction )
{
2015-01-14 18:08:45 -08:00
# 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
2014-12-19 08:45:50 -08:00
SeqNb + + ;
break ;
}
}
# endif
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2014-12-19 08:45:50 -08:00
/* Get initial pulse width for each Servo */
2015-01-14 18:08:45 -08:00
for ( Idx = 0 ; Idx < SERVO_MAX_NB ; Idx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
StartMinMs [ Idx ] = 0xFFFFFFFF ;
2014-12-19 08:45:50 -08:00
}
2015-01-14 18:08:45 -08:00
for ( Idx = 0 ; Idx < SequenceLength ; Idx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
ServoIdx = ( int8_t ) PGM_READ_8 ( Table [ Idx ] . ServoIndex ) ;
if ( ServoIdx ! = 255 )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
if ( ( uint32_t ) PGM_READ_32 ( Table [ Idx ] . StartMotionOffsetMs ) < = StartMinMs [ ServoIdx ] )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
StartMinMs [ ServoIdx ] = ( uint32_t ) PGM_READ_32 ( Table [ Idx ] . StartMotionOffsetMs ) ;
StartInDegrees = ( uint16_t ) PGM_READ_8 ( Table [ Idx ] . StartInDegrees ) ;
Servo [ ServoIdx ] . Motor . write ( StartInDegrees ) ;
2014-12-19 08:45:50 -08:00
}
}
}
2014-12-23 13:07:19 -08:00
# endif
2014-12-19 08:45:50 -08:00
}
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
//========================================================================================================================
2015-01-14 18:08:45 -08:00
void RcSeq_DeclareCommandAndShortAction ( uint8_t CmdIdx , uint8_t Pos , void ( * ShortAction ) ( void ) )
2014-12-19 08:45:50 -08:00
{
uint8_t Idx ;
2015-01-14 18:08:45 -08:00
# 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 + + )
2014-12-19 08:45:50 -08:00
{
if ( ! CmdSequence [ Idx ] . TableOrShortAction )
{
2015-01-14 18:08:45 -08:00
# 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
2014-12-19 08:45:50 -08:00
SeqNb + + ;
break ;
}
}
# endif
}
# endif
//========================================================================================================================
2015-01-14 18:08:45 -08:00
uint8_t RcSeq_LaunchSequence ( const SequenceSt_t * Table )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
uint8_t Idx , Ret = 0 ;
for ( Idx = 0 ; Idx < SEQUENCE_MAX_NB ; Idx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
if ( CmdSequence [ Idx ] . TableOrShortAction = = ( void * ) Table )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
Ret = ExecuteSequence ( CmdSequence [ Idx ] . CmdIdx , CmdSequence [ Idx ] . Pos ) ;
2014-12-19 08:45:50 -08:00
break ;
}
}
return ( Ret ) ;
}
//========================================================================================================================
void RcSeq_Refresh ( void )
{
2015-01-14 18:08:45 -08:00
static uint32_t NowMs = millis ( ) ;
static uint32_t StartChronoInterPulseMs = millis ( ) ;
2014-12-19 08:45:50 -08:00
SequenceSt_t * SequenceTable ;
void ( * ShortAction ) ( void ) ;
int8_t ShortActionCnt ;
uint8_t ServoIdx ;
uint32_t MotionDurationMs , StartOfSeqMs , EndOfSeqMs , Pos ;
uint16_t StartInDegrees , EndInDegrees ;
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
2014-12-23 13:07:19 -08:00
uint8_t ChIdx ;
int8_t CmdPos ; /* Shall be signed */
2014-12-19 08:45:50 -08:00
uint32_t RcPulseWidthUs ;
/* Asynchronous RC Command acquisition */
2015-01-14 18:08:45 -08:00
for ( ChIdx = 0 ; ChIdx < CmdSignalNb ; ChIdx + + )
2014-12-19 08:45:50 -08:00
{
if ( ! RcChannel [ ChIdx ] . Pulse . available ( ) ) continue ; /* Channel not used or no pulse received */
2015-01-14 18:08:45 -08:00
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 )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
if ( RcChannel [ ChIdx ] . Pos . Idx ! = CmdPos )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
RcChannel [ ChIdx ] . Pos . Idx = CmdPos ;
RcChannel [ ChIdx ] . Pos . StartChronoMs = millis ( ) ;
2014-12-19 08:45:50 -08:00
}
else
{
2015-01-14 18:08:45 -08:00
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 */
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
ExecuteSequence ( ChIdx , CmdPos ) ;
RcChannel [ ChIdx ] . Pos . Idx = NO_POS ;
2014-12-19 08:45:50 -08:00
}
}
}
else
{
2015-01-14 18:08:45 -08:00
RcChannel [ ChIdx ] . Pos . Idx = NO_POS ;
2014-12-19 08:45:50 -08:00
}
}
# endif
2015-01-14 18:08:45 -08:00
NowMs = millis ( ) ;
2014-12-23 13:07:19 -08:00
if ( ( NowMs - StartChronoInterPulseMs ) > = 20UL )
2014-12-19 08:45:50 -08:00
{
/* We arrive here every 20 ms */
/* Asynchronous Servo Sequence management */
2015-01-14 18:08:45 -08:00
for ( int8_t Idx = 0 ; Idx < SeqNb ; Idx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
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) */
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
SequenceTable = ( SequenceSt_t * ) CmdSequence [ Idx ] . TableOrShortAction ;
ServoIdx = ( int8_t ) PGM_READ_8 ( SequenceTable [ SeqLine ] . ServoIndex ) ;
2014-12-19 08:45:50 -08:00
# ifdef RC_SEQ_WITH_SHORT_ACTION_SUPPORT
2015-01-14 18:08:45 -08:00
if ( ServoIdx = = 255 ) /* Not a Servo: it's a short Action to perform only if not already done */
2014-12-19 08:45:50 -08:00
{
ShortActionCnt + + ;
2015-01-14 18:08:45 -08:00
StartOfSeqMs = CmdSequence [ Idx ] . StartChronoMs + ( int32_t ) PGM_READ_32 ( SequenceTable [ SeqLine ] . StartMotionOffsetMs ) ;
if ( ( NowMs > = StartOfSeqMs ) & & ! TST_BIT ( CmdSequence [ Idx ] . ShortActionMap , ShortActionCnt ) )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
ShortAction = ( void ( * ) ( void ) ) PGM_READ_16 ( SequenceTable [ SeqLine ] . ShortAction ) ;
2014-12-19 08:45:50 -08:00
ShortAction ( ) ;
2015-01-14 18:08:45 -08:00
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 ) )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
CmdSequence [ Idx ] . InProgress = 0 ;
CmdSequence [ Idx ] . ShortActionMap = 0 ; /* Mark all Short Action as not performed */
2014-12-19 08:45:50 -08:00
}
}
continue ;
}
# endif
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2015-01-14 18:08:45 -08:00
if ( Servo [ ServoIdx ] . RefreshNb & & SeqLine ! = Servo [ ServoIdx ] . SeqLineInProgress )
2014-12-19 08:45:50 -08:00
{
continue ;
}
2015-01-14 18:08:45 -08:00
StartOfSeqMs = CmdSequence [ Idx ] . StartChronoMs + ( int32_t ) PGM_READ_32 ( SequenceTable [ SeqLine ] . StartMotionOffsetMs ) ;
2014-12-19 08:45:50 -08:00
MotionDurationMs = ( int32_t ) PGM_READ_32 ( SequenceTable [ SeqLine ] . MotionDurationMs ) ;
EndOfSeqMs = StartOfSeqMs + MotionDurationMs ;
2015-01-14 18:08:45 -08:00
if ( ! Servo [ ServoIdx ] . RefreshNb & & Servo [ ServoIdx ] . SeqLineInProgress = = NO_SEQ_LINE )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
if ( ( NowMs > = StartOfSeqMs ) & & ( NowMs < = EndOfSeqMs ) )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
Servo [ ServoIdx ] . SeqLineInProgress = SeqLine ;
StartInDegrees = ( uint16_t ) PGM_READ_8 ( SequenceTable [ SeqLine ] . StartInDegrees ) ;
Servo [ ServoIdx ] . RefreshNb = REFRESH_NB ( MotionDurationMs ) ;
Servo [ ServoIdx ] . Motor . write ( StartInDegrees ) ;
2014-12-19 08:45:50 -08:00
}
}
else
{
/* A sequence line is in progress: update the next position */
2015-01-14 18:08:45 -08:00
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 )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
Servo [ ServoIdx ] . SeqLineInProgress = NO_SEQ_LINE ;
2014-12-19 08:45:50 -08:00
/* Last servo motion and refresh = 0 -> End of Sequence */
2015-01-14 18:08:45 -08:00
if ( SeqLine = = ( CmdSequence [ Idx ] . SequenceLength - 1 ) )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
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
2014-12-19 08:45:50 -08:00
}
}
}
2014-12-23 13:07:19 -08:00
# endif
2014-12-19 08:45:50 -08:00
}
}
2014-12-23 13:07:19 -08:00
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_OUT_SUPPORT
2014-12-19 08:45:50 -08:00
SoftRcPulseOut : : refresh ( 1 ) ; /* Force Refresh */
2014-12-23 13:07:19 -08:00
# endif
2015-01-14 18:08:45 -08:00
StartChronoInterPulseMs = millis ( ) ;
2014-12-19 08:45:50 -08:00
}
}
//========================================================================================================================
// PRIVATE FUNCTIONS
//========================================================================================================================
2015-01-14 18:08:45 -08:00
static uint8_t ExecuteSequence ( uint8_t CmdIdx , uint8_t Pos )
2014-12-19 08:45:50 -08:00
{
void ( * ShortAction ) ( void ) ;
2015-01-14 18:08:45 -08:00
uint8_t Idx , Ret = 0 ;
2014-12-19 08:45:50 -08:00
2015-01-14 18:08:45 -08:00
for ( Idx = 0 ; Idx < SeqNb ; Idx + + )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
if ( ( CmdSequence [ Idx ] . CmdIdx = = CmdIdx ) & & ( CmdSequence [ Idx ] . Pos = = Pos ) )
2014-12-19 08:45:50 -08:00
{
# ifdef RC_SEQ_WITH_SHORT_ACTION_SUPPORT
2015-01-14 18:08:45 -08:00
if ( CmdSequence [ Idx ] . TableOrShortAction & & ! CmdSequence [ Idx ] . SequenceLength )
2014-12-19 08:45:50 -08:00
{
/* It's a short action */
2015-01-14 18:08:45 -08:00
ShortAction = ( void ( * ) ( void ) ) CmdSequence [ Idx ] . TableOrShortAction ;
2014-12-19 08:45:50 -08:00
ShortAction ( ) ;
2015-01-14 18:08:45 -08:00
Ret = 1 ;
2014-12-19 08:45:50 -08:00
}
else
# endif
{
/* It's a Table of Sequence */
2015-01-14 18:08:45 -08:00
if ( ! CmdSequence [ Idx ] . InProgress )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
# 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
2014-12-19 08:45:50 -08:00
}
}
break ;
}
}
2015-01-14 18:08:45 -08:00
return ( Ret ) ;
2014-12-19 08:45:50 -08:00
}
//========================================================================================================================
# ifdef RC_SEQ_WITH_SOFT_RC_PULSE_IN_SUPPORT
2015-01-14 18:08:45 -08:00
static int8_t GetPos ( uint8_t ChIdx , uint16_t PulseWidthUs )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
int8_t Idx , Ret = - 1 ;
uint16_t PulseMinUs , PulseMaxUs ;
2014-12-19 08:45:50 -08:00
2015-01-14 18:08:45 -08:00
for ( Idx = 0 ; Idx < RcChannel [ ChIdx ] . PosNb ; Idx + + )
2014-12-19 08:45:50 -08:00
{
switch ( RcChannel [ ChIdx ] . Type )
{
2014-12-23 13:07:19 -08:00
case RC_CMD_STICK : /* No break: normal */
case RC_CMD_MULTI_POS_SW :
2015-01-14 18:08:45 -08:00
if ( ( RcChannel [ ChIdx ] . Type = = RC_CMD_MULTI_POS_SW ) | | ( ( RcChannel [ ChIdx ] . Type = = RC_CMD_STICK ) & & ( Idx < ( RcChannel [ ChIdx ] . PosNb / 2 ) ) ) )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
PulseMinUs = RcChannel [ ChIdx ] . PulseMinUs + KEY_MIN_VAL ( Idx , RcChannel [ ChIdx ] . StepUs ) ;
PulseMaxUs = RcChannel [ ChIdx ] . PulseMinUs + KEY_MAX_VAL ( Idx , RcChannel [ ChIdx ] . StepUs ) ;
2014-12-19 08:45:50 -08:00
}
else
{
2015-01-14 18:08:45 -08:00
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 ) ;
2014-12-19 08:45:50 -08:00
}
break ;
2014-12-23 13:07:19 -08:00
case RC_CMD_CUSTOM :
2015-01-14 18:08:45 -08:00
PulseMinUs = ( uint16_t ) PGM_READ_16 ( RcChannel [ ChIdx ] . KeyMap [ Idx ] . Min ) ;
PulseMaxUs = ( uint16_t ) PGM_READ_16 ( RcChannel [ ChIdx ] . KeyMap [ Idx ] . Max ) ;
2014-12-23 13:07:19 -08:00
break ;
2014-12-19 08:45:50 -08:00
}
2015-01-14 18:08:45 -08:00
if ( ( PulseWidthUs > = PulseMinUs ) & & ( PulseWidthUs < = PulseMaxUs ) )
2014-12-19 08:45:50 -08:00
{
2015-01-14 18:08:45 -08:00
Ret = Idx ;
2014-12-19 08:45:50 -08:00
break ;
}
}
return ( Ret ) ;
}
# endif
//========================================================================================================================