-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite documentation, implement board-specific build actions
- Loading branch information
Showing
6 changed files
with
268 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,111 @@ | ||
# Ratel | ||
## Next-generation, zero-cost abstraction microconroller programming in Nim | ||
### Next-generation, zero-cost abstraction microconroller programming in Nim | ||
|
||
This README is a bit light at the moment, please visit | ||
[the website](https://ratel.peterme.net) to see the getting started guide and | ||
documentation! | ||
## Getting started | ||
Getting started with Ratel is fairly simply. First you need to have [Nim](https://nim-lang.org/) installed along with the toolchain for | ||
the controller you want to compile for. This guide is written with the Arduino Uno in mind, so for that board this would be the AVR toolchain. If you | ||
have written code for Arduino before chances are good you already have these tools installed. Once that is done you need to install | ||
Ratel itself: | ||
|
||
``` | ||
nimble install ratel | ||
``` | ||
|
||
If your board is not included in the official distribution you need to grab support for that as well. You should be able to search for all Ratel | ||
based packages in the [package directory](https://nimble.directory/search?query=ratel). In order to compile and build for your board you | ||
will also need the toolchain to compile C code and upload that to your board as well. The details of this should be found with the board support | ||
library. | ||
|
||
Once you have Ratel and your board support installed you need to set up your project. In this tutorial we'll be building for an Arduino Uno, but | ||
this process is pretty much the same no matter the board. Simply create a `config.nims` file in your folder with the content: | ||
|
||
```nim | ||
import boardConf | ||
board "unor3" | ||
avr.any.gcc.path = "/usr/bin" | ||
avr.any.gcc.exe = "avr-gcc" | ||
avr.any.gcc.linkerexe = "avr-gcc" | ||
``` | ||
|
||
This does a couple of things, starting with including the `boardConf` module from Ratel. Then it calls `board` which tells | ||
Ratel that the board we want to build for is the Arduino Uno rev. 3. This will automatically include a set of sane defaults for compiling for this | ||
micro-controller, along with some procedures that we'll get back to later. | ||
|
||
The last three lines are simply telling Nim where to find the specific compiler we need to use for the CPU/OS combination we're using. | ||
These could also be placed in your [global configuration](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files) | ||
as they will only be applied when compiling for these platforms. | ||
|
||
## Writing our code | ||
Now that our project is all set up we need to write some code, the sample from the front page is a good start. Simply save the following code in | ||
a file with the `.nim` extension named the same as the folder it's in. If you're using Nim on the devel branch you can leave out the `main` proc and simply | ||
have everything in the global scope. | ||
|
||
```nim | ||
import board | ||
import board / [times, serial, progmem] | ||
Serial.init(9600.Hz) | ||
Serial.send p"Hello world\n" | ||
Led.output() | ||
while true: | ||
Led.high() | ||
Serial.send p"Led is on\n" | ||
delayMs(1000) | ||
Led.low() | ||
Serial.send p"Led is off\n" | ||
delayMs(1000) | ||
``` | ||
|
||
To write your own code have a browse through the documentation and check out any Ratel modules in Nimble. | ||
|
||
## Compiling and uploading | ||
With the project set up and the code written it is time to compile. When we imported the board configuration earlier we got some tasks for doing | ||
this loaded into our configuration. To run these we use the `ratel` binary. So to build simply run: | ||
|
||
``` | ||
ratel build | ||
``` | ||
|
||
If you missed the sentence earlier about putting your file in a folder of the same name this will fail, but fret not, simply pass the file to | ||
build with the `-f` flag. This command should have created a binary file in the same folder with the same name as your file but without | ||
the extension. In order to check how big the resulting binary is we can simply run: | ||
|
||
``` | ||
ratel size | ||
``` | ||
|
||
Or if you're of the curious kind: | ||
|
||
``` | ||
ratel sizeDetails | ||
``` | ||
|
||
The size breakdown you get from this should be familiar to you if you have done any kind of programming with Arduino, it's the same one which is | ||
written out in the terminal before uploading. It should look something like this: | ||
|
||
``` | ||
AVR Memory Usage | ||
---------------- | ||
Device: atmega328p | ||
Program: 298 bytes (0.9% Full) | ||
(.text + .data + .bootloader) | ||
Data: 0 bytes (0.0% Full) | ||
(.data + .bss + .noinit) | ||
``` | ||
|
||
Now the final step of the process is to upload our code to the controller. You can of course do this manually with `avrdude` but Ratel | ||
comes with a task for this as well: | ||
|
||
``` | ||
ratel upload --device=/dev/ttyACM0 | ||
``` | ||
|
||
For me the board is connected to the USB port at `/dev/ttyACM0` but this might be different for you. | ||
|
||
And that should be it! Your board should now be flashing an LED and printing to the serial terminal. To view the output you can either use the | ||
serial terminal that comes with Arduino if you have that installed, or you can use a number of terminal applications. The easiest might even be to run | ||
`tail -f /dev/ttyACM0`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,17 @@ | ||
# Package | ||
|
||
version = "0.1.0" | ||
version = "0.2.0" | ||
author = "Peter Munch-Ellingsen" | ||
description = "Nim for microcontrollers" | ||
license = "MIT" | ||
srcDir = "src" | ||
installExt = @["nim"] | ||
bin = @["ratel"] | ||
|
||
|
||
# Dependencies | ||
|
||
requires "nim >= 1.6.2" | ||
requires "nim >= 1.6.4" | ||
requires "nimscripter" | ||
requires "compiler" | ||
requires "cligen" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,23 @@ | ||
include "../helpers/avrBoardConf.nim" | ||
|
||
proc build*(file: string) = | ||
exec "nim c -d:danger --os:any " & file | ||
|
||
proc upload*(file: string) = | ||
exec "avr-objcopy -O ihex -R .eeprom " & file & " " & file & ".hex" | ||
exec "teensy-loader-cli --mcu=TEENSY2 -v -w " & file & ".hex" | ||
exec "rm " & file & ".hex" | ||
|
||
proc size*(file: string) = | ||
exec "avr-size -C --mcu=atmega32u4 " & file | ||
|
||
proc sizeDetails*(file: string) = | ||
size(file) | ||
echo ".data section:" | ||
exec "avr-nm -S --size-sort " & file & " | grep \" [Dd] \" || echo \"empty\"" | ||
echo "" | ||
echo ".bss section:" | ||
exec "avr-nm -S --size-sort " & file & " | grep \" [Bb] \" || echo \"empty\"" | ||
echo "" | ||
echo ".text section:" | ||
exec "avr-nm -S --size-sort " & file & " | grep \" [Tt] \" || echo \"empty\"" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,21 @@ | ||
include "../helpers/avrBoardConf.nim" | ||
|
||
proc build*(file: string) = | ||
exec "nim c -d:danger --os:any " & file | ||
|
||
proc upload*(device: string, file: string) = | ||
exec "avrdude -F -V -c arduino -p atmega328p -P " & device & " -b 115200 -U flash:w:" & file & ":e" | ||
|
||
proc size*(file: string) = | ||
exec "avr-size -C --mcu=atmega328p " & file | ||
|
||
proc sizeDetails*(file: string) = | ||
size(file) | ||
echo ".data section:" | ||
exec "avr-nm -S --size-sort " & file & " | grep \" [Dd] \" || echo \"empty\"" | ||
echo "" | ||
echo ".bss section:" | ||
exec "avr-nm -S --size-sort " & file & " | grep \" [Bb] \" || echo \"empty\"" | ||
echo "" | ||
echo ".text section:" | ||
exec "avr-nm -S --size-sort " & file & " | grep \" [Tt] \" || echo \"empty\"" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import strutils, tables, os, options | ||
import nimscripter, nimscripter / vmops | ||
import cligen | ||
|
||
var | ||
keys*: Table[string, string] | ||
defines*: seq[string] | ||
|
||
proc switch*(key: string, value = "") = | ||
if key == "define": | ||
defines.add value.strip(chars = {'"'}) | ||
else: | ||
keys[key] = value.strip(chars = {'"'}) | ||
|
||
proc getSetting*(key: string): string = | ||
keys[key] | ||
|
||
proc isDefined*(key: string): bool = | ||
defines.contains key | ||
|
||
proc runCommand(command: string, arguments: varargs[string]) = | ||
addVMops(tasks) | ||
addCallable(tasks): | ||
proc build(file: string) | ||
proc getSize(file: string) | ||
|
||
exportCode(tasks): | ||
template `--`(key, val: untyped) = | ||
switch(strip(astToStr(key)), strip(astToStr(val))) | ||
|
||
template `--`(key: untyped) = | ||
switch(strip(astToStr(key))) | ||
|
||
exportTo(tasks, switch, getSetting, isDefined) | ||
|
||
var searchPaths: seq[string] | ||
# TODO: figure out paths on Windows | ||
let nimblePath = getEnv("NIMBLE_PATH") | ||
let nimblePaths = if nimblePath.len == 0: | ||
@[getHomeDir() & "/.nimble/pkgs/", getHomeDir() & "/.nimble/pkgs2/", "/opt/nimble/pkgs/", "/opt/nimble/pkgs2/"] | ||
else: | ||
@[nimblePath] | ||
for path in nimblePaths: | ||
for kind, path in path.walkDir: | ||
if kind == pcDir: searchPaths.add path | ||
|
||
var | ||
addins = implNimscriptModule(tasks) | ||
interpreter = loadScript("config.nims".NimScriptPath, addins, modules = @["strutils"], searchPaths = searchPaths) | ||
|
||
case arguments.len: | ||
of 0: interpreter.get.invokeDynamic(command) | ||
of 1: interpreter.get.invokeDynamic(command, arguments[0]) | ||
of 2: interpreter.get.invokeDynamic(command, arguments[0], arguments[1], returnType = void) | ||
of 3: interpreter.get.invokeDynamic(command, arguments[0], arguments[1], arguments[2], returnType = void) | ||
of 4: interpreter.get.invokeDynamic(command, arguments[0], arguments[1], arguments[2], arguments[3], returnType = void) | ||
else: interpreter.get.invokeDynamic(command, arguments) | ||
|
||
proc build(file = getCurrentDir().lastPathPart().addFileExt(".nim")) = | ||
runCommand("build", file) | ||
|
||
proc upload(device = "autodetect", file = getCurrentDir().lastPathPart()) = | ||
if device != "autodetect": | ||
runCommand("upload", device, file) | ||
else: | ||
runCommand("upload", file) | ||
|
||
proc size(file = getCurrentDir().lastPathPart()) = | ||
runCommand("size", file) | ||
|
||
proc sizeDetails(file = getCurrentDir().lastPathPart()) = | ||
runCommand("sizeDetails", file) | ||
|
||
dispatchMulti([build], [upload], [size], [sizeDetails]) | ||
|
Oops, something went wrong.