Hey all,
I'm currently working on porting a pretty sizable Atmel AVR project over to Teensy 4.1.
The First main issue I've run into, is the OLED Screen I'm working with. The AVR Chip I was working with was only running at 16Mhz, so The timing requirements for this screen weren't really that big of an issue.
With the 4.1's significantly higher clock speed, I really have to pay more attention to those timing requirements.
My board layout uses this display in 8bit parallel mode, So I've got 8 data pins, a Data/Command pin, and an enable pin going to the display. Since I can't really write to ports anymore, My plan was to write to a buffer of 16bit Ints, where the bottom 8 bits are the Data pins, and the 9th bit is the Data/command pin(bits 10-16 are unused). Using an interval timer, I can write what gets stored in the buffer to the screen, and the rest of the program can write to the buffer without any serious clashes.
Here is the code for how I expect that to work:
OLED Library (separate File):
Globals struct from GlobalVariables Library(separate File):
Main Code(main.ino):
When Compiling, I get this error:
SO.
The question is, What's the right way to pass a variable to into the function pointer that intervalTimer.begin() expects?
In my operating systems class, we never used variables inside of function pointers, but this article seems to insinuate that you can pass a variable to a function pointer.
I tried setting things up in those code examples this way, but I can't get this to compile.
I do understand that I can just make the function being called by interval timer use a global variable, but sharing global variables across files means using "extern", which feels like it will get very messy with all the global variables that will be flying around in this project.
I could also use the OLED screen in question in SPI or TWI modes, but parallel is significantly faster, and I've already ordered the boards for the parallel layout.
This is kindof different coming from AVR, since you could pass variables to functions called inside of ISR functions, no problem.
I thought of maybe busting out the datasheet, and digging through the interval timer library to see If I could just roll my own interrupt timer, but seeing tables of other function pointers made me think I just needed to figure out how to get this working. Or maybe overload the .begin method in the Interval Timer library? That also sounds messy for distributing this codebase. (I foresee library conflicts If I don't do things exactly the right way.)
Using Visual Micro on Windows.
Here is the repository for the project being ported:
https://github.com/BrutalistInstruments/Tsunami-CS-1
Thanks for reading a long explanation for what is essentially just a syntax question.
I'm currently working on porting a pretty sizable Atmel AVR project over to Teensy 4.1.
The First main issue I've run into, is the OLED Screen I'm working with. The AVR Chip I was working with was only running at 16Mhz, so The timing requirements for this screen weren't really that big of an issue.
With the 4.1's significantly higher clock speed, I really have to pay more attention to those timing requirements.
My board layout uses this display in 8bit parallel mode, So I've got 8 data pins, a Data/Command pin, and an enable pin going to the display. Since I can't really write to ports anymore, My plan was to write to a buffer of 16bit Ints, where the bottom 8 bits are the Data pins, and the 9th bit is the Data/command pin(bits 10-16 are unused). Using an interval timer, I can write what gets stored in the buffer to the screen, and the rest of the program can write to the buffer without any serious clashes.
Here is the code for how I expect that to work:
OLED Library (separate File):
Code:
#include "OLEDLib.h"
#include "globalVariables.h"
uint8_t new_line[4] = { 0x80, 0xA0, 0xC0, 0xE0 };
uint8_t OLEDPinArray[9] = {OLEDData0,OLEDData1,OLEDData2,OLEDData3,OLEDData4,OLEDData5, OLEDData6, OLEDData7, OLEDDataCommand};
void enableCycle(volatile Globals *OLEDGlobals) //called on by interval timer.
{
if (OLEDGlobals->OLEDBuffer[OLEDGlobals->oledReadIndex]) //if this is a non-0 value, continue with interupt.
{
//we need to bring the enable pin high, then wait one micro second, then low.
//this interupt will be happenign every 5 to 10 microseconds. We'll keep the delay in for now, and if it negativley impacts performance, we can get rid of it.
uint16_t toParse = OLEDGlobals->OLEDBuffer[OLEDGlobals->oledReadIndex]; //create a parse variable, so we don't destroy the variable in the buffer. Might be unnecessary.
for (int i= 0; i<9;i++ )
{
digitalWriteFast(OLEDPinArray[i],(toParse&1)); //mask toParse with 1, so we just get the first bit.
toParse = toParse >> 1; //shift toParse down one, so we can get the next bit to write.
}
OLEDGlobals->OLEDBuffer[OLEDGlobals->oledReadIndex] = 0; //reset buffer to 0, now that we have shifted out the data.
OLEDGlobals->oledReadIndex = OLEDGlobals->oledReadIndex + 1; //increment read Index
digitalWriteFast(OLEDEnable, HIGH);
delayMicroseconds(1);
digitalWriteFast(OLEDEnable, LOW); //This pulse sends our data to the screen
}
}
void command(uint8_t c, Globals *OLEDGlobals)
{
uint16_t toBuffer; //this number will be inserted into the buffer, at current buffer index.
toBuffer = c; // since D/C pin is 0, we don't need to shift anything in. bit #8 is just a 0.
OLEDGlobals->OLEDBuffer[OLEDGlobals->oledWriteIndex] = toBuffer;
OLEDGlobals->oledWriteIndex = OLEDGlobals->oledWriteIndex + 1; //incriment write index.
//no need to worry about overflows, we overflow to the next part of the buffer.
}
void data(uint8_t d, Globals *OLEDGlobals)
{
uint16_t toBuffer; //this number will be inserted into the buffer, at current buffer index.
toBuffer = d;
toBuffer = toBuffer | (1 << 8); //this will be our "HIGH" message to the D/C pin
OLEDGlobals->OLEDBuffer[OLEDGlobals->oledWriteIndex] = toBuffer;
OLEDGlobals->oledWriteIndex = OLEDGlobals->oledWriteIndex + 1; //incriment write index.
}
Code:
typedef struct Globals
{
//New Globals for Teensy port.
uint16_t OLEDBuffer[256];
uint8_t oledReadIndex;
uint8_t oledWriteIndex;
}Globals;
Main Code(main.ino):
Code:
#include "OLEDLib.h"
#include "globalVariables.h"
//initialize our global structs.
volatile Pattern currentPattern;
volatile Globals currentGlobals;
volatile Screen screenBank;
IntervalTimer OLEDIntervalTimer; //we may be able to save these in our global struct.
void (*enableCyclePointer)(volatile Globals *OLEDGlobals) = &enableCycle;
void setup() {
initBank(¤tPattern); //set bank to factory defaults
initGlobals(¤tGlobals, 0); //set globals to factory defaults. both of these will change once the eeprom is implemented.
///for current testing, these will stay in here.
initPins();
OLEDIntervalTimer.begin((enableCyclePointer(¤tGlobals)), 6); //This is the Line in Question.
initScreen(¤tGlobals);
}
// the loop function runs over and over again until power down or reset
void loop() {
}
When Compiling, I get this error:
Code:
Teensy4.1Port.ino: 25:66: error: invalid use of void expression
OLEDIntervalTimer.begin((enableCyclePointer(¤tGlobals)), 6);
SO.
The question is, What's the right way to pass a variable to into the function pointer that intervalTimer.begin() expects?
In my operating systems class, we never used variables inside of function pointers, but this article seems to insinuate that you can pass a variable to a function pointer.
I tried setting things up in those code examples this way, but I can't get this to compile.
I do understand that I can just make the function being called by interval timer use a global variable, but sharing global variables across files means using "extern", which feels like it will get very messy with all the global variables that will be flying around in this project.
I could also use the OLED screen in question in SPI or TWI modes, but parallel is significantly faster, and I've already ordered the boards for the parallel layout.
This is kindof different coming from AVR, since you could pass variables to functions called inside of ISR functions, no problem.
I thought of maybe busting out the datasheet, and digging through the interval timer library to see If I could just roll my own interrupt timer, but seeing tables of other function pointers made me think I just needed to figure out how to get this working. Or maybe overload the .begin method in the Interval Timer library? That also sounds messy for distributing this codebase. (I foresee library conflicts If I don't do things exactly the right way.)
Using Visual Micro on Windows.
Here is the repository for the project being ported:
https://github.com/BrutalistInstruments/Tsunami-CS-1
Thanks for reading a long explanation for what is essentially just a syntax question.