This is my current version:
.H
// ir_driver.h
#ifndef _IR_DRIVER_h
#define _IR_DRIVER_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <TimerOne.h>
class Ir_driverClass
{
private:
//Variables to hold pin numbers.
static uint8_t _SDI_PIN, _CLK_PIN, _LE_PIN, _OE_PIN, _SDO_PIN;
static uint8_t sdipinio;
//Variables to store frequency and data rates.
//NOTE: Not yet supported!!!!
//unsigned long _DESIRED_FREQUENCY, _DESIRED_DATARATE;
//Variables to hold state of transmission.
static uint8_t* DATA_BUF_ptr;
static uint8_t DATA_BUF_LENGTH;
static uint8_t OEstate, LEstate, SDIstate, CLKstate;
static unsigned int cycleno;
static uint8_t SDIcount, SDIbitno, SDIbyteno;
//ISR that sends the message.
//NOTE: Must be static in order to call as parameter for Timer1.Initialize()
static void sendISR(void);
public:
//Constructor
Ir_driverClass(uint8_t SDI_PIN, uint8_t CLK_PIN, uint8_t LE_PIN, uint8_t OE_PIN, uint8_t SDO_PIN);
//Initialises the class
void init();
//This method is non-blocking.
void sendData(uint8_t* dataToSend, uint8_t length);
};
#endif
.CPP
#include "ir_driver.h"
#include <TimerOne.h>
Ir_driverClass::Ir_driverClass(uint8_t SDI_PIN, uint8_t CLK_PIN, uint8_t LE_PIN, uint8_t OE_PIN, uint8_t SDO_PIN)
{
_SDI_PIN = SDI_PIN;
_CLK_PIN = CLK_PIN;
_LE_PIN = LE_PIN;
_OE_PIN = OE_PIN;
_SDO_PIN = SDO_PIN;
}
void Ir_driverClass::init()
{
pinMode(_SDI_PIN, OUTPUT);
pinMode(_CLK_PIN, OUTPUT);
pinMode(_LE_PIN, OUTPUT);
pinMode(_OE_PIN, OUTPUT);
pinMode(_SDO_PIN, OUTPUT);
//TODO: Calculate interrupt timing and initialize timer accordingly.
Timer1.initialize(17);
}
void Ir_driverClass::sendData(uint8_t* dataToSend, uint8_t length)
{
//Prepare buffers.
DATA_BUF_ptr = dataToSend;
DATA_BUF_LENGTH = length;
//Reset counters before starting transmission.
SDIbitno = 0;
SDIcount = 0;
SDIbyteno = 0;
cycleno = 0;
Timer1.attachInterrupt( sendISR );
}
void Ir_driverClass::sendISR(void)
{
// 24 cycles per bit * 10 bits per byte * number of bytes + 24 to allow for delay before data is latched to output.
if(cycleno >= ((240 * DATA_BUF_LENGTH) + 24))
{
Timer1.detachInterrupt();
bitClear(PORTD, _SDI_PIN);
bitClear(PORTD, _CLK_PIN);
bitClear(PORTD, _LE_PIN);
bitSet(PORTD, _OE_PIN);
return;
}
//CLK
if(cycleno % 2) //If odd cycle.
CLKstate = 1;
else
CLKstate = 0;
//LE
//NOTE: Could not work out why LEcount was implemented as seperate variables from SDIcount so deprecated it.
/*if(LEcount >= 21 && LEcount < 23)
LEstate = 1;
else
LEstate = 0;*/
if(SDIcount >= 21 && SDIcount < 23) //Latch our data to the output buffers after all the data has been shifted in.
LEstate = 1;
else
LEstate = 0;
//SDI
switch(SDIbitno)
{
case 0: //If start bit.
SDIstate = 0;
break;
case 9: //If stop bit.
SDIstate = 1;
break;
default: //Else, fetch the correct bit value.
SDIstate = bitRead(DATA_BUF_ptr[SDIbyteno], SDIbitno - 1);
break;
}
if(SDIcount == 23) //We've counted our 12 cycles for this bit.
//NOTE: 12 is hardcoded due to relationship between encoding frequency (28.8khz) and baud rate (2400bps). 28800 / 2400 = 12.
{
SDIcount = 0; //Reset our SDI cycle counter.
SDIbitno++; //Incremenet our bit counter.
if(SDIbitno == 10) //We've finished this byte
{
SDIbitno = 0; //Reset our bit counter.
//Increment our byte counter.
if(SDIbyteno < DATA_BUF_LENGTH) //NOTE: This is to ensure we don't overflow when message is finished.
SDIbyteno++;
}
}
else
SDIcount++; //Otherwise, go ahead and increment our SDI cycle counter.
//OE
if((cycleno > 24) && (cycleno % 2)) //Oscillate our output enable pin each time the ISR is run, but only once the first bit of data has been shifted to the output.
OEstate = 0;
else
OEstate = 1;
//NOTE: This section prevents LE SDI or CLK being modified once last bit of data has been latched.
if(cycleno > (240 * DATA_BUF_LENGTH))
{
LEstate = 0;
SDIstate = 0;
CLKstate = 0;
}
//NOTE: This is not a nice way of doing it as it means we can't use other PORTD pins!
//TODO: fix this!
PORTD = (OEstate << _OE_PIN) + (LEstate << _LE_PIN) + (SDIstate << _SDI_PIN) + (CLKstate << _CLK_PIN);
//NOTE: Removed from original source. See LE section for reimplementation.
/*if(LEcount >= 23)
LEcount = 0;
else
LEcount++;*/
cycleno++;
}
If I remove the static keywords from the individual variables and functions, and attempt to make the class static
static class Ir_driverClass
I run into:
ir_driver.cpp:In file included from
ir_driver.h:47: error: a storage class can only be specified for objects and functions
ir_driver.cpp:In member function 'void Ir_driverClass::sendData(uint8_t*, uint8_t)'
ir_driver.cpp:42: error: no matching function for call to 'TimerOne::attachInterrupt(<unresolved overloaded function type>)'
TimerOne.h:attachInterrupt(void (*)(), long int)
Error compiling
Having each variable and function static is fine for my purposes, but using the code I listed above, I run into the following error when trying to call the constructor:
ir_driver.cpp.o:In function `Ir_driverClass'
ir_driver.cpp:_SDI_PIN'
ir_driver.cpp:_CLK_PIN'
ir_driver.cpp:_LE_PIN'
ir_driver.cpp:_OE_PIN'
ir_driver.cpp:_SDO_PIN'
Error creating .elf
Where the code that calls the class is:
#include "ir_driver.h"
#include <TimerOne.h>
#define SDI 2
#define CLK 3
#define LE 4
#define OE 5
#define SDO 7
uint8_t data[] = {'H', 'E', 'L', 'L', 'O'};
//Note: Guaranteed to work for these pin numbers. Won't work on anything outside of PORTD.
//Note: Class consumes entirety of PORTD, whether defined here or not!
//TODO: Fix that.
Ir_driverClass ir(SDI, CLK, LE, OE, SDO);
void setup()
{
//ir.init();
}
void loop()
{
//ir.sendData(data, sizeof(data));
delay(1000);
}