Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 19 of 19

Thread: How do I get started programming for Teensy... without using any libraries?

  1. #1
    Junior Member
    Join Date
    Oct 2021
    Posts
    8

    How do I get started programming for Teensy... without using any libraries?

    How do I get started programming for Teensy... without using any libraries?

    I've made and flashed sketches to Teensy 3.2 (and Arduino Mega) using the Arduino IDE, and I also feel that I know C++ well enough to build desktop command line utilities.

    I want to learn microcontroller programming, but I feel like I am missing "basic" information about what is going on because the Arduino tools and libraries are hiding things and doing things automatically, and I want to understand what is actually going on.

    For desktop C++, I'm using CodeLite on Debian 11 with the GCC compiler.

    And what I would like to do, as a learning exercise, if possible, is write the simplest program possible (for a Teensy 3.2) that blinks a pixel on an OLED, but using the following rules:

    1. Write code using only .cpp and .h files. No .ino or Arduino specific files.
    2. If possible, compile the code without using the Arduino IDE. Can I just use GCC in Codelite (or command line) to cross compile a flashable .hex?
    3. Existing Teensy or Arduino headers or libraries are NOT allowed. No Arduino.h or anything else.
    4. Any C++ standard library ie. std::array, std::vector, etc IS allowed. (if it works on ARM Cortex processors)

    I'm interested in working only at the C++ (or maybe C) level, no assembler, but I want to avoid using frameworks and libraries so I understand how the code I write actually talks to the microcontroller without framework abstraction.

    I don't think pinMode() and digitalWriteFast() actually exist in the chip. There's probably something else going on, at the C++ level to make these functions work, that are being hidden away by the Arduino framework.

    And I want to understand what is happening at that level.

    So I want to write the most basic program possible that initializes the ARM Cortex, and instructs it to communicate over SPI to a SH1106 OLED controller IC, *without* using existing libraries that hide how the protocol works, or hide what actually needs to be done to work with a microcontroller directly.

    Unfortunately, it's hard to get information on this, because everything "Arduino" seems to be based on opening the Arduino IDE and including SomoneElsesLibrary.h. I've been searching a lot online, but only finding bits and pieces of information.

    I've also tried looking at source code, but there's lots of arrays or structs full of hex values, without any explanation of what those values actually do. I can recognize a bitshift operator, but that doesn't help me learn what's happening without understanding what is actually being shifted and why. (Because bits only have meaning when they represent things that are known).

    I think there's several separate things that I need to figure out, and I would appreciate if anyone could point me to resources on them:

    1. What tools do I need for a Linux based C++ to ARM workflow, (without using Arduino tools like the Arduino IDE)? Assume that I have built Linux command line programs for Linux x86_64 in CodeLite with GCC, and have flashed Teensy/Arduino from the Arduino IDE, but have no other microcontroller experience. (I don't know how to cross-compile, but I know it's a thing).

    2. Where can I get the data sheet that I need for the ARM Cortex M4? I went to ARM's website and got several PDF's, and they are technical, but I don't think they are meant for software programmers. Is there documentation that lists the basic instructions that C++ can "send" to the chip? I'm honestly not sure how that works. If I want to tell a pin to go high, there has to be something in my code that tells the chip to do that, and I doubt that its pretty function like "DigitalWrite()". I'm sure there's a list of chip-level functions somewhere and how to invoke them from code.

    I have a Teensy 3.2 on a PCB with a known working SH1106 OLED. I've tested it with known working firmware (built in the Arduino IDE) and the hardware is fine. Now I want to erase it and start from scratch. Actual scratch. Because I think it's important to learn how these chips actually work, and how SPI actually works, without the abstractions that come from other people's libraries and frameworks.

    I'm sure there's already information out there on this, but I'm not finding a comprehensive source for the information I need.

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,258
    For T_3.2 see here for the manuals on the processor: teensy32.html#tech
    > similar pages exist for the other ARM processors in use on their product page.

    As far as #'s 1-4
    > An INO is a CPP file, you can bypass the default include and preprocessing of Arduino by making an empty 'nothing.ino' in a folder called 'nothing'
    > Start adding your c/cpp/h source files as desired where they have functions: setup() and loop()
    > The compiler in use installed by TeensyInstaller is : "GCC"

    That will build with the IDE and do nothing, except if USB type Serial was selected - it will allow printing to Serial Monitor - except build on YEARS of work by PJRC powering up and making the ARM processor ready to use.

    To blink an LED - find the address and MUX and setup steps as defined in the manual.

    If Windows were in use - not linux : To do it without opening the IDE - that has to be installed and then TeensyInstaller to bring in the details and build tools (gcc, etc) of Teensy you can try : github.com/Defragster/Tset
    > hopefully the readme is complete to set up TSET to run the command line builder interface to Arduino IDE so it can stay closed, and use TyCommander for Uploading and Serial Monitor in conjunction with the batch file system TSET presents/creates.

    To do it with a makefile - one is included with the TeensyInstaller and some notes are on PJRC.com.

    As far as using SPI - that is another course reading in the Manual - and the Device Doc's to figure out command interaction with that.

    When TeensyInstaller runs it puts ALL the sources on the computer for reference.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    First, the easy part, technical documentation. On the Teensy 3.2 product page, scroll down to Technical Information. The reference manual has all the details about the on-chip peripherals. The "Definitive Guide..." book isn't free, but if you want to get into the low-level details of Cortex-M4, you really need that book.

    The next easy part is compiling from command line. Teensyduino puts a sample Makefile deep inside the Arduino folder. Look for it in hardware/teensy/avr/cores/teensy3. Maybe edit the file to configure a few things, and then just run "make" from the command line. It will compile all the core library files. Once you get it to produce a HEX file and you can use Teensy Loader to get it onto your Teensy 3.2, maybe start with editing in main.cpp.

    Now for the definitely not easy part. Starting from scratch probably is not a feasible path. Instead, I'd recommend trying to whittle down the core library code. Lots of stuff, like tone, analogWrite, hardware serial can be deleted if you're not using it. I recommend approaching this like a tough video game where you make *lots* of save points, so you can go back and try again when you realize you took a wrong path and got trapped.

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    Here are some links to people who've aspired to do basically the same thing, write every line of code from scratch.

    https://disconnected.systems/blog/ba...he-teensy-3.1/

    https://gist.github.com/mdaffin/d6fb7e91aa21d6943ef4

    https://forum.pjrc.com/threads/55089...r-from-scratch

    https://eyl.io/blog/teensy-from-absolute-scratch-idea/

    https://eyl.io/blog/teensy-32-disass...ng-led-part-1/

    I recall one from years ago, but sadly can't find it anymore, where someone actually got the USB to start up and wrote a lengthy blog article about it.

    This stuff is possible. I brought up Teensy 3.0 from scratch starting with a Teensy 2.0 bitbanging the "ezport" protocol, and I brought up Teensy 4.0 from scratch using a variety of tools I've made for Teensy 3.x development. But even with many years of experience, it is very difficult. I spent several months before I could get FlexSPI to write to the flash memory (that was with early chips NXP supplied... before their SDK was published). So it is possible to do, but even with a lot of experience the process takes years, especially for supporting all the complex peripherals.

  5. #5
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    Thanks for the replies,

    To clarify, my goal isn't to rebuild the existing Arduino or Teensy libraries from scratch. I just want to understand how the code works for several reasons:

    1. If I order an IC for a project and there's no library for it, I would like to know how to write those libraries.
    2. If a library doesn't work or needs ported, I would like to be able to do that (Arduino DMX libraries all seem processor specific, for example).
    3. To understand what is necessary, so I know where it makes sense to reduce code size by cutting things I don't need.
    4. To understand what specs I need to look for and consider when creating PCBs and selecting components for projects.

    I thought that trying to write something "simple" by hand would be a good way to learn.

    For example, when I wanted to get serious about learning HTML, I created webpages using notepad, rather than a tool like Dreamweaver. Later in life, I wrote PHP scripts that generated the HTML, but I could only get to that point by learning HTML, which required me to not use someone else's HTML generation tool and do a bit of it by hand first. And then I got good at PHP by building my own framework, rather than using one that existed.

    So from a position of ignorance, I thought that if the basic C++ program can be written in a few lines:

    Code:
    #include <iostream>
    int main(int argc, char **argv){
       std::cout << "Hello World!\n";
       return 0;
    }

    Then on an ARM, you should be able to do:

    Code:
    #include <arm_m4>
    int main(){
       
       char nCommand {0xf2}; // Pretend this means "set pin value"
       char nPinID {0x01}; // Select Pin one
       bool bPinValue {1}; // Pin High
       
       // Tell the "ARM M4" Object to set a pin with ID of 1 to high:
       arm::m4 << nCommand << nPinID << bPinValue;
       return 0;
    }
    So I may not even be asking the right questions.

    Is the complexity of bootstrapping a Teensy based on having the USB flashing hardware on the board? Or put another way, would it actually be simpler to create a hello world program from scratch if the microcontroller was programed using a separate USB JTAG flasher? I ordered an ARM-USB-OCD-H a while ago to do a learning project, but haven't used it yet because I haven't been able to find any of the chips in stock.

    Would it be correct to think of the files in the `/hardware/teensy/avr/cores/teensy3/` folder as the operating system that my program runs on? Are these analogous to kernel modules?

    Or to put it another way, on a desktop computer, I don't want to build my own operating system. I just want to build the most basic program that I can that runs on the operating system, without any "Dreamweaver" stuff happening for me inside a framework.

    So on a Teensy platform, (or any ARM) where would I draw the line between "user space" and "kernel space"?

  6. #6
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    Quote Originally Posted by PaulStoffregen View Post
    The next easy part is compiling from command line. Teensyduino puts a sample Makefile deep inside the Arduino folder. Look for it in hardware/teensy/avr/cores/teensy3. Maybe edit the file to configure a few things, and then just run "make" from the command line. It will compile all the core library files. Once you get it to produce a HEX file and you can use Teensy Loader to get it onto your Teensy 3.2, maybe start with editing in main.cpp.
    This seems like a good place to start.

    I've created a folder called TeensyTest, and copied into it all the files from /hardware/teensy/avr/cores/teensy3/.

    I've also installed several packages for gcc-arm cross-compiling: gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu, which also pulled in a binutils for each.

    Now, when I try to run make in the directory, I get:

    Code:
    /hardware/tools/arm/bin/arm-none-eabi-gcc  -Wall -g -Os -mcpu=cortex-m4 -mthumb -MMD -DF_CPU=48000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -DUSING_MAKEFILE -D__MK20DX256__ -DARDUINO=10613 -DTEENSYDUINO=132 -I.  -c -o analog.o analog.c
    make: /hardware/tools/arm/bin/arm-none-eabi-gcc: No such file or directory
    make: *** [<builtin>: analog.o] Error 127
    So the question is, should I change the makefile to use gcc-arm-linux-gnueabi, or do I need to install the arm-none-eabi-gcc package?

    So I opened the makefile and changed the paths as follows:

    Code:
    # OLD names for the compiler programs
    #CC = $(COMPILERPATH)/arm-none-eabi-gcc
    #CXX = $(COMPILERPATH)/arm-none-eabi-g++
    #OBJCOPY = $(COMPILERPATH)/arm-none-eabi-objcopy
    #SIZE = $(COMPILERPATH)/arm-none-eabi-size
    #New Names:
    CC = $(COMPILERPATH)/arm-linux-gnueabi-gcc
    CXX = $(COMPILERPATH)/arm-linux-gnueabi-cpp
    OBJCOPY = $(COMPILERPATH)/arm-linux-gnueabi-objcopy
    SIZE = $(COMPILERPATH)/arm-linux-gnueabi-size
    This was a best guess, and I'm not sure if it's correct.

    I also commented out:
    Code:
    #ifndef NO_ARDUINO
    # Path to your arduino installation
    #ARDUINOPATH ?= ../../../../..
    #ARDUINOPATH ?= ../../../..
    #endif
    to remove the Arduino dependencies. And it started to compile, until it got to the audio library then it crashed. This is the end of the make output:

    Code:
    /usr/bin/arm-linux-gnueabi-cpp -std=gnu++0x -felide-constructors -fno-exceptions -fno-rtti -Wall -g -Os -mcpu=cortex-m4 -mthumb -MMD -DF_CPU=48000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -DUSING_MAKEFILE -D__MK20DX256__ -DARDUINO=10613 -DTEENSYDUINO=132 -I.  -c -o AudioStream.o AudioStream.cpp
    arm-linux-gnueabi-cpp: fatal error: ‘-c’ is not a valid option to the preprocessor
    compilation terminated.
    make: *** [<builtin>: AudioStream.o] Error 1
    So I think I'm close, but something is not quite setup right. There were also two package options, one for hard float and one for soft float, and I'm not sure which I should use.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    You're definitely going in the wrong direction to edit the Makefile so it uses a toolchain which compiles for Linux operating system.

    Use the correct toolchain, which is located in the {Arduino}/hardware/tools folder.
    Last edited by PaulStoffregen; 10-15-2021 at 05:22 AM.

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    If you try swapping out the toolchain Teensyduino provides for any other "arm-none-eabi-gcc", consider the version. We use a fairly old version of arm-none-eabi-gcc. A lot of code probably breaks in subtle ways if compiled on any other version. That's why Teensyduino stay with the same old version, because we all the code has been tested for years on only that version. A few times in Teensy's history we've updated the toolchain to a newer version and it's almost always a painful process.

    Seriously, if you're still just trying to learn how the low-level code works, throwing a different compiler into the mix of unknowns is absolutely the wrong direction.

    In fact, if you really want to learn how things work, you'd probably do much better to just stay with compiling using Arduino and read & play with changes in the core library code. That is where all the details you say you want to learn are found.

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    Quote Originally Posted by lightnb View Post
    So from a position of ignorance, I thought that if the basic C++ program can be written in a few lines:

    Code:
    #include <iostream>
    int main(int argc, char **argv){
       std::cout << "Hello World!\n";
       return 0;
    }

    Then on an ARM, you should be able to do:

    Code:
    #include <arm_m4>
    int main(){
       
       char nCommand {0xf2}; // Pretend this means "set pin value"
       char nPinID {0x01}; // Select Pin one
       bool bPinValue {1}; // Pin High
       
       // Tell the "ARM M4" Object to set a pin with ID of 1 to high:
       arm::m4 << nCommand << nPinID << bPinValue;
       return 0;
    }
    Both of these assume build on top of libraries essentially the same as Arduino's APIs like Serial.print() and digitalWrite().

    "arm::m4" imagines come library has a C++ class or namespace called "arm" which provides a class named "m4" that is a library having Arduino-like functionality with cout-style operator overloading.

    Other than the API looking different (and whether this way of operator overloading would even work) from a practical point of view this imagines the same sort of libraries as Arduino uses, just with C++ operators to call them rather than C-style functions.

  10. #10
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    Thanks Paul,

    I got it to compile without errors by using hardware/tools/arm/bin/ as the compiler path.

    Is it normal for the file command to report a .hex as ASCII?

    Code:
    file main.hex 
    main.hex: ASCII text, with CRLF line terminators
    It does look like a hex representation of binary:

    Code:
    :10001000E1070000E1070000E1070000E107000040
    So arm-none-eabi-gcc is meant for ARM on bare metal, where arm-linux-gnueabi-gcc is meant to create a binary that runs on Linux on an ARM platform (like a Raspberry Pi)?

    I installed the arm-none-eabi-gcc package, and it compiles some of the files, but encounters errors, as you said it might. But specifying hardware/tools/arm/bin/ in the makefile path does seem to compile correctly.

  11. #11
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    For a quick attempt to explain actual low-level programming....

    Consider turning on the LED on Teensy 3.2.

    First, look at the Teensy 3.2 schematic. The LED is connected to Arduino pin 13. If you refer to the schematic, you'll see that pin on the processor is named "PTC5".

    You must read the manual for the MK20 chip for the low-level details. Start with chapter 49 (GPIO) which begins on page 1331. You'll see the GPIO is arranged into five 32 bit ports, called A, B, C, D, E. Since this is PTC5, it's bit #5 in the "C" port.

    On page 1334 you'll find the registers for port C.

    Click image for larger version. 

Name:	screenshot.png 
Views:	14 
Size:	77.0 KB 
ID:	26182

    To turn on the LED, you will need to write to both GPIOC_PDDR to cause the pin to become an output, and to GPIOC_PDOR to actually configure the output (or you could use the set / toggle registers).

    Writing to a hardware register involves defining a volatile pointer and then dereferencing it. The C / C++ syntax looks like this:

    *(volatile uint32_t *)0x400FF008

    You'll see 400FF008 is the documented memory address for the hardware register. When code writes to that specific address, it cause the hardware to change output.

    The (volatile uint32_t *) tells the compiler to typecast that number into a pointer. The volatile keyword means the compiler optimizer will not treat it as normal memory, where it knows a write is unnecessary if you never read it back or use it in any way. The volatile keyword is the essential ingredient of using hardware registers. The first "*" means to dereference the pointer, so if you write this:

    *(volatile uint32_t *)0x400FF008 = (1<<5);

    The compiler will actually write the number 32 to memory address 400FF008.


    So you might expect if you write to both GPIOC_PDDR and GPIOC_PDOR, the LED might turn on. But no, more is needed. Many other important details are lucking elsewhere in the manual.

    Almost all the peripherals default to a low power disable state. If you try to use them before you turn on their power, your code will crash (or trigger a fault exception... one of many important topics covered in that Definitive Guide book).

    To turn on power to PORTC, you need to write to bit 11 of the SIM_SCGC5 register. Turn to page 254 to find that register's documentation. You would do this the same way, by dereferencing a volatile pointer to its address and writing (1<<11).

    But there is still another register needed. Every pin has a configuration register, which selects which of 8 different peripherals will control the pin. Those GPIO registers will have no effect unless you also write to the config register. The specific register you need is PORTC_PCR11, which is documented on page 223 (it's address) and 227 (details which apply to all the config registers).

    So to turn on the LED, you would perform 4 writes, by dereferencing 4 volatile pointers to the 4 specific memory addresses of those hardware registers.


    If you look at the kinetis.h file, you'll see it defines these volatile pointers for every register in the entire reference manual. For example:

    Code:
    #define GPIOC_PDOR              (*(volatile uint32_t *)0x400FF080) // Port Data Output Register
    So if you've included kinetis.h, you can write "GPIOC_PDOR = (1<<5);"

    Hopefully this message, which didn't turn out to be so quick, can at least give you the concept of what actual hardware level programming really is. The reference manual documents the many peripherals in the chip from NXP/Freescale, so you can control everything by reading and writing those hardware registers.

    But not everything is the reference manual. The processor portion comes from ARM. It too has hardware registers (which are also defined in kinetis.h) and to learn how all that stuff works, you really need to read the Definitive Guide book.

    As lengthy as the manual and book are, if you want to program peripherals like USB, so much more learning is required.

    But fundamentally it's all built on top of code which reads and writes hardware registers by dereferencing volatile pointers to the specific documented addresses of those registers. Writing such code, knowing which registers to use and what to write to them, requires reading an immense amount of documentation about how the hardware works. You can look up each peripheral in the reference manual, and read some of the generic chapters (eg, 3 to 15) for info about things that apply to the entire chip to get an idea of what to do. Don't skip getting the Definitive Guide book, as there is much to learn about how the ARM processor works which isn't documented anywhere by NXP.

  12. #12
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    Thank you for explaining that. That was one of the things i was having trouble understanding, is how the CPP code actually tells the MPU to do something.

    I will get a copy of the Definitive Guide book to read. And learn more about volatile.

    I achieved the first goal though (the easy one)! I got the Teensy 3 core files to compile from the command line, then built the cli flashing tool, then used it to flash the hex file. And I now have a blinking light!

    I appreciate your helping me work through this and to better understand how it works. And I think the CLI tools are going to be much faster to use for learning since I can just run make and then flash to test stuff.

  13. #13
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    So I made an attempt at this, and it compiles and uploads, but the light doesn't illuminate.

    I think the part that I'm not understanding is the configuration register. I think we want to set the 3 MUX bits to 001 to make the pin GPIO. But I don't understand why we're writing to PORTC_PCR11 and not PORTC_PCR5 since it's Port C, pin 5 that we want, right? I also tried the address for PORTC_PCR5 but it didn't work either.

    Do I need to set other bits besides the MUX bits in the configuration register?

    This is my attempt:

    Code:
    // We need to toggle Arduino Pin 13, which is PTC5 on the Teensy.
    // See pg. 1334 of K10 sub family manual.
    // Port C direction Register = 0x400FF094
    #define REG_PORT_C_DIRECTION *(volatile int*)0x400FF094
    #define REG_PORT_C_OUTPUT    *(volatile int*)0x400FF080
    
    // System Clock Gating Control Register 5 - Turns on power to certain pins. Port C is bit 11, set to 1 to enable power.
    #define REG_SIM_SCGC5    *(volatile int*)0x40048038
    
    // I don't know why we want Pin Control Register 11, shouldn't it be PCR5 for pin 5?
    #define PORTC_PCR11    *(volatile int*)0x4004B02C
    #define PORTC_PCR5    *(volatile int*)0x4004B014
    
    
    void BusyWait(){
       volatile int nDummy = 0;
       for(int i = 0; i<1000000; i++){nDummy++;}
    };
    
    
    extern "C" int main(void){
    
       // Turn on Power to Port C Pins:
       REG_SIM_SCGC5 = (1 << 11);
       
       PORTC_PCR11 = 0b00000000000000000000000100000000; // Set MUX (bits 8-10) to GPIO Mode = 001.      
       
       // Set Port  C, Pin 5 to output:   
       REG_PORT_C_DIRECTION = (1<<5);
       
       while (1) {
          
          REG_PORT_C_OUTPUT |= (1<<5);
    		
          BusyWait();		
    		
          REG_PORT_C_OUTPUT &= ~(1<<5);
    
          BusyWait();
    
       }
       
    };
    Last edited by lightnb; 10-17-2021 at 04:12 AM. Reason: Added bitwise operators in loop

  14. #14
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    Opps, sorry, my mistake. I typed PORTC_PCR11 but of course it really should be PORTC_PCR5. Wrote that whole thing quickly and didn't proofread.

  15. #15
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,258
    re p#13 - just for ref - to test an IDE install - or if wanting to post code on the forum for others to edits run.

    That code runs and works like this - only add is the Serial Print for fun - and slowed the BusyWait() blink in testing:
    Code:
    // https://forum.pjrc.com/threads/68449-How-do-I-get-started-programming-for-Teensy-without-using-any-libraries?p=291266&viewfull=1#post291266
    // We need to toggle Arduino Pin 13, which is PTC5 on the Teensy.
    // See pg. 1334 of K10 sub family manual.
    // see ...\hardware\teensy\avr\cores\teensy3\kinetis.h
    // Port C direction Register = 0x400FF094
    #define REG_PORT_C_DIRECTION *(volatile int*)0x400FF094
    #define REG_PORT_C_OUTPUT    *(volatile int*)0x400FF080
    
    // System Clock Gating Control Register 5 - Turns on power to certain pins. Port C is bit 11, set to 1 to enable power.
    #define REG_SIM_SCGC5    *(volatile int*)0x40048038
    
    // PCR5 for pin 5 IS led pin 13
    #define PORTC_PCR5    *(volatile int*)0x4004B014
    
    
    void BusyWait() {
      volatile int nDummy = 0;
      for (int i = 0; i < 5000000; i++) {
        nDummy++;
      }
    };
    
    void setup() {    //extern "C" int main(void){
      Serial.begin(115200); // recent TeensyDuino versions will wait up to 2 seconds for connect
      Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
    
      // Turn on Power to Port C Pins:
      REG_SIM_SCGC5 = (1 << 11);
    
      PORTC_PCR5 = 0b00000000000000000000000100000000; // Set MUX (bits 8-10) to GPIO Mode = 001.
    
      // Set Port  C, Pin 5 to output:
      REG_PORT_C_DIRECTION = (1 << 5);
    
      while (1) {
        REG_PORT_C_OUTPUT |= (1 << 5);
        BusyWait();
        REG_PORT_C_OUTPUT &= ~(1 << 5);
        BusyWait();
      }
    };
    
    void loop() {
      // put your main code here, to run repeatedly:
    }

  16. #16
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    So the LED isn't flashing anymore while uploading to the Teensy. And lsusb doesn't show the Teensy as connected to the computer anymore either.

    But interestingly, if I run:

    Code:
    teensy_loader_cli --mcu=TEENSY32 main.hex -w -v
    It still seems to be uploading:

    Code:
    Teensy Loader, Command Line, Version 2.2
    Read "main.hex": 1124 bytes, 0.4% usage
    Waiting for Teensy device...
     (hint: press the reset button)
    Found HalfKay Bootloader
    Read "main.hex": 1124 bytes, 0.4% usage
    Programming..
    Booting
    It will sit and wait until I press the button on the Teensy, then it seems to be writing the program.

    So I'm guessing that part of what gets flashed to the user program space on the Teensy is code that identifies the device type to the computer that it's plugged into. ie "Hi, I'm a keyboard!" or "I'm a MIDI controller". This is normally set with the drop down in Teensyduino. How do I set that flag with teensy_loader_cli? So that the Teensy will show up to the computer in lsusb and in the Arduino IDE again?

  17. #17
    Junior Member
    Join Date
    Oct 2021
    Posts
    8
    Quote Originally Posted by defragster View Post
    That code runs and works like this - only add is the Serial Print for fun - and slowed the BusyWait() blink in testing:
    Is it working for you? Are you using the Arduino IDE? This still isn't working for me, but I'm not using the IDE, just the main.cpp file, plus the mk20dx256.ld file in the same folder which the compiler needs to make the .hex file.

    I still can't get the light to turn on. I've even tried the brute force approach:


    Code:
    #define REG_PORT_C_DIRECTION *(volatile uint32_t*)0x400FF094
    #define REG_PORT_C_OUTPUT    *(volatile uint32_t*)0x400FF080
    #define REG_PORT_C_SET_OUTPUT    *(volatile uint32_t*)0x400FF084
    #define REG_SIM_SCGC5    *(volatile uint32_t*)0x40048038
    #define PORTC_PCR5    *(volatile uint32_t*)0x4004B014
    
    extern "C" int main(void){
    
       REG_SIM_SCGC5 = 0xFFFFFFFF;
       PORTC_PCR5 = 0xFFFFFFFF;
       REG_PORT_C_DIRECTION = 0xFFFFFFFF;
       REG_PORT_C_OUTPUT = 0xFFFFFFFF;
       REG_PORT_C_SET_OUTPUT = 0xFFFFFFFF;
    }
    I think there's probably another register I need to set...


    EDIT: The blinking works fine with the Arduino.h included, and using the Arduino blink sketch with the Arduino functions.
    Last edited by lightnb; 10-17-2021 at 10:55 AM.

  18. #18
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,206
    Quote Originally Posted by lightnb View Post
    This still isn't working for me, but I'm not using the IDE, just the main.cpp file, plus the mk20dx256.ld file in the same folder which the compiler needs to make the .hex file.
    Sounds like you didn't go with the advice to first slowly whittle down the many core library files, but instead just deleted everything. Well, something you deleted was essential. You can figure out what was needed by putting stuff back until it's able to run. (probably stuff in mk20dx128.c)

    You should be aware that most of the support for Teensy revolves around use of all those files and the libraries that come with Teensyduino and a good number of other commonly used libraries. The farther you go down your own path with completely different code, the less I or anyone else will be able to help.

    Hopefully these messages so far on this thread have helped you get started. But you really should understand you'll be increasingly on your own to solve these sorts of problems as your code diverges from the common libraries we all know & use.

  19. #19
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,258
    Quote Originally Posted by lightnb View Post
    Is it working for you? Are you using the Arduino IDE? This still isn't working for me, but I'm not using the IDE, just the main.cpp file, plus the mk20dx256.ld file in the same folder which the compiler needs to make the .hex file.
    ...
    Indeed it was built in the IDE. There is no way anyone can replicate 'the configuration in use' there - that's the value of IDE with PJRC install.

    What that showed was the port setup steps seem to be correct see other(){} below - though there may be some overlapping PJRC housekeeping before setup() is called that is helping?

    To better simulate the configuration desires the Bare_T32.INO now looks like this:
    Code:
    // https://forum.pjrc.com/threads/68449-How-do-I-get-started-programming-for-Teensy-without-using-any-libraries?p=291266&viewfull=1#post291266
    // We need to toggle Arduino Pin 13, which is PTC5 on the Teensy.
    // See pg. 1334 of K10 sub family manual.
    // see ...\hardware\teensy\avr\cores\teensy3\kinetis.h
    
    extern "C" void other();
    void setup() {    //extern "C" int main(void){
      Serial.begin(115200);
      Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
      other();
    }
      void loop() {
      // put your main code here, to run repeatedly:
    }
    And the code for other() is in other.c as:
    Code:
    // Port C direction Register = 0x400FF094
    #define REG_PORT_C_DIRECTION *(volatile int*)0x400FF094
    #define REG_PORT_C_OUTPUT    *(volatile int*)0x400FF080
    
    // System Clock Gating Control Register 5 - Turns on power to certain pins. Port C is bit 11, set to 1 to enable power.
    #define REG_SIM_SCGC5    *(volatile int*)0x40048038
    
    // PCR5 for pin 5 IS led pin 13
    #define PORTC_PCR5    *(volatile int*)0x4004B014
    
    
    void BusyWait() {
      volatile int nDummy = 0;
      for (int i = 0; i < 1000000; i++) {
        nDummy++;
      }
    }
    
    void other() {    //extern "C" int main(void){
       // Turn on Power to Port C Pins:
      REG_SIM_SCGC5 = (1 << 11);
    
      PORTC_PCR5 = 0b00000000000000000000000100000000; // Set MUX (bits 8-10) to GPIO Mode = 001.
    
      // Set Port  C, Pin 5 to output:
      REG_PORT_C_DIRECTION = (1 << 5);
    
      while (1) {
        REG_PORT_C_OUTPUT |= (1 << 5);
        BusyWait();
        REG_PORT_C_OUTPUT &= ~(1 << 5);
        BusyWait();
      }
    }
    That separate .c file gets no help or includes from the IDE preprocessing work done for the INO.

    This is now blinking and dropping BusyWait() count down, restored the FASTER blink.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •