This posting presents some of the ways to use the Cosa AnalogPin class. The first style of usage is, as Arduino, pure synchronous where the processor will busy-wait for the analog conversion.
uint16_t luminance = sensor.sample();
As described in previous postings, Cosa is an object-oriented approach to programming the Arduino where the resources in the processor are C++ objects. Analog pins are objects with methods/functions. In the above example the object named sensor is an instance of AnalogPin and may perform the methods (member functions) available in the class. The method sample() corresponds directly to the Arduino function analogRead() with the difference that no parameter is required as the pin number this is already known by the AnalogPin instance.
Fig.1: AnalogPin Member Functions
The AnalogPin constructor requires the Board analog pin name (e.g. Board::A0) and has also a reference voltage parameter with the default value AVCC_REFERENCE. The full expanded statement for the above example is:
AnalogPin sensor(Board::A0, AnalogPin::AVCC_REFERENCE);
There are three reference voltage types defined as an enum in the AnalogPin class; APIN_REFERENCE, use the Arduino reference voltage pin, AVCC_REFERENCE, use the power supply voltage as reference, and A1V1_REFERENCE, 1.1 internal voltage reference. Depending on your application you should select the correct reference voltage and perform necessary scaling of the sampled values.
The reference voltage may be changed with the method, set_reference(). It should be noted that the reference voltage is per analog pin (per sample) and not a single setting for all analog pins. This is a major difference compared to the Arduino analogReference() function which is global and does not use strong data typing, i.e., provide compile checking of the parameter. The Cosa AnalogPin class also provides an operator>> variant of the sample() method.
sensor >> luminance;
The latest sample value is available with the access method get_value(). This is an important aspect of Cosa AnalogPin as the state of the analog pin is maintained by the object. This reduces application code and memory footprint.
There are two primary programming styles in Cosa to allow applications to execute code while waiting for a conversion to complete. The first method is to request a sample, execute some code and then wait the conversion. This partially reduces the busy-wait section and adds concurrency.
// Some code to execute before waiting for the conversion to complete
uint16_t luminance = sensor.await();
For instance, you could print a message before waiting for the analog conversion to complete. This is an interesting example as the UART is also a hardware unit and works concurrently with the processor. In this case, you could have three concurrent activities processing at the same time; 1) the analog to digital conversion (ADC), 2) the UART transmitting characters from the IO buffer, 3) the processor itself which could be doing some computation with a previous sample value.
The Arduino processor (ATmega328P) has a number of hardware units (Timers, UART, SPI, I2C, EEPROM, etc) all possible to run concurrently with the processor. It is important to have a programming paradigm that does not limit this ability. The IO processing should be conducted, synchronized, by the processor and not worked in a serial, sequential, fashion. An analog to digital conversion takes about 1800 instructions cycles (112 us) to complete, and the maximum number of conversions per second is less than 10,000. This should be compared to the 16,000,000 instructions per second that the processor can execute. Busy-waiting for the conversion is basically a waste of 1800 instruction cycles and "electricity".
The default behavior for the AnalogPin interrupt handler (ISR) is to push Event:: SAMPLE_COMPLETED_TYPE with the object and conversion value onto the event queue. The interrupt handler, on_interrupt(), is also a virtual method and may be replaced by the application if needed. The interrupt handler is per instance which allows additional modification after application requirements.
The sketch must use an event dispatcher in the Arduino loop() to process the completion events. The events in the queue should be viewed as delayed function calls. Instead of executing code in the interrupt handler the code, action, is delayed as an event.
The event dispatcher will call the AnalogPin implementation of the virtual method Event::Handler::on_event(). The default behavior of this method is to handle two events, Event::TIMEOUT_TYPE, and the above Event::SAMPLE_COMPLETED_TYPE. On timeout the event handler will issue a sample_request() which allows periodic sampling of an analog pin by attaching the pin object to one of the Watchdog timer queues. There is one queue for each of the Watchdog timeout levels.
The above statement will attach the sensor to receive timeout events every 64 milliseconds and automatically request a new sample. The analog pin may be viewed as holding a continues snapshot of sampled values.
When receiving Event::SAMPLE_COMPLETE_TYPE the AnalogPin default event handler will check if the value has changed and call the on_change() virtual method. Applications should defined their own action by sub-classing AnalogPin and implementing the on_change() virtual method. Other methods of allowing extension is to provide a callback function. The advantage of using a virtual method is that the context of the callback is provided by the object.
The flow of control from interrupt to event handler and action is:
=> AnalogPin::on_interrupt(uint16 value)
===> Event::push(Event::SAMPLE_COMPLETED_TYPE, this, value);
(4) => AnalogPin::on_event()
- Interrupt Service Routine (ISR) is called when the analog conversion is completed. It will enqueue an Event::SAMPLE_COMPLETED_TYPE, together with the AnalogPin object and the converted value.
- The dispatch loop function Event::queue.await(&event) will dequeue the new event.
- The event.dispatch() corresponds to calling the on_event() method for the receiving object; the AnalogPin object.
- The default on_event() behavior for the AnalogPin will call the on_change() if the value on the analog pin has changed compared to previous sample.
Cosa also support the sampling of a batch of analog pins. Please see the documentation of Cosa AnalogPins class for more details.