Software

Der Con­trol­ler läuft mit dem in­ter­nen 8 MHz RC-Os­zil­la­tor. Die Ge­nau­ig­keit ei­nes Quarz­os­zil­la­tors ist hier nicht er­for­der­lich da keine asyn­chro­nen Schnitt­stel­len (wie RS232) be­nutzt wer­den und auch kei­ne Echt­zeit­uhr re­ali­siert wer­den muss.

Ein Quarz ist da­her auch auf der Leiter­plat­te nicht vor­ge­se­hen.

Programmierung

Denken Sie daran, wäh­rend der Pro­gramm­ierung des Con­trol­lers müs­sen Sie den Tas­ter ge­drückt hal­ten da die Pins dann in Hi-Z wech­seln und die Ver­sor­gung da­mit ab­ge­scha­ltet wer­den wür­de. Der ge­drückte Tas­ter sorgt für eine per­ma­nente Ver­sor­gung der Schal­tung wäh­rend der Pro­gram­mie­rung.

Timer Interrupt

Timer 0 wird be­nutzt um wie­der­keh­ren­de Er­eig­nis­se (wie den Mul­ti­plex-Be­trieb des Dis­plays) zu re­ali­sie­ren. Die In­ter­rupt-Rou­tine wird da­bei 1000 mal pro Se­kunde auf­ge­ru­fen. Bei 6 Digits macht dies eine Re­fresh-Rate von 166 Hz. Sie er­laubt da­bei eine Hel­lig­keits-Steu­er­ung in drei Schrit­ten. Da­zu wird das Dis­play nur in ei­nem bis zu al­len drei von drei Zy­klen ein­ge­schal­tet (oder auch in kei­nem, Dis­play aus.) Da wir hier mit Bat­te­ri­en ar­bei­ten kann dies ver­wen­det wer­den um Strom zu spa­ren.

Da sich durch das Dim­ming die Re­fresh-Rate ent­spre­chend ver­rin­gert ist es nicht sinn­voll, mehr als drei Stu­fen zu er­mög­li­chen. Dabei haben wir im­mer noch mehr als 55 Hz und damit eine flimmer­freie Dar­stel­lung.

Timer 0 be­treibt auch ei­nen Se­kun­den-Zäh­ler der be­nutzt wird um in den Power-Save oder Stand­by-Modus zu wech­seln oder bei län­ge­rer In­akti­vi­tät ganz ab­zu­schal­ten.

Dieser ist jedoch nicht Be­stand­teil einer Echt­zeit­uhr! Er wird zu ver­schie­de­nen Ge­le­gen­hei­ten auf 0 ge­setzt, haupt­säch­lich um Time­outs zu re­ali­sie­ren.

Bin6toBCD8

Da der AD-Wand­ler ei­nen 24-bit Bi­när­wert lie­fert ist eine Rou­ti­ne nö­tig, die die­sen (nach der Um­rech­nung in kg) in eine (gepackte) BCD-Zahl um­wan­delt. Ge­packt heißt hier, dass in ei­nem Byte zwei Di­gits ent­hal­ten sind. Die BCD-Zahl passt da­her in ei­nen long-Wert. Das packed BCD kann an­schließend ganz leicht ins Dis­play-RAM der Sie­ben­seg­ment-An­zeige ge­schrie­ben wer­den.

Ich habe diese Rou­tine in Assemb­ler ge­schrie­ben da sie sich hier we­sent­lich ef­fek­ti­fer re­ali­sie­ren lässt. In C gibt es kein Carry-Flag so dass die Schie­be-Operationen we­sent­lich auf­wän­di­ger wä­ren.

Das ge­wähl­te Ver­fah­ren kommt mit 24 Schie­be-Ope­rat­io­nen, je­weils über al­le 7 Bytes, aus. Vor dem Schie­ben muss für je­des Digit (also für je­des Nibble des Er­geb­nis­ses) ge­prüft wer­den, ob es größer als 5 ist. Dann muss 3 ad­diert wer­den. Hier wur­de es so ge­ändert, dass stets 3 ad­diert wer­den und falls es falsch war, drei wieder sub­tra­hiert wird. Das er­gibt ei­nen klei­ne­ren Code, da das MSB jedes Nibbles nur gann ge­setzt ist, wenn es größer als 5 war. Ist es nicht ge­setzt subtra­hie­ren wir die drei wie­der.

Ich hal­te hier al­le Wer­te in Re­gis­tern. Dies be­schleu­nigt die Aus­füh­rung, die be­trächt­lich dau­ern kann. Ich ver­wen­de aus­schließ­lich Re­gis­ter, die im AVR-GCC als vo­gel­frei gelten, also nicht auf dem Stack ge­sichert wer­den müs­sen. Falls das auf­ru­fen­de Pro­gramm sie be­nut­zen wür­de, müsste es selbst für die Si­che­rung vor und die Rück­sicherung hin­ter dem Auf­ruf sor­gen.

Wenn Sie einen an­de­ren Com­pi­ler ver­wen­den kann das mö­gli­cher­wei­se nicht so funk­tio­nie­ren und Sie müs­sen die Re­gis­ter evtl. vor­her si­chern!

Meine Ver­sion be­nö­tigt al­so kein RAM und kei­nen Stack, lässt sich aber nicht so ein­fach auf größere Da­ten­ty­pen er­wei­tern da nicht ge­nug freie Re­gis­ter ver­füg­bar sind.

Na­tür­lich sa­bo­tiert die­se Me­thode jeg­li­chen Por­ta­bi­li­täts-Ge­dan­ken. Nicht nur für an­de­re Ar­chi­tek­tu­ren, selbst für an­dere AVR-De­ri­vate müss­ten Sie da­rü­ber nach­den­ken um die­se Rou­ti­ne zu im­ple­men­tie­ren. Aber brau­chen wir das? Ei­gent­lich nicht! Es muss funk­tion­ie­ren, es muss schnell funk­tio­nie­ren und wenig Strom ver­brau­chen. Tat­säch­lich ent­wickle ich in mei­ner Praxis ge­ra­de die zehn­te Ver­sion eines Mo­bile-Mo­dems und die elfte ist be­reits angekündigt. Kein Ge­rät hält heut­zu­tage län­ger als zwei oder drei Jah­re. Ent­wed­er wird ir­gend­ein Bau­teil ab­ge­kün­digt oder die An­ford­erun­gen stei­gen. Das si­chert zwar un­se­re Exis­tenz­grund­lage als Ent­wickler, macht aber häu­fig auch ein­fach kei­nen Spaß!

Power-Save-Modus

Wenn sich das Ge­wicht nicht we­sent­lich än­dert wird in den Power-Save-Mo­dus ge­wech­selt. Da­bei wird das Dis­play schritt­wei­se ab­ge­dun­kelt um Strom zu spa­ren. Die Mes­sung läuft je­doch weiter.

Än­dert sich das Ge­wicht wird wie­der in den Nor­mal-Modus ge­wech­selt und das Dis­play zeigt das Ge­wicht in vol­ler Hel­lig­keit.

Ändern heist hier­bei mehr als 5 g Un­ter­schied zum Ge­wicht vor ei­ner Se­kun­de.

Abschaltung

Bei mehr als vier Mi­nu­ten In­aktivität schal­tet sich das Ge­rät selbst ab. Erst da­mit ist die Zu­wie­ge­funk­tion ge­löscht und beim er­neu­ten Ein­schal­ten zeigt die Waa­ge 0 als Start-Ge­wicht an, un­ab­hän­gig da­von was tat­säch­lich auf­liegt.

Wenn Sie tat­säch­lich mehr als vier Mi­nuten be­nö­ti­gen und wei­ter mess­en wol­len kön­nen Sie durch ei­nen kur­zen Druck auf den Wäge­tel­ler oder dem Tas­ter zum Nor­mal-Mo­dus zu­rück­keh­ren und ha­ben er­neut vier Mi­nuten Zeit bis zur Ab­schal­tung.

Wenn kein nen­nens­wer­tes Ge­wicht auf­liegt (±5 g) er­folgt die Ab­schal­tung be­reits nach 15 s. Das dient der Bat­terie-Er­spar­nis und kann bewusst ge­nutzt wer­den in­dem Sie am En­de der Mes­sung die Waage auf Null stel­len.

Kalibrierung

Wird der Taster für min­des­tens 10 s ge­drückt wech­selt die Waage in einen Kali­brier-Mo­dus. Das Dis­play zeigt dann CAL.

Nach Los­las­sen des Tas­ters kann der Be­nut­zer eines der vor­de­fi­nier­ten Re­fe­renz­ge­wich­te aus­wäh­len. Durch Tas­ten­druck kann zum nächs­ten pas­sen­den ge­wech­selt wer­den.

Erst wenn län­ger als fünf Se­kun­den kein Tas­ten­druck er­folgt, gilt das ak­tu­ell an­ge­zeig­te Ge­wicht als ge­wählt. Sein Wert blinkt nun für drei Se­kun­den schnell um die Wahl an­zu­zei­gen.

Hal­ten Sie nun die­ses Re­fe­renz­ge­wicht be­reit, wir brau­chen es bald! Wenn Sie sich hier ver­tan ha­ben, tren­nen Sie ein­fach die Strom­ver­sor­gung oder war­ten Sie ei­ne Mi­nute bis das Ge­rät sich selbst ab­schal­tet und nichts wird sich än­dern.

Das Gerät er­wartet zu­nächst ei­nen leer­en Wä­ge­tel­ler und zeigt dann don't touch (-touch). Da­rauf­hin wer­den 128 Mes­sun­gen vor­ge­nom­men um den Null­punkt zu er­mit­teln. Die Waage mit­telt dazu 128 Mes­sun­gen was etwa 13 s dau­ert.
Ein (hexa­de­zi­ma­ler) Count-Down-Zäh­ler, der im Wech­sel ein­ge­blen­det wird, gibt ei­nen vi­su­el­len Pro­gress-Bar der Mes­sung.
Das Dis­play zeigt danach rEF. War­ten Sie bis da­hin. Be­rüh­ren Sie nicht den Wäge­tel­ler und ma­chen sie kei­nen un­nö­tigen Wind. All dies wür­de die Ka­li­brier-Ge­nau­ig­keit be­ein­träch­tigen. Le­gen Sie dann ein Re­ferenz-Ge­wicht auf, dass 1 kg, 2 kg, 5 kg oder 10 kg wie­gen kann, wie Sie es vor­her aus­ge­wählt ha­ben. Ide­al wä­ren 10 kg da dies ma­xi­male Ge­nauig­keit er­geben wür­de. Auch hier mit­telt die Waage über 128 Mes­sun­gen was wie­der­um et­wa 13 s dau­ert. Be­rüh­ren Sie in die­ser Zeit nicht den Wäge­tel­ler und ma­chen Sie kei­nen Wind. Dre­hen Sie kei­ne Hei­zung auf und öff­nen Sie kein Fens­ter! Die Waage zeigt das ge­mes­sene Ge­wicht an (was na­tür­lich dem tat­sächli­chen Re­fe­renz­ge­wicht ent­spre­chen soll­te) und es kann durch Tas­ten­druck akzep­tiert wer­den und wird da­mit im EEPROM als Re­fe­renz ge­spei­chert. Wenn Sie hier­bei ei­nen Feh­ler ma­chen tren­nen Sie ein­fach die Strom­ver­sor­gung oder war­ten Sie eine Mi­nute bis das Ge­rät sich ab­schal­tet und al­les bleibt wie es war und wieder­ho­len Sie die Pro­ze­dur. Durch Auf­legen fal­scher Ge­wichte kön­nen Sie die Ge­nauig­keit bis zur Un­brauch­bar­keit ver­fäl­schen.
Ins­be­son­dere kann durch ei­ne fal­sche Wahl des Re­fe­renz­gew­ichts (z.B. Sie wäh­len 5 kg, le­gen aber nur 2 kg auf) eine völ­lig un­sin­nige Re­fe­renz ge­spei­chert wer­den.

Wenn Sie das an­ge­zeig­te Ge­wicht be­stä­ti­gen (und nur dann) wird es so­fort als Re­fe­renz im EEPROM ge­spei­chert und ist auch nach ei­nem Neu­start gül­tig.

Wenn an dieser Stelle kei­ne Be­stä­ti­gung er­folgt star­tet das Ge­rät nach ei­ner Mi­nute neu und al­les ist wie vor­her.

Umrechnung der Raw-Werte

Um das Gewicht aus ei­nem 24-Bit Raw-Wert mit 24-Bit-Ge­nau­ig­keit mit der simp­len For­mel <raw>*a/b in Integer-Arith­metik zu be­werk­stel­ligen müs­sen wir min­des­tens mit 48-Bit-Zahlen rech­nen. Die AVR-Bi­blio­thek kennt den Typ int64_t so dass dies auch ohne Ver­wen­dung von Fieß­komma-Zah­len ein­fach mög­lich ist.

Auch bei der Ka­li­brie­rung ist es so ein­fach mög­lich, die Kom­po­nen­ten a und b zu be­rech­nen.

In­te­res­sant ist, dass die Be­rech­nung in int64 zwar we­sent­lich klei­ne­ren Code er­zeugt aber mit 285 µs rund drei­mal so lan­ge dau­ert wie die Be­rech­nung in double! Das liegt wohl daran, dass double im AVR-GCC nur 32 Bit breit sind. 24 Bit Genauigkeit wären damit selbst in double wohl illusorisch!

Fil­te­rung der Mess­wer­te

Durch das Rau­schen des AD-Wand­lers ist sein Wert nicht di­rekt zur An­zei­ge ge­eig­net.

Zum Tes­ten habe ich drei Me­tho­den einer di­gi­ta­len Fil­ter­ung im­ple­men­tiert:

Gleitende Mittelwertbildung

GetAVG1 ge­nannt in den Sour­cen. Mein er­ster An­satz war ei­nen glei­ten­den Mit­tel­wert über die je­weils letz­ten 8 Mes­sun­gen zu bil­den.

Diese Methode hat den Vor­teil, dass sich das Er­geb­nis linear dem ak­tu­el­len Mess­wert an­nä­hert. Nach­tei­lig ist der ho­he Spei­cher­be­darf, da wir uns die letz­ten acht Mess­werte mer­ken müs­sen.

Tat­säch­lich ha­be ich so­gar noch ei­ne er­wei­ter­te Ver­sion re­ali­siert, die 64 Wer­te mit­telt. Per Dop­pel­klick (al­so zwei Tas­ten­drücke in­ner­halb ei­ner Se­kun­de) kann zwi­schen 8 und 64 Wer­ten um­ge­schal­tet wer­den.

Das Dis­play zeigt nach ei­nem Dop­pel­klick kurz H1 oder L0 um den Hoch­auf­lö­sen­den oder schnel­len Mo­dus an­zu­zei­gen.

Der Nach­teil die­ser Me­tho­de ist der ho­he Spei­cher­be­darf. Die glei­ten­de Mit­tel­wert­bil­dung be­nö­tigt et­wa 100 Bytes mehr Flash und 256 Bytes mehr RAM als die nach­fol­gen­de:

IIR-Filter

Die zwei­te Me­tho­de, GetAVG2, be­rech­net das ak­tu­el­le Er­geb­nis le­dig­lich aus dem letz­ten Mess­wert, der um eine Kon­stan­te stär­ker ge­wich­tet wird, und dem ak­tu­el­len Mess­wert, also nach der For­mel xi=(x(i-1)*c+x)/(c+1)

Dies hat den Vor­teil ei­nes ge­rin­gen Spei­cher­be­darfs, auch für ho­he Wer­te von c, er­kauft mit dem Nach­teil dass sich der Wert nur mit ei­ner e-Funk­tion dem ak­tu­el­len Mess­wert an­nä­hert und da­mit, da wir hier nur mit Ganz­zah­len rech­nen, tat­säch­lich nie­mals. Er kommt nur auf (c-1) an den Mess­wert he­ran aber da die­ser rausch­be­haf­tet ist wird auch die­se Gren­ze über­wun­den so­fern das Rau­schen größer als c ist.

Aller­dings be­wirkt ge­nau die­se e-Funk­tion ein Ver­hal­ten, das dem ei­ner me­cha­ni­schen Waage am nächs­ten kommt: man sieht ziem­lich schnell, wo­hin es geht aber dann wird der Zei­ger lang­sa­mer und bis man den ge­nau­en Wert hat muss man ein we­nig war­ten.

avgFactor

Ist die be­stim­mende Größe für GetAvg2(). Dies ist der Fak­tor, um den der letz­te Wert hö­her ge­wich­tet wird als der ak­tu­el­le Mess­wert. avg­Fac­tor wird we­sent­lich klei­ner sein als hist­Size beim glei­ten­den Mit­tel­wert da sich der Wert erst nach et­wa 12×avgFactor Mes­sun­gen sta­bi­li­siert.

Auch hier kann per Dop­pel­klick zwi­schen zwei Wer­ten um­ge­schal­tet wer­den, schnell (L0) oder Prä­zi­si­on (H1).

Zum Ku­chen­backen reicht LO (das auch nach dem Ein­schal­ten ein­ge­stellt ist) völ­lig aus da es hier­bei nicht auf 10 oder 100 mg an­kommt. Wenn Sie prä­zi­se mes­sen wol­len, schal­ten Sie mit dem Dop­pel­klick um.

FIR-Filter

Der dritte Ansatz war ein FIR SINC-Filter, GetAvg3() ge­nannt. Die end­li­che Im­puls­ant­wort lie­fert auch nach end­li­cher Zeit ein sta­bi­les Sig­nal.

Ich habe zu­nächst den Rech­ner von Tom Roelands be­nutzt um die Ko­ef­fi­zien­ten zu be­rech­nen. (Cut­off-Fre­quency 2 Hz, Tran­si­tion Band­with 3 Hz, Ham­ming-Fens­ter) und er­hielt ein Fil­ter mit 11 Ko­ef­fi­zien­ten.

Al­ler­dings kam mir des­sen Er­geb­nis sus­pekt vor da der ers­te und letz­te Ko­ef­fi­zient 0 war. Nun, ein an­de­rer Rech­ner bei TFilter lie­fer­te mir Er­geb­nis­se bei de­nen das nicht so war!

Ko­ef­fi­zien­ten ein­tra­gen ist ja nicht so kom­pli­ziert so ha­ben Sie in mei­nen Sour­cen bei­de Op­tio­nen und kön­nen da­mit ex­pe­ri­men­tie­ren.

Die Gretchenfrage: rei­chen 32 Bit für die Be­rech­nung? Nun, nicht prin­zi­piell! Wenn al­le Ko­effi­zien­ten 127 wä­ren und al­le 24-bit-Werte 8388607 de­fi­ni­tiv nicht. Aber die Ko­effi­zien­ten sind nicht al­le 127. Sie sind Kon­stan­ten so­dass wir den einen Mul­ti­pli­kator ken­nen.

Die Sum­me der Ab­so­lut­wer­te ist 335 bzw. 361, so dass selbst bei knapp 6 Mio noch kei­nen Über­lauf er­zie­len wür­den.

Der Wand­ler liefert nur etwa ±2 Mio Counts. Das Fil­ter funk­tio­niert auch mit 6 Mio noch ohne Über­lauf so­dass wir hier ge­nug Re­ser­ve ha­ben um die Be­rech­nung mit 32 Bits zu ris­kie­ren.

Die ist meine ers­te Ein­schät­zung, die auch ex­pe­ri­men­tell fuk­tion­ierte. Da das FIR-Filter je­doch ke­ine bes­seren Re­sul­ta­te lie­fer­te als das IIR ha­be ich es auch nicht wei­ter ver­folgt. Ich habe noch ei­nen FIR-Fil­ter mit 0.1 Hz pro­biert (bei dem TFilter ver­sag­te, je­doch Roelands ein Er­geb­nis brach­te) doch dies ist sub­jek­tiv nicht bes­ser als das ein­fache­re IIR-Fil­ter.

Auswahl der Fil­te­rung

Welche Me­tho­de sie be­vor­zu­gen müs­sen sie zur Com­pile-Zeit an­ge­ben in­dem Sie in scale.h USE_GetAvgx de­fi­nie­ren. Ei­ne Aus­wahl zur Lauf­zeit ist nicht vor­ge­se­hen. Ich ha­be mich in mei­nen Sour­cen für das IIR-Fil­ter ent­schie­den, aber es ist ja nur eine Fra­ge der #defines.

Bildung des Anzeigewertes

Der HX lie­fert 10 Mess­wer­te pro Se­kun­de. Selbst der ge­fil­ter­te Wert rauscht er­heb­lich und ist da­her nicht di­rekt zur An­zei­ge ge­eig­net. Die letz­ten zwei Digits wür­den nur un­ko­ordi­niert Flim­mern und könn­ten vom Be­nut­zer nicht er­fasst wer­den.

Da­her wird nicht je­der er­rech­ne­te Wert di­rekt an­ge­zeigt son­dern wir bil­den er­neut einen Mit­tel­wert über die letz­ten vier ge­fil­ter­ten Wer­te und zei­gen die­sen 2.5 mal pro Se­kun­de an.

Die­se Rate kann gut er­fasst wer­den und der Wert wird da­durch deut­lich ru­hi­ger.