MAKING A SOLAR-CELL TESTER WITH MECRISP-STELLARIS FORTH
In the last two articles on Forth, I’ve ranted about how it’s stunning but strange, and then gotten you set up on a basic system and blinked some LEDs. and while I’ve pointed you at the multitasker, we haven’t made much real use of it yet. getting started on a Forth system like this is about half the battle. working inside the microcontroller is different from compiling for the microcontroller, and figuring out the workflow, how to technique problems, and where the beneficial resources are isn’t necessarily obvious. Plus, there’s some terrific features of Mecrisp-Stellaris Forth that you might not notice until you’ve hacked on the system for a while.
Ideally, you’d peek over the carry of someone doing their thing, and you’d see some of how they work. That’s the goal of this piece. If you’ve already flashed in our version of Mecrisp-Stellaris-plus-Embello, you’re ready to follow along. If not, go back and do your homework real quick. We’ll still be here when you’re done. A lot of this post will be very certain to the Mecrisp-Stellaris flavor of Forth, but given that it runs on tons of ARM chips out there, this isn’t a bad place to be.
Getting Acclimatized
The first thing you’re going to need to get used to in Forth is the stack. You know that old chestnut about people only being able to keep five (seven?) things in their mind at one time? Forth puts that to the test.
Last time, I briefly pointed out the .s (“print stack”) command. In the Hackaday Edition, I’ve re-defined the conventional Mecrisp .s to be a little bit less verbose, and to my eyes a lot more readable. If you find yourself hitting .s a lot, and you will, I’ve also written a function that (temporarily) overwrites the “ok.” prompt by appending a stack printout to it, whenever you hit enter. type print.stack and hit enter one a lot more time to see how it works. hitting the reset button, or typing reset will wipe everything in RAM, and that includes the stack-printing prompt, so you’ll be back to a clean slate.
Now is probably a good time to play around with the stack operators. have you read the Mecrisp glossary? check out the list of stack juggling operations there. turn on print.stack and play around until they all make sense.
Have you run words yet? It spits out a linked list of every word that Forth knows, along with the memory locations where they live, and some extra details — too much detail, unless you’re debugging the system itself. There’s an extra, nonstandard, word in Mecrisp that just prints out the function names: list. give that a shot now. If you haven’t already defined a few words, do so.
: hw .” hello, world!” cr ;
is a good one to have on hand.
Layers in Memory
Mecrisp-Stellaris’ memory is divided into two gross locations: RAM and flash. All words that are provided before the “— Mecrisp-Stellaris Core —” mark are in RAM, and will be lost on reset or power-down. new RAM functions will be appended to the front of the list.
After the “— Mecrisp-Stellaris Core —” mark come functions in flash. In the early parts of flash, before “— Flash Dictionary —” is the conventional Mecrisp Forth core. From there until “<>” are words taken from the Mecrisp distribution that are normally useful, including some debugging functions and multitasking. preceding “<>” are the contributions from the Embello libraries, including a lot of GPIO definitions, and those before “<>” were added just for this post series.
What’s not evident is that all of these markers with brackets surrounding them are cornerstones. These allow you clear out flash up until that memory location. So if you’ve added some extra functions into flash, and want to clear back out to the Hackaday edition default state, you can type <
Overwriting History
If you define a word twice with the same name, you’ll have two versions of the word in the dictionary. When a word is called or compiled, the interpreter looks through memory, from the top of RAM down, and then from the end of flash back to the beginning. Words with the same name in RAM thus get called before those in flash. What can be particularly odd about Forth is that, because it compiles in real-time, the word that is referred to in any calling word is the one that was on the top at the time the calling word was defined. Making tangled histories is a sure way to go insane.
: foo .” foo!” ;
: bar foo .” bar!” ;
bar foo! bar! Bine.
: foo .” bizzle!” ; Redefine foo. Bine.
foo bizzle! Bine.
bar foo! bar! Bine.
On the other hand, here’s a great way to work that takes advantage of these various memory features. RAM gets erased on every reset, giving you a clean slate, but by using cornerstones, flash isn’t immutable either. Of course, the deeper in flash you have to erase,the a lot more words you have to redefine later, assuming that some of them were useful. This suggests a layered technique to development, with the most “core” words innermost in flash. That’s also natural because words need to be defined to be called, so defining the basics first makes sense anyway.
You can compile a new word to RAM (the default) by calling compiletoram or to flash by invoking compiletoflash. Prototype your words in RAM. feel complimentary to overwrite them as lots of times as you want, but remember that you have to redefine any dependent words after you change something upstream to keep the calling history intact. once you’re made with a chunk of development, type reset and clear RAM. now redefine these words into flash. If you develop your application from the bottom up, you’ll find that this all hangs together nicely. When you’ve discovered a bug in something that’s already written to flash, the cornerstones come to the rescue.
Finally, this layered nature of Forth definitions can be really handy. For example, a function init that’s defined in flash gets run on each reset. It includes things like setting the processor speed and the system tick, that you probably don’t want to mess with. because you can overwrite init, and any compilation uses the words available at the time of compilation, you can simply layer your functionality onto init: : init init .” howdy!” cr ;. The first “init” is the name of the new word, and the second is calling the pre-existing init, and doing all of the setup. The remainder of the definition is yours to play with. On restart, everything will get carried out in the buy it was defined.
Creature Comforts
I write a lot of code in an editor (Vim) and then send it over to the chip to play around with. very specifically, I wrote a script called forth_upload.sh that contains the following:
[[ $1 ]] && TERM=$1 || TERM=/dev/ttyUSB0
DELAY=0.2
while read -r f; do echo “$f” ; echo “$f” > $TERM ; sleep ${DELAY}s ; Terminat
To send a file to the serial port, and thus to Forth, forth_upload.sh < myfunctions.fs will work. It loops through each line in the file, allowing a 0.2 second delay for the system to compile anything. This delay is too long, in my experience, but the bugs from having it short are lousy to find. Folie takes the technique of trying to find the “ok.” prompt to speed things up, but this has issues of its own. If you’re using Vim, you can send individual words or a full file with the following:
nnoremap
inoremap
nnoremap
This setup lets me develop and tweak functions in an editor that I like, and then send them nearly quickly into the Forth system to test out. I keep a terminal window open that’s always logged into the Forth system, so I can enjoy the new words get defined in, and then start playing around with them interactively. It’s pretty sweet.
The Solar Cell Tester
Now to a quick real example that will make use of all of the above. I recently made a decision to characterize a bunch of small solar panels that I had in my junk drawer. This indicates adjusting the load on the panel and noting down the voltage generated by the panel and the current through the load. I hooked up two multimeters and wrote down some numbers, but this is undoubtedly a job for a microcontroller.
As is clear from the image, this is a quick lashup. The larger chunk of copper-clad has a current-measurement resistor and a pair of resistors configured as a voltage divider to step down the panel voltage to something the 3.3 V ADC can handle.
Dangling off that is the (silvered) variable load circuit, which is entirely sub-optimal: a MOSFET dissipates the heat by being turned half on by a PWM’ed voltage fiiltered by that 100 uF capacitor. The 9 V battery and optoisolator were cobbled on to make sure a high enough voltage to fully open the MOSFET, which wanted a lot more than 3.3 V. This horrible, but functional, load circuit was a later addition — the first version just had a potentiometer here, until that got smoked by running too much current through it.
The pushbuttons are used to start and stop recordings from the device out on the balcony without having to run back inside. The procedure was to alligator-clip in a new panel, and hold the white button while it made recordings. The small black button is pressed once to demarcate a new cell’s data. The data goes back to my laptop over UART serial through an ESP8266 running esp-link as a transparent WiFi serial bridge, seen in the upper left, ideal next to the recycled laptop batteries. hot glue and cardboard round out the state-of-the-art build.
To the Datasheet!
This kind of quick and hands-on tool-building is where Forth shines. The Jeelabs Forth libraries already have some functions that simplify the ADC setup, but they actually didn’t work for me, so I looked to the datasheet.Minimul gol că trebuie să obțineți lucrul ADC este să activați ceasurile periferice ADC, să activați unitatea ADC, apoi selectați ora de eșantionare ADC. S-ar putea, de asemenea, dorim să rulați procedura de calibrare ADC pentru a vă asigura că citirile noastre sunt corecte. Acesta este un fel de detalii la nivel scăzut pe care doriți să le faceți cu orice microcontroler – sau să vă sprijiniți pe o bibliotecă care o face pentru dvs.
Citirea datei de date, pentru a activa ceasul sistemului ADC, trebuie să setăm bitul ADC1EN în registrul de memorie RCC-APB2ENR. Tot ceea ce. Mecrisp-Stellaris are un cod de sprijin care citește aceste valori din fișierele furnizate de producător. Le-am inclus în directorul de bază / registre. Fișierul hărții de memorie a avut mari mnemonici pentru toate registrele, dar nu sunt încântat de modul în care sunt manipulate numele de biți individuale. Asta e un yak pentru a rade într-o altă zi, încerc să fac lucruri făcute.
Iată setul de cuvinte ADC la nivel de biți pe care am venit cu:
\ None
\ Pb0 – AIN8 este conectat la tensiunea panoului
\ Pb1 – Ain9 este conectat la rezistența curentă a sensului
: ADC.RCC-Activare 9 biți RCC-APB2ENR BIS! ; \ setați ADC1EN.
: adc.set-adon 0 biți adc1-cr2 bis! ; \ setați ADON pentru a activa ADC
: ADC.SET-CAL-BIT 2 BIT ADC1-CRI2 BIS! ;
: adc.cal-făcut? 2 biți ADC1-CR2 bit @ 0 =;
: ADC.ISDONE? 1 biți ADC1-SR bit @;
9 biți creează un număr cu cel de-al nouălea set de biți și toate celelalte zero și RCC-APB2ENR BIS! Setează bitul dorit în Registrul de control ADC relevant. Toate în toate, destul de opace, dar și una-pentru-una cu instrucțiunile tehnice. Denumirea buna a acestor cuvinte cu strat de fund face ca lucrurile putin un strat mai bine. De exemplu, cuvântul responsabil pentru executarea unei calibrări (setat un pic, așteptați până când altul este clar) este relativ lider :: adc.cal adc.set-cal-biți ADC.Cal-făcut? pana cand ;.
Și aici vedem primul nostru control al fluxului, începe … până la construire. Cumpăra este ciudat, nu? Începe pornește bucla, funcția de testare ADC-Cal-făcută? EXECUTE, lăsând o valoare adevărată sau falsă pe stivă și apoi până când citește această valoare și bucle înapoi pentru a începe până când valoarea este adevărată. Acest lucru este același ca un c în timp ce (! ADC-CAL-DONE ()) {;} buclă de așteptare ocupată. Și este o introducere într-un alt IDIOM: Folosind? Pentru tot ceea ce întoarce o booleană.
Testați, fixați și încercați din nou
Deci, să încercăm aceste cuvinte. Inițializarea ADC și citirea este ușoară: Acum să le luăm pentru o alergare și asigurați-vă că valorile sunt ceea ce așteptăm. Biciuirea unui dispozitiv de testare rapidă este una dintre principalele sale puncte forte. : Test-ADC începe ADC @. CR 10 MS cheie? pana cand ; Definește un cuvânt care citește ADC, imprimă valoarea și repetă fiecare zece milisecunde până când apăsați Enter. Dacă faceți acest lucru cu un panou solar sub o lumină fluorescentă, de exemplu, veți ridica valurile de frecvență de rețea pe măsură ce vă lovesc panoul, ceea ce este ceva ce s-ar putea să nu vă gândiți. Abilitatea de a juca cu noile dvs. cuvinte de îndată ce le definiți, face ca acest tip de codare de investigație să curgă în mod natural.
Adevărata poveste. Când am scris acest cod, am stabilit un timp prea scurt de eșantion pe inputurile ADC. Acest lucru face ca valoarea de la o lectură să afecteze următorul, deoarece condensatorul intern al ADC nu are timp să se încarce sau să deschidă complet. Am primit prea multe citiri pentru curent când tensiunea a fost ridicată și citirile prea scăzute pentru curent când tensiunea a fost scăzută. Am depanat acest lucru destul de rapid prin scrierea unei rutine care a citit fiecare canal de câteva ori la rând și a tipărit valorile afară și am lăsat acele cuvinte în posteritate.
(Luați 8 mostre de fiecare, pentru depanare)
: citiți – ambele 8 0 Citiți-v. buclă 8 0 Citiți-i. buclă;
: Readloop începe să citească – atât CR 100 MS cheie? pana cand ;
Feedback-ul de la acest exercițiu de testare mă conduce să schimbe timpul de eșantionare pe ADC la valoarea lor maximă, deoarece nimic aici era vital și am folosit o sursă destul de înaltă de impedanță. (Implicit este să eșantionați cât mai repede ca P