FlashForth: Datasheets

4 minute read

Why the microcontroller datasheet is so important to programming in Forth.

The Datasheet

The ATmega328P datasheet is critical to understanding how to program the 328P. Exploring it with Forth is the joy of Forth as Forth allows you to interactively test commands, ports, timers, ADC’s etc of the 328P. And once you are convinced you have the correct set of commands to make what you want to happen, you can codify it into a word that becomes part of the vocabulary of Forth.

In Why Forth? I used a simple program manipulating the on-board LED to illustrate how to program in Forth:

\ Demonstrates 3 levels of Forth: HAL, Primitives, User 
\ Setup on-board LED on Arduino UNO
\ Goal: BD_LED (on/off/blink/toggle)

\ HAL: provides constants for LEDs and Buttons
$0024 constant DDRB  \ Port B data direction register
$0025 constant PORTB  \ Port B output register
%00100000 constant BD_LED \ Pin 5, Port B

\ PRIM: primitives for setting up LED
: PB_output ( bit -- ) DDRB mset ;  \ set a B pin as output
: init ( --- ) \ initialize ports for LED
	BD_LED PB_output
  ;
init

\ USER: user interface
: on ( pin -- ) PORTB mset ; \ set a B pin high
: off ( pin -- ) PORTB mclr ;  \ set a B pin low
: toggle ( pin -- ) PORTB c@ xor PORTB c! ; \ toggle the B pin
: blink ( pin -- ) \ blink B pin 
  begin 
	dup toggle 200 ms key?
  until
  drop ;

\ BD_LED ( on | off | blink | toggle )

Take a look at the code for toggle, I read the port, XOR it with a 1 then store it again. Its a conventional method of toggling a bit. (In fact, in reviewing some of the tutorials on FlashForth, this is exactly how they did it as well.)

I was reviewing the 328P datasheet for the millionth time and I noticed this passage:

“Three I/O memory address locations are allocated for each port, one each for the data register – PORTx, data direction register – DDRx, and the port input pins – PINx. The port input pins I/O location is read only, while the data register and the data direction register are read/write. However, writing a logic one to a bit in the PINx register, will result in a toggle in the corresponding bit in the data register. In addition, the pull-up disable – PUD bit in MCUCR disables the pull-up function for all pins in all ports when set.” –ATmega328P [DATASHEET] 7810D–AVR–01/15 page 58

WOW! That is far more simple than what I was doing. So I fired up my Uno, entered the above program and quickly tested the old way of doing it. Everything works as I previously thought.

The next thing I did was to simply test the code in an interactive manner. I know I can use the word “mset” to set memory-mapped port locations. It requires a mask and an address (as in “mset (mask addr – )” from the FlashForth documentation.) I opened a serial program to the Arduino and from boot, just typed in the following:

Serial screen showing the test of PINB

Serial screen showing the test of PINB

I entered “words” to ensure I didn’t already have a word defined which might mess with the test. All of the words are standard Forth words, we’re good. I entered the code three times to ensure the state of the LED changed each time. It did! Based on this new code and extremely simple test, I made the change.

Changing toggle to be the new more simple method: (I copied and pasted the following code into my terminal program, Serial)

\ Demonstrates 3 levels of Forth: HAL, Primitives, User 
\ Setup on-board LED on Arduino UNO
\ Goal: BD_LED (on/off/blink/toggle)

\ HAL: provides constants for LEDs and Buttons
$0023 constant PINB  \ Port B input register
$0024 constant DDRB  \ Port B data direction register
$0025 constant PORTB  \ Port B output register
%00100000 constant BD_LED \ Pin 5, Port B

\ PRIM: primitives for setting up LED
: PB_output ( bit -- ) DDRB mset ;  \ set a B pin as output
: init ( --- ) \ initialize ports for LED
    BD_LED PB_output
  ;
init

\ USER: user interface
: on ( pin -- ) PORTB mset ; \ set a B pin high
: off ( pin -- ) PORTB mclr ;  \ set a B pin low
\ : toggle ( pin -- ) PORTB c@ xor PORTB c! ; \ toggle the B pin
: toggle ( pin -- ) PINB mset ; \ toggle PortB pin by setting input register
: blink ( pin -- ) \ blink B pin 
  begin 
    dup toggle 200 ms key?
  until
  drop ;

\ to test code: BD_LED ( on | off | blink | toggle )
This new approach works well! Its a better way to do it as it uses less code, it is still readable and it matches the datasheet.

This article in Hackaday also discusses the value of datasheets, Forth and Forth’s interactive nature.

Next Step: Compile Your Own Version!

Comments powered by Talkyard.