Two seperate programs in a Teensy 3.6

Status
Not open for further replies.

pd0lew

Well-known member
Hi all,

I have two programs each about 3200 lines ( CW and RTTY program) is it possible to have two separate programs in a Teensy 3.6 so that I can choose when startup which program I like to use.

Best,
Johan
 
Probably the easiest way is to combine them and add a new definition for the setup function that runs one or the other based on your choice.
 
Probably the easiest way is to combine them and add a new definition for the setup function that runs one or the other based on your choice.

This is not possible because I have many many lines of code and can not combined.

Perhaps there is an other way I don't have a clue on this moment.......
Thanks for the reply.
Best,
Johan
 
Johan,
This sounds like an XY problem. It's doubtful that you're anywhere close to running out of flash, so what you want could be done pretty easily with a few if() statements. Conditionals would be much easier than what you're proposing.
 
So as it is, you have rtty.ino and cw.ino, each with bunches of functions and data defined, and numerous potential conflicts of naming.

I think you can wrap your existing programs each inside its own c++ namespace and then write a third program which chooses which of the namespaces to activate.

The resulting cw_or_rtty.ino might look like:
Code:
namespace RTTY { /* contents of existing rtty.ino */ }
namespace CW { /* contents of existing cw.ino */ }
void (*loopfunc)(void);
void setup() {
  if (cw_is_selected()) {
    CW::setup();  loopfunc = CW::loop;
  } else if (rtty_is_selected()) {
    RTTY::setup();  loopfunc = RTTY::loop;
  } else {
    /* supply default behavior */
  }
}
void loop() { (*loopfunc)(); }
You can implement the selection however you like: write an eeprom byte before reset, use the paddle state at reboot, add a pushbutton or toggle switch, ask the serial port, etc..

You can also put the RTTY and CW namespaces each in their own .cpp file, which may make for easier editing, and split shared code into a third namespace. Or you might just leave the existing programs as they are and simply:
Code:
namespace RTTY {
#include "../rtty/rtty.ino"
}
namespace CW { 
#include "../cw/cw.ino"
}
I haven't tested any of this except by googling so it may be missing some subtleties about inter-namespace access.

-- rec --
 
Oh, some good points in that code. If you wanted to compile for two different USB device interfaces, you couldn't use the namespaces like I did, because you need to compile and link with the USB interface specified. And if your code is doing global initializations then they'll happen in both namespaces unless you move them into the namespace::setup() function. And some libraries do static initializations when you #include them, so if those were included in both namespaces, they would possibly only be effective in one namespace. They'd need to be pulled outside the namespaces and done once for all variations.

The idea of simply stepping through the different programs on each reset is neat, though, only need the reset button and a byte of eeprom to figure out which program to run each time.

-- rec --
 
you could use a gpio pin to select which program starts/runs, or a comms signal/display touch activation
many possibilities.

If you write the code good enough, you can change back and forth during runtime without any resets
 
All the core main.cpp code does is:
// Arduino's main() function just calls setup() and loop()....
setup();
while (1) {
loop();
yield();
}

Simple to just choose which code is run during setup() and/or loop() as desired. That can be easily replicated from within user code and just never return. And yield() is of minimal or no value if not using any code under serialEvent(), unless EventResponder is in use.

The other day it came up that alternate USB device type was needed - and I showed a direction to do that as well. Assume that isn't an issue here, though with a static update to EEPROM or GPIO on boot that could be read after restarting to even get that.
 
This is merely difficult, and probably many hours of works, not certainly impossible.

This is not possible because I have many many lines of code and can not combined.

Putting 2 programs into the memory isn't impossible either, only difficult (and perhaps a lot of work, maybe, maybe not, depending on your level of skill and ability to troubleshoot tough problems). So the real question is which difficult way to choose. But since you asked for this way, here's my best attempt to answer...

First, you're going to need to make an alternate linker script, or maybe 2 or 3 others. The default one is mk66fx1m0.ld. The important part is this line:

Code:
        FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1024K

This configures where the compiler will locate the code. In one program, you'll need to set "ORIGIN" to someplace not at the beginning of the flash. That will cause the compiler to create a program which can exist in the 2nd part of the memory. While not strictly required, you'll probably want to reduce the LENGTH number, so the 2 can't overlap. But that doesn't actually solve any problem, it only gives you a compiler error which is much easier to understand than "it doesn't work" (which you'll find is a theme with this way.....)

I mentioned possibly 3 files. The next problem you're likely to encounter is code in mk20dx128.c which only works if the hardware is in a freshly reset state. Especially the startup of the crystal & PLL and configuring of clocks probably will not work if you run this code twice.

Maybe you'll make a 3rd very small program which resides at ORIGIN zero, which then runs either of the 2 others. Maybe you'll put all or most of the startup code into the 1st startup program? Or maybe you'll keep that one minimal (running from the RC oscillator, not even the crystal) and then jump to either of the other 2. Or maybe you'll decide to divvy up the mk20dx128.c code, perhaps put the hardware init stuff into the 1st program that starts and the runtime init stuff into the other 2?

Or maybe you'll go with only 2 programs, where 1 always runs first and the first thing it does is check to see if the other should be run. But even that will involve some tricky decisions about what startup stuff to do before the jump.

Making the jump to the other program will also involve some trickery. By default, the first 4 bytes are the initial stack pointer value and the next 4 are the address where the code begins. If you locate the 2nd program at 0x000020000, you can't just put the number 0x000020000 into R15 (the program counter) or otherwise jump to that address. If you use things as they are, you'll need to craft some code to read those first 8 bytes and init the SP first, then shove the next word into PC. For only a single program, the ARM hardware does this as it comes out of reset, but if you want to run another program that's built the same way, you'll need to do it.

Or maybe you'll alter the 2nd linker, or both of the others if using a 3rd small startup program, so they're in a different format where you can just jump to them more easily.

Earlier I mentioned this may or may not be more work depending on your level of skill and ability to troubleshoot tough problems with little or no feedback about what went wrong. I say this from experience of bringing up code on brand new chips... a lot of things go wrong with virtually no info other than "it didn't work". You really have to be extremely familiar with the hardware (which is *really* hard if it's a brand new chip with little in the way of working examples), but still pretty tough when you have a lot of working code to read. When you get a detail wrong like the stack pointer unaligned to 32 bits or trying to jump to an address in a way the ARM core expects one of the "unused" bits to be set to indicate Thumb execution mode, the result is a fault or hard lockup that's really hard to understand what went wrong.

In contrast, the "impossible" task of merging 2 programs with lots of conflicting symbols mostly involves compiler errors. It may seem like an insurmountable number, and many tedious hours of work may be needed to resolve them all (but cleaver use of C++ namespaces might help - again, more skill in certain areas can greatly help), but the upside is a pretty low chance of really tough-to-troubleshoot problems like getting the PLL and CPU clocking into the wrong mode where "nothing works" and you can't figure out why because the chip just ends up in a locked condition where you get pretty much no useful info.

If you're not already very familiar with most of Joseph Yiu's book and assembly language level programming, I'd advise against attempting the linker script and startup code hacking. It may seem easier, but it's a painful path of really thorny problems.
 
Last edited:
If you do decide to dive down the 2 or 3 separately compiled+linked programs approach, another challenge you'll face is Teensy Loader's auto mode. The default usage from Arduino is to upload 1 HEX file, then reboot to it.

You'll have 2 or 3 HEX files. Probably the simplest approach would be to use a program or script (easier on Linux) or just a text editor to combine them. Then you can use Teensy Loader the normal way to open a single HEX file and program it.

The other thing you could do it take Teensy Loader out of Auto mode. Open each HEX file and program it. Then reboot your Teensy.

But the main point you need to know is Teensy has a feature where it completely erases the flash memory when you begin the first programming after it as rebooted into bootloader mode. So you can program 3 HEX files, but only if you do so *without* rebooting between them. If the chip reboots and bootloader mode starts over again, the first HEX file loading you do after rebooting causes full flash erase.

Out of all the technical problems you'll face, this one is fairly minor. HEX files are easy to combine. Just delete the last end-of-data line from all but the last, and concatenate them. The HEX format is ascii text and well documented, so this sort of thing is very easy to do from a script (if using a system like Windows, install mingw or cygwin scripting stuff that normally comes by default on Linux and Mac). If you make multiple copies of Arduino, each with the modified system for each, maybe you could even work such a script into platform.txt using one of the post compile or post linking hooks (if you can find Arduino's documentation about such things...)

There are probably many other little details like this if you go with the 2 or 3 separate programs approach. But most should be documented in the ARM info or Joseph Yiu's book. This one is specific to Teensy, so if you go this route at least knowing about this first-upload-erases feature might save you from some nothing-works-can't-see-why frustration.
 
Status
Not open for further replies.
Back
Top