Multi purpose I-O - Mk2

I've been using the Mk1 board for some time with success. Its main role is to act as a sun/cloud sensor - using some light dependent resistors to measure when they're in direct sunlight (and therefore create shadows) or if the day is cloudy (hence no shodows). The non-linear nature of the LDR's light-resistance relationship does mean that when the sun is very bright, there is only a matter of a few counts difference between a sensor in the light (i.e. pointing towards the sun) and one in shade (with it's light sensitive component in shadow).


I wanted something with better resolution - and while I was thinking along those lines, some extra functions and features would be handy, too. These are what I ended up with.
The top one controls two R/C servos (among other things) and the bottom one controls two stepper motors


What I came up with uses all the resources of the processor. In the 11 years since building the Mk1, things have moved on. This version is written in C, which makes coding easier - but did lead to some tricky issues when debugging problems (see later). I also decided to make two different versions. the main difference being whether to have the board drive R/C servos, or stepper motors. Servo control was something I hadn't done before, but sounded like a handy option to have for less fine-grained positional control. Servos only require 1 control pin, so I decided to implement 2 servo interfaces, which left more pins available for digital inputs and outputs. I also decided to forego the MAX232 interface chip from the Mk1 and use a simpler and cheaper design from here which I've used before and found to be very reliable.


The circuit of the servo version is available here and for the stepper version, the circuit is here. The servo version came first and the stepper board is a refinement from that. This means that the stepper board (apart from having fewer digital I-O pins available) has a few changes from the servo version. The biggest difference is that the 50Hz square-wave output on the servo (let's call that v2) board is upgraded to a programmable 1 - 500Hz output on the stepper (henceforth v3) board.


Here's a run down of the features for both version:

Common elements
4MHz operation
Written in Hi-Tech C
4800Baud 8N1 serial I-O to PC
6 Analog inputs with 12-bit resolution and 1mV per count
Load current monitor, 1mA resolution
20MHz, 8 digit frequency counter
Event timer with 1mSec resolution
Servo (v2) features Stepper (v3) features
8 digital outputs: D0 - D7. Outputs can be commanded to set or clear (1 or 0) up to 65535 seconds into the future, providing power is maintained to the board. 4 digital outputs, same ability to set bits at specified times
8 digital inputs: B0 - B7, with internal pull-ups. Inputs B6, B7 report the number of state changes (i.e. 0 - 1 or 1 - 0) longer than 1mSec that occurred since the last report was sent 4 digital inputs, B6 & B7 have same transition counting ability
On/off programmable 50Hz square-wave output Variable frequency square-wave, programmable between 500Hz and 1Hz
  End-stop inputs that will halt stepper motor motion when the input is set high.


So far as user interaction goes, the board is controlled through it's RS232 connection. When power is applied, it will announce itself and start running (as evidenced by the flashing LED - I believe all microprocessor projects should have at least 1 flashing light. It's the single, most basic and most useful diagnostic). The board accepts commands with a fairly simple format of a single letter, followed in some chases by qualifiers: such as the output to set, or the number of servo steps to rotate. The board can also respond to enquiries by sending a line containing all it's data - analog input values, digital inputs, frequency and other measurements. This data is in a fixed format as described below. The board can also be set to report this data at regular intervals from 300mSec to 6553 seconds between reports. I find that a frequency of 1 report per second, or 1 per minute suits my purposes.

A - D converter, oversampling.
Although the A-D on this chip has a resolution of 8 bits, there is a technique called oversampling that, at least in theory, can squeeze some extra bits of resolution from an ADC.
The theory goes that since any real-world ADC voltage reference has some noise associated with it, if the noise level is more than 1/2 a bit, it can be exploited. By taking multiple ADC readings and averaging them, the small differences in readings (due to readings being taken when the reference voltage is slightly different from the previous one) will increase the ADC's resolution. For every 16 readings, you can increase the resolution by 2 bits. I was keen to see if I could push an 8 bit ADC to give a 12 bit result. The answer is yes, you can. However to do this you need to take 256 8 bit measurements, which with a 4MHz clock takes around 11mSec, so the technique only works for slow-moving analog voltages.
I also found that it doesn't work for input voltages of less than 1 (native) bit - i.e. 16mV in this case. They all read as zero.

Command format

Tnnnnn Set time between reports to nnnnn tenths of a second
R report now
D0x
D1x
Set output bit x to 0 or 1 immediately
Sxnnnnn
Cxnnnnn
Set or Clear bit x in nnnnn seconds, i.e. 1 - 65535 seconds (i.e. from 1 second, up to just over 18 hours)
Q1
Q0
Turn on / off 50Hz square wave tone
Only on the v2 board
Ffff Set square wave frequency to 500 / fff. So for a frequency of 500Hz, fff must be set to 1. Note, since only integer values can be sent, frequencies available are 500Hz, 250Hz, 167Hz, 125Hz ...
only on the v3 board
Wcccc Set the PWM (output to cccc). the value must be between 0 (turns PWM off) and 1024
Pnssss Move R/C servo A or B to position ssss. the position value should be between 0 and 1000. A value of 0 disables the control, leaving the servo at it's current position, but unpowered so it could be moved manually. A value of 0 is also useful to stop the servo "jittering".
This command only works on the v2 board
M Dump memory. This is a (marginally useful) diagnostic. however, since the serial output buffer takes up a large amount of the dumped memory, you have to know what you're looking for.
Stepper commands for the v3 board
A!
B!
Stop stepper A or B immediately
A+nnnnn:sssss
A-nnnnn:sssss
Step motor A nnnnn clockwise or anti-clockwise at sssss milli-seconds per step
The same command applies to stepper B
B>nnnnn:sssss
B<nnnnn:sssss
Step motor B clockwise or anti-clockwise a maximum of nnnnn steps at sssss milliseconds per step until endstop input B (or A) goes high

Output format

Each line sent from the board has the same format, as illustrated in the sample line below:

B4:0000:0012:FF:2000:0192:2048:4080:3920:3200:3840:1616:001999877:00251:00000014.75
In order, these fields are:

Port B inputs, as 2 hex digits
The number of times port B7 changed since the last report
(note, if the number of changes per second is constant, but the time between reports changes this number will change, too
The number of times the port B6 changed since the last report
The output state of port D. When a timed change happens, this field will reflect it
8 analog readings, in decimal with 1 count corresponding to 1mV. So the first analog input is reading 2.000 Volts. Note: analog channel 3 is the voltage reference and will always read 4080. Analog channel 7 reads the voltage across the 1Ohm load current sensor. therefore it's reading shows the number of milliAmps drawn by any load which uses this port as it's 0 Volts point.
The frequency of the signal source fed to the T1CKI pin. In this case 2MHz. It always reads slightly low (99.994% of the actual reading - although the boards crystal cound also be running a little fast) as there are a few microseconds each second when the counter is turned off. the count is updated once per second, no matter how frequently the board sends reports
The number of milliseconds the event timer on pin RA4 was high. In this case the value was 251mSec. The event timer will only report the last event since the previous report. it will also split event timings across reports, if the event lasts longer than a single report time.
The time in seconds and hundredths since the board was switched on. In this case 14.75 seconds


Finally, here's the source for the v2 board and here's the complete project for the v3 board (download the ZIP file). if you haven't already got a copy, you should download the HiTech C compiler (free version) from their website. You will also find Microchip's MPLAB IDE and debugger / simulator to be indispensible.

There are also some notes and questions I raised on the Microchip user's forum during the development of this project.