I prefer to write the code in a editor like VSCode or Vim and compile and flash it via cli.
The Arduino IDE has an "external editor" option in File > Preferences. If you turn that on, Arduino's editor is disabled so you can edit the files with vim or vscode or anything else. It automatically detects when you've saved changes. Of course you can still use Verify, Upload and the Serial Monitor.
Even if you later switch to the command line loader, this way is the easiest to get started and can let you focus on understanding the hardware and existing code before you also dive into customizing the tools and build process. Life is much easier when you don't try to take on too many unfamiliar things all at once!
I have to get a serial connection to the PC. The best would be via USB, but how do I get startet to it? My first thought was to search the MMIO registers for the UART. But I have no clue how to get the UART to the USB Port of the board.
Again, before you deep dive into the code, at least do something like this in Arduino and watch it print to the serial monitor:
Code:
void setup() {
}
void loop() {
Serial.println("Hello World");
delay(500);
}
Even if your end goal it to not use Arduino, at the very least you can copy this into Arduino, select Teensy in Tools > Boards, and upload it to your Teensy. Then select Tools > Ports and open the serial monitor. Having a known good starting point makes all of this so much easier than trying to craft code from scratch.
The code which actually implements these functions is in {Arduino}/hardware/teensy/avr/cores/teensy4. It's
also on github. If you read through the code, you'll find the Print class ultimately calls
usb_serial_write() in usb_serial.c with bytes you're transmitting. Ultimately it's just copying bytes into a transmit buffer and then writing to the transfer descriptor structures in memory to tell the USB hardware what to do.
To understand how the hardware works, you'll need the IMXRT1060 reference manual. It's available here:
https://www.pjrc.com/teensy/datasheets.html
The USB controller uses DMA to read that struct from memory, and the read the data from memory too. If you're used to programming simple peripherals like UARTs where you write to registers, this concept of writing to a chunk of memory may seem strange. You do also write to registers, but mainly just to "prime" the endpoint to start reading from memory. There's also a register to tell the hardware where your top-level data structure is placed in memory. But pretty much everything else is done though the data structures in memory rather than hardware registers. The USB hardware is pretty regularly access the memory to do its work.
That transfer descriptor data format is documented starting on page 2348 with Table 42-61 showing the various fields on page 2349, and which bits the hardware will modify as it works on transmitting the data. You can also find the other in-memory data structures documented on nearby pages.
Of course this is just part of the complete USB code. There's also code in usb.c which initializes the hardware and responds to events and answers the many USB control transfer messages which are required by the USB protocol, using the data defined in usb_desc.c and usb_desc.h (which is how your PC knows it's implementing USB serial).
There's quite a lot of code in there to read, and realistically to make sense of most of the USB stuff you'll probably need to read at least chapters 4, 5, 8 and 9 of the USB spec. Here's a direct link to just the PDF, which you can of course get from
www.usb.org, but it takes a little digging to find it.
https://www.pjrc.com/teensy/beta/usb20.pdf
You can edit any of these files and the Arduino IDE will automatically recompile them. As a quick sanity check, I usually add a syntax error and click Verify, just to make sure I'm editing the right copy that Arduino really is compiling before each upload.
If you really want to start your own HAL completely from scratch, that is theoretically possible. It would be pretty hypocritical of me to say not to do so, since I (and some contributions from KurtE and others) wrote all that core library code from scratch. Nearly 2 years of intense effort was needed to get everything in Arduino working well on Teensy 4. If you're willing to take on such a lengthy project, you certainly can. But you can save yourself a lot of time by leveraging the low-level code which already works.