====== amforth ====== amforth is a [[forth language]] implementation for the Atmel AVR family of [[microcontrollers]]. http://amforth.sourceforge.net all input and output is in hex by default (this can be changed with the **decimal** and **hex** words). when defining new words, the words are written immediately to flash. so they are still there after losing power. you can set a startup procedure (called the "turnkey" in amforth) that will get automatically executed when the [[Microcontroller]] powers up, making this actually useful ===== examples ===== ==== sleeping ==== (: ms n -- , pause for n ms 1- 0 do 1ms loop ; ==== hello world (aka, blinking an LED) ==== NOTE: there is a more sophisticated version of this example in the blocks/pollin.frt file in the amforth distribution since it's customary to blink an LED as your first microcontroller program, here is code to do it. memory addresses are for an ATMega8. this code assumes you have an LED connected to pin 28 (PC5, or PORTC5). === in short === : ms n -- , pause for n ms 1- 0 do 1ms loop ; : blink -- , blink PORTC5 for 100ms 20 35 c! 64 ms 0 35 c! 64 ms ; : blinks n -- , blink n times 0 do blink loop ; \ set the pin to output mode 20 34 c! 10 BLINKS === in detail === PORTC5 is controlled with bit 5 of the PORTC register. in the ATMega8, the PORTC register is mapped to memory address 0x35 (see "Register Summary" in the ATMega8 datasheet). additionally we need to set the pin to be an output pin, the Data Direction Register for PORTC (DDRC) is at 0x34. to set the direction of PORTC5, we need to write "1" to bit 5 of DDRC. we can only write bytes, so we need to write 00100000 in binary (bit 0 is on the right), which is 0x20 in hex. 20 34 c! PORTC5 is now in output mode. by default, the pin should be low. if your LED (and resistor) are connected to +V, your LED should be lit, as the pin is low and should be sinking current. if it's connected to ground, it should be off. now we can set the pin high by writing 0x20 to PORTC: 20 35 c! if your LED is connected to ground, the LED should be lit now, otherwise it should be off. and set it low again by writing 0. 0 35 c! we can define a word that blinks the LED. first we need the word from the above example, so if you don't already have it: (you don't have to bother typing the stuff in the brackets, it's just a comment) : ms n -- , pause for n ms 1- 0 do 1ms loop ; then, : blink -- , blink PORTC5 for 100ms 20 35 c! 64 ms 0 35 c! 64 ms ; **(64 ms)** NOTE: 100 is 0x64 in hex, so pauses for 100ms. then a word to blink multiple times: : blinks n -- , blink n times 0 do blink loops ; so now 10 blinks should blink your LED **16** times (as 10 in hex is 16 in decimal.. ah, gotcha! ===== questions ===== * how do you remove a word? * implementations of **forget** and **marker** are found in the blocks/ans.frt file ===== tips ===== ==== example files ==== there are device specific constants (like the ones i needed to use in the blinking example) in the devices/ directory. there are several files of useful definitions in the blocks/ directory. you need to "upload" these onto the [[Microcontroller]] via the terminal. but you have to be careful not to overload the connection, as there is no flow control. pv ==== uploading with **pv** ==== under linux you can limit the upload using ($ pv -L 30 blocks/ans.frt > /dev/ttyS0 that limits the output to a roaring 30 chars/sec. which is probably overly cautious. it's hard to say because it seems to have more to do with the number of lines rather than the number of chars. i came up with 30 cps by sending lots of short commands to find the worst case. my controller is running at 4MHz at couldn't handle lots of **42 .** commands at 40cps. ==== better uploading with python ==== [[http://libarynth.fo.am/pub/Libarynth/AmForth/amforth-upload.py.txt|amforth-upload.py.txt]] python upload script for amforth NOTE: rename this script to amforth-upload.py when you have downloaded it. twiki won't let me have a .py file as an attachment for security reasons **rolls eyes** even better than using **pv** , is this script i just made. it watches the output from amforth, and uses the character echo as a kind of software flow-control. it also waits for a prompt after entering a line, to give the compiler a chance to run. here is an example that will loads everything into my ATMega8 (run in the amforth-1.3/ directory). (python amforth-upload.py devices/atmega8.frt blocks/*.frt for help, run (python amforth-upload.py -h ==== backing up ==== i added these backup/restore rules to my makefile, they use **avrdude** to read the memory off of the controller into a file which i can later restore: PROG=dapa backup: $(AVRDUDE) -c $(PROG) -p atmega8 -P /dev/parport0 -U flash:r:backup.hex:i -U eeprom:r:backup.eep.hex:i restore: backup.hex backup.eep.hex $(AVRDUDE) -c $(PROG) -p atmega8 -P /dev/parport0 -e -U flash:w:backup.hex:i -U eeprom:w:backup.eep.hex:i you need to set **PROG** to whatever programmer you have. i use the el-cheapo resistors-in-a-parallel-cable version --PiX - 01 Mar 2007 ---- ---- Libarynth > Libarynth Web > AmForth r4 - 09 Mar 2007 - 11:56 ---- ==== amforth-upload.py ==== #!/usr/bin/env python # amforth-upload.py # # uploads text files containing forth commands to a microcontroller running amforth (v1.3) # monitors the output from the microcontroller and uses the character echo to implement a kind of software flow control # also waits for a prompt after each line to allow the compiler a chance to run # # in theory it should never overrun the input buffer on the controller, # but it will easily get stuck if the output from the controller is unusual # # you have to kill any other processes that are accessing the serial port before running this, # otherwise it will miss characters that are stolen by the other process and get stuck # # perhaps a better way to implement this would have been as a program that could be plugged into a terminal program # like x/y/zmodem for minicom (except for the difficulty of killing it when it gets confused). oh well. # # mailto:pix@test.at # http://pix.test.at/ import sys import getopt import os import re def write_line_flow(string,dest): # strip comments # these probably will strip comment-like structures out of ." .." strings as well. if debug: print >>sys.stderr, "line before comment stripping: "+string string = re.sub("(^| )\( .*?\)"," ",string) string = re.sub("(^| )\( [^\)]*$"," \n",string) string = re.sub("(^| )\\\\ .*","",string) if re.match("^\s*$",string): if verbose: print >>sys.stderr, "skipping empty line" return if debug: print >>sys.stderr, "line after comment stripping: "+string if verbose: print >>sys.stderr, "sending line: "+string for o in list(string): dest.write(o); if o == "\t": o = " " while True: i = dest.read(1) #print "<"+i+"]", #print >>sys.stderr, "["+i+"]" sys.stdout.write(i) sys.stdout.flush() if i == o: #print "|", break #dest.write("\n") if verbose: print >>sys.stderr, "waiting for prompt" start, nl, space, o, gt = range(5) state = start while True: #print >>sys.stderr, "{"+str(state)+"}" #dest.write("") i = dest.read(1) #print "<"+i+"]", #print >>sys.stderr, "["+i+"]" sys.stdout.write(i) sys.stdout.flush() if state == start: if i == "\r": state = nl elif i == " ": state = space continue elif state == nl: if i == ">": state = gt else: state = start continue elif state == gt: if i == " ": if debug: print >>sys.stderr, " '>" break else: state = start continue elif state == space: if i == "o": state = o else: state = start continue elif state == o: if i == "k": if debug: print >>sys.stderr, "" break else: state = start def write_file_flow(in_file,dest): while(True): line = in_file.readline() if len(line)>0: write_line_flow(line,dest) else: break def main(argv): global verbose, debug #in_file = file("file.frt") #tty_dev = file("/dev/ttyS0","w+",0) tty_dev_name = "/dev/ttyS0" force = False verbose = False debug = False try: opts, args = getopt.getopt(argv,"ht:vfd") except getopt.GetoptError: print >>sys.stderr, "unknown option. try -h" sys.exit(1) for opt, arg in opts: if opt == "-h": print >>sys.stderr, "usage: amforth-upload [-h] [-v] [-f] [-t tty] [file1] [file2] [...]" print >>sys.stderr, "\n default tty is "+tty_dev_name print >>sys.stderr, "\n if no files are specified, input is read from the the terminal" print >>sys.stderr, "\n -f will run without checking for other processes accessing the tty" print >>sys.stderr, "\n -v will print extra information during execution" sys.exit(1) elif opt == "-t": tty_dev_name = arg elif opt == "-v": verbose = True elif opt == "-f": force = True elif opt == "-d": debug = True if not force: if not os.system("which fuser >/dev/null 2>&1"): if not os.system("fuser -u "+tty_dev_name): print >>sys.stderr, "the above process is accessing "+tty_dev_name+"." print >>sys.stderr, "please stop the process and try again." sys.exit(1) else: print >>sys.stderr, "couldn't find fuser. so i can't check if "+tty_dev_name+" is in use." print >>sys.stderr, "run with the -f option to force execution anyway" tty_dev = file(tty_dev_name,"r+",0) if len(args)<1: if verbose: print >>sys.stderr, "processing stdin" write_file_flow(sys.stdin,tty_dev) else: for filename in args: in_file = file(filename,"r") if verbose: print >>sys.stderr, "processing "+filename write_file_flow(in_file,tty_dev) in_file.close() if __name__ == "__main__": main(sys.argv[1:])