The Promised C Code
I know a lot of you want to have microcontroller code written in C. See my blog on Assy vs C on the home page. Well, I did do a C version. In fact, I compiled C for this project in SDCC and MPLAB-X. (C source/hex files are incuded in the downloads on the “Display” page.) I specifically wrote the ADC computations using the same method as I used in the assembly language. No libraries or other math routines were needed and none were included. This way, a comparison of the code size will be accurate.
And I am happy to report to you that the hex code size generated was about 1:3 in favor of Assembly Language !! I should have devised a timing test. Just to see who would win that. I wonder which would win? So, neither speed nor size really means anything for this project. This project has gobs of memory left over. And everything has been slowed down to accommodate human I/O. So, would I have written the code faster or had fewer errors if I had originally written it in C? Most likely. I had some problem with bank switching that C would have handled for me. It is easier to read C and the logic can be easier to figure out. But I still have trouble thinking in C on a micro. Still have to “translate” up. But I win the size/speed competition anyway.
Assembly – C Comparison
I have to post it. Here is the output of an sdiff, assembly on the left, C on the right:
:020000040000FA | :060000000A128A11122809 :100000001728000000000000A400030E8301A500D3 | :10000800FE00030EF1000408F2000A08F300831250 :100010008B018C10A00B102807146030A0009B0AE5 | :1000180003137F08F4000A128A11F8290A128A11B8 :10002000250E8300A40E240E0B17071009003D3087 | :10002800152883010A128A11412A831203131F1DFE :10003000850080308600831603308E00C830810032 | :1000380020280000000019288B13831603131E08BC :100040000E309F003D30850086013C3087008312D2 | :1000480083120313CC004C08BD008B171E08CC008C :1000500081309F006030A0000130A1008E20831607 | :100058004C08BC003C08CC004C08B800B90133304F :1000600002308C00FF30920083120C309D0006306D | :10006800C3000030C4003808CC00CD014C08C500DE :100070009200C0308B0087200C131F150000000079 | :100078004D08C6000A128A11D8200A128A11831262 :100080001F193E288B13000083161E0883128B173E | :1000880003134308CE004E08BC003C08B038CC002F :10009000A7001E08A600A200A3013330A202031984 | :100098004C08B0003330F5000030F6003808CC00CA :1000A0005828031C5528A30A4D283330A207592885 | :1000A800CD014C08F7004D08F8000A128A113521D5 :1000B000A30A30302304AA002E30AB002208A60089 | :1000B8000A128A11750883120313CE004E08BC0079 :1000C000A70DA60DA70DA60D2608A200A3011530A9 | :1000C8003C08CD003D08CC000310CC0DCD0D03102D :1000D000A20203197228031C6F28A30A672815308F | :1000D800CC0DCD0D4D08CE004E08B800AE30CC008A :1000E000A2077328A30A30302304AC000310A20C2B | :1000E8004C08B1001530C3000030C4003808CC00FB :1000F0000A3022020319A20322083038AD009820EA | :1000F800CD014C08C5004D08C6000A128A11D82047 :1001000087208720872087201F1585143E28A80177 | :100108000A128A11831203134308CE004E08BC005A :10011000A901A90B8928A80B89280800C130AA00C9 | :100118003C08B038CC004C08B2000230C3000030B4 :10012000C430AB00C330AC00D630AD00982008001E | :10012800C4003808CC00CD014C08F7004D08F80091 :10013000071787172A08A72007132B08A7200717D8 | :100138001530F5000030F6000A128A1135210A122E :1001400087132C08A72007132D08A7200800860076 | :100148008A11760883120313C601C6077508C5010C :08015000861300008617080069 | :10015800C5070A128A11D8200A128A1183120313BA > :100168004308CE004E08B8000A303802031CC028E5 > :100178000930CC004C08B8003808B038CC004C081E > :10018800B3000A128A11D3210A128A110A128A119B > :100198001C220A128A110A128A111C220A128A11B6 > :1001A800831203131F15192883120313C901C41FCF > :1001B800E428C309C409C30A0319C40AC901C90A3E > :1001C800C61FEF28C509C609C50A0319C60A0130A2 > :1001D800C7004708C906CA01CB01440843040319EC > :1001E8002629C801C80AC41B052901300310C30DFC > :1001F800C40DFF3E031DFA280130C7004708C80791 > :10020800F72801300310CA0DCB0DFF3E031D062948 > :1002180044084602031D122943084502031C1B29F2 > :100228004308C5024408031CC603C6024A14013029 > :100238000310C40CC30CFF3E031D1C290130C80267 > :10024800031D0529490803192E29CA09CB09CA0A19 > :100258000319CB0A4B08C401C4074A08C301C307E2 > :10026800080083120313C201F81F4129F709F8098E > :10027800F70A0319F80AC201C20AF61F4829F50944 > :10028800F609F50A0319F60A76087504031975299B > :10029800C101C10AF61B5C2901300310F50DF60DEA > :1002A800FF3E031D51290130C0004008C1074E29F7 > :1002B80076087802031D622975087702031C6A29EB > :1002C8007508F7027608031CF803F80201300310DA > :1002D800F60CF50CFF3E031D6B290130C102031D0E > :1002E8005C29420803197D29F709F809F70A031957 > :1002F800F80A7808F601F6077708F501F507080007 > :100308003030831203138500803086000330831653 > :1003180003138E00C83081000E309F000130850025 > :1003280086011D3087008130831203139F006030DF > :10033800F6007608B4000130F6007608B500013002 > :10034800F6007608B600C130F6007608B000CE3068 > :10035800F6007608B100C130F6007608B200C43065 > :10036800F6007608B3000A128A11D3210A128A11FC > :100378000A128A1132220A128A110A128A111C22BE > :100388000A128A110A128A111C220A128A110A12E6 > :100398008A111C22831203130C131F1508008312EB > :1003A80003130717871730080A128A114C220A12FA > :1003B8008A1183120313071331080A128A114C2277 > :1003C8000A128A11831203130717871332080A12B5 > :1003D8008A114C220A128A11831203130713330855 > :1003E8000A128A114C2208008B018C100130B402C9 > :1003F800031D102A0130B502031D102A6030F000D9 > :100408007008B4000130F0007008B5000130F00049 > :100418007008B60736089B000B177408FF007308AE > :100428008A0072088400710E8300FE0E7E0E090099 > :10043800FF30F500750883120313BF003F08031946 > :100448000800FF30F5007508BE003E0803192F2A82 > :100458000130BE02292A0130BF02222A0230831647 > :1004680003138C00FF3092000C30831203139D009D > :1004780006309200C0308B0008000A128A118421CD > :100488000A128A110A128A1119200A128A111228CC > :10049800F500750883120313860086130000831283 > :0604A80003138617080093 > :00000001FF
Test of ADC Calculation
Like I have said before, I’m no math (not maths, it’s MY web page) wizzard. I looked for ways to do 10-bit math on an 8-bit machine, but I never found anything useful. So I settled for my ‘braindead’ method that I presented in the video. When I saw the data being displayed on the DL1414 and the parallel LCD jittering up and down, I was afraid that that was due to my code. The nice result on the serial LCD seems to indicate PORT B activity is affecting the ADC measurements. That’s for another time.
The first thing wrong with the ADC conversion is that I forced the result to have only two decimal places. 1024 bits into 5V should resolve to a little less than 5mV, or three decimal places. But I limited the value to fit the DL1414. So these ‘fudged’ divisors I used could not evenly divide the two decimal places that I did have. And, secondly, I threw away any remainder for the hundredths digit. So we should expect some amount of error.
I really wanted to test how my conversion code effected the result, how much was it off? I knew there was probably a way to mathematically demonstrate that, but since I could not come up with a decent method to do the conversion in the first place, I wasn’t going to come up with any mathematical tests. Eventually it struck me that if I could generate a set of conversion results using floating point and align that with a set of results using my method, I could just visually compare the results. So, python to the rescue. (If you ever thought about learning python, DO IT.) You can downlowd that code here:
Open for copying: adc_table.py
This will print three columns of data. The first is the conversion using python floating-point. The second is the value using my algorithm. And the third is the difference (error) between the values in the first two columns. At the end is a python dictionary. The entries are of the form: error(3rd column):(count of how many times it occurred). Turns out (if this test is meaningful) that there is an offset error, and then maybe an error of +/-4 counts (+/-.02) only “occasionally”. If you add 0.01 to each of my calculations, you will get a little better result. (This technique of adding 0.01 is done in some real hardware devices.) Here is the dictionary reordered (both columns) and compared to the result with 0.01 added (right-hand column):
0.01 : 93 0.02 : 93 0.00 : 326 0.01 : 326 -0.01 : 385 -0.00 : 385 -0.02 : 200 -0.01 : 200 -0.03 : 19 -0.02 : 19
Whether this conversion method is useful or not depends on requirements. In this case, it got us by.
As far as testing ADC accuracy using this hardware set-up, it’s not worth it. At the least you do not want to use the internal +5V as a reference. Using a 4.096V reference is the way to go. Even if you want to use 5V for a reference, there are 5.12V references available. I may get into all this in a future project. Just have to see what comes along.
Here is a list of some of the goof-ups while I did this project.
- Did not notice the addressing was 3-0, left-right. I had it backwards.
- A bit was stuck on the display. Turned out ICSP mode was enabled in the Configuration Word. This forced RB3 high (I think. I forgot to document the state).
- WDT was not disabled in Configuration Word.
- Brown-Out reset was not disabled (but probably a non contributer).
PWM too fast:
- PR2 was not set to 0xff
PORT A missing signal:
- PUP (pull-up) fixes RA4
- ADCON0: channel set to 7, but not implemented on ‘876.
- PORT A defaults to analog functions. Did not figure that out for a while.
- Never proved the problem. Pretty sure was servicing CCP2IF in the interrupt routine, while TMR2IE was the only interrupt enabled.
- Pig-headedness lead to the project described below.
I was so sure there was a silicon error in the ‘876 that I wanted to program an ‘877A I had. Turns out my PICSTART will not program an ‘877A. So I developed a programmer with an ‘876. It minimally programs an ‘877A correctly. Running the code from the sick ‘876 on the ‘877A, the ‘877A exhibited exactly the same symptoms. I had a suspicion that I was being an idiot. Then I finally analyzed the code and found the interrupt error. No one saw me, so I don’t think anyone will ever know. And I get a new project to do (yet another programmer in the world), but I’m not sure if I want to do it.
What Have We Learned
I have learned that I can still program (I did have to do a lot of refreshing). I can figure out some peripherals. I can write C for a micro without too much uneasyness. (I used the assembly code comments as a sort of pseudo-code to use as a crutch.) I’ve learned to pay attention to the datasheets. I’ve learned that a problem is 99.99% likely to be MY problem and not that of the hardware manufacturer. I promise to do better next time.