The [[Serial console output]] demo has the µC system clock running at its default 16 MHz and the serial connection at 9600 baud. But the STM32F411 could run 6x faster, i.e. 100 MHz, and its serial port will easily support 1 Mbd (well within the limits of a 12 Mbps USB connection).
Let's also enable formatted [printf](https://en.wikipedia.org/wiki/Printf) output and show some system information on startup.
## File: `logf.cpp`
Create a new file called `logf.cpp` in a new folder, containing:
```c++
#include <jee.h>
#include <jee/hal.h>
using namespace jeeh;
#include "defs.h"
int main () {
initBoard();
while (true) {
auto s = cycles::millis() / 1000;
logf("%02d:%02d", s/60, s%60);
cycles::msBusy(1000);
}
}
```
## File: `defs.h`
The configuration code has moved to a separate header file, called `defs.h`:
```c++
namespace serio {
enum { SR=0x00, DR=0x04, BRR=0x08, CR1=0x0C };
void init () {
Pin::config("A9:U7");
RCC(ena::USART1,1) = 1;
USART1[BRR] = (SystemCoreClock/2) / 1'000'000;
USART1[CR1] = (1<<13) | (1<<3); // UE TE
}
void write (void const* ptr, int len) {
for (auto i = 0; i < len; ++i) {
while (!USART1[SR](7)) {} // TXE
USART1[DR] = ((uint8_t const*) ptr)[i];
}
}
}
void jeeh::logWriter (void const* ptr, size_t len) {
serio::write(ptr, len);
}
void initBoard () {
fastClock();
cycles::init();
serio::init();
logf("logf: %s @ %d MHz", SVDNAME, SystemCoreClock / 1'000'000);
}
```
## File: `platformio.ini`
And lastly, there needs to be a `platformio.ini` file, with the following contents:
```ini
[platformio]
src_dir = .
[env]
platform = ststm32
framework = cmsis
board = blackpill_f411ce
lib_deps = jcw/jeeh@7
build_flags =
-std=c++17 -Wall -Wextra -Wpedantic -Werror -Wno-format -DXTAL=25
build_src_filter = +<${PIOENV}.cpp>
monitor_speed = 1000000
[env:logf]
```
The command to build and upload is: `pio run -t upload. As before, a serial port connection needs to be set up in a separate terminal window: `pio device monitor`
## Sample output
As the output shows, this code implements a crude stopwatch:
```text
logf: STM32F411xx @ 100 MHz
00:00
00:01
00:02
00:03
[etc ...]
```
## A few notes
- the above code illustrates how source code tends to be structured for JeeH
- the `jee/hal.h` include file wraps most of JeeH's headers, including `jee/cycles.h`
- `logf()` is a wrapper around `vsnprintf()` - it will append a newline if not present
- note also that `logf()` limits the amount of output to 80 text bytes per call
- the reason for adding a separate `defs.h` include file will become clear later
- note the line "`using namespace jeeh;`" - the `defs.h` include must _follow_ it
- the `jeeh::logWriter()` definition redirects the output to the `serio` interface
- I tend to use short variable names when their use is immediate (`auto s = ...`)
- the [auto](https://en.cppreference.com/w/cpp/language/auto) keyword is modern C++: define the variable type to match its initialiser
## One more change
There's one more change which will help with the many examples coming up next:
In `defs.h`, add these lines at the top:
```c++
// Lines with "CG" control the code-generated parts of this file.
//CG pio
```
And at the end of that same header file, change this:
```c++
logf("logf: %s @ %d MHz", SVDNAME, SystemCoreClock / 1'000'000);
```
To this:
```c++
logf("%s: %s @ %d MHz", PIOENV, SVDNAME, SystemCoreClock / 1'000'000);
```
Then build/upload the code again. The startup greeting will now automatically mention the name of the specific application being run, i.e. it's still `logf: ...` in this example, but it will change as more example apps are added.
The mechanism at play is due to the [[Inline code generator]] in JeeH. It looks for text lines starting with `//CG` and _replaces_ one or more lines with associated information. This only works in `.h` files, and only in the PIO source directory (which was set with `scr_dir = .`). This is also the reason why some boilerplate code was moved into a separate `defs.h` file next to `logs.cpp`. A lot more functionality from the code generator will be used later on.