123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- /*
- * Moistcontrol - sensor
- *
- * Copyright (c) 2013 Michael Buesch <m@bues.ch>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "sensor.h"
- #include "util.h"
- #include "main.h"
- #include <string.h>
- /* Warmup time: The time to wait with sensor _enabled_
- * before performing one measurement. */
- #define WARMUP_TIME msec_to_jiffies(50)
- /* Wait time: The time to wait with sensor _disabled_
- * between measurements. */
- #define WAIT_TIME msec_to_jiffies(500)
- /* Sensor state-machine values. */
- enum sensor_status {
- STAT_IDLE, /* Idle: No measurement requested. */
- STAT_WAIT, /* Wait: Wait time between measurements. */
- STAT_WARMUP_P0, /* Warmup: First warmup time before measurement. */
- STAT_WARMUP_P1, /* Warmup: Second warmup time before measurement. */
- STAT_ADC_CONV, /* ADC-conv: ADC-conversion is in progress. */
- };
- /* Context of a measurement. */
- struct sensor_context {
- /* Current state-machine status. */
- enum sensor_status stat;
- /* Generic timer used for wait and warmup. */
- jiffies_t timer;
- /* The sensor number of the currently active
- * measurement, if any. */
- uint8_t nr;
- /* Temporary buffer for the measured values. */
- uint16_t values[3];
- uint8_t value_count;
- };
- /* Instance of the measurement context. */
- static struct sensor_context sensor;
- /* Port mappings for the supply-A lines of the sensors.
- * The array indices are the sensor number.
- * The array values are the port/ddr bit number, DDR register
- * and PORT register, respectively.
- */
- static const uint8_t PROGMEM sensor_a_bit[] = {
- [0] = 5,
- [1] = 6,
- [2] = 7,
- [3] = 2,
- [4] = 1,
- [5] = 0,
- };
- static volatile uint8_t * const PROGMEM sensor_a_ddr[] = {
- [0] = &DDRD,
- [1] = &DDRD,
- [2] = &DDRD,
- [3] = &DDRB,
- [4] = &DDRB,
- [5] = &DDRB,
- };
- static volatile uint8_t * const PROGMEM sensor_a_port[] = {
- [0] = &PORTD,
- [1] = &PORTD,
- [2] = &PORTD,
- [3] = &PORTB,
- [4] = &PORTB,
- [5] = &PORTB,
- };
- /* Sensor supply terminal "B" definitions */
- #define SENSOR_SUPPLY_B_PORT PORTC
- #define SENSOR_SUPPLY_B_DDR DDRC
- #define SENSOR_SUPPLY_B_BIT 3
- /* The number of available sensors. */
- #define SENSOR_COUNT ARRAY_SIZE(sensor_a_bit)
- /* Read the current ADC value. */
- static uint16_t sensor_adc_read_value(void)
- {
- return ADCW;
- }
- /* Start an ADC conversion. */
- static void sensor_adc_start(void)
- {
- /* Initialize ADC to 125 kHz (on 16 MHz CPU).
- * Select AVcc reference.
- * Set multiplexer to ADC0.
- */
- build_assert(F_CPU == 16000000ul);
- ADMUX = (1 << REFS0);
- ADCSRA = (1 << ADEN) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
- ADCSRA |= (1 << ADSC);
- }
- /* Returns the boolean state of the ADC unit.
- * 0: conversion active. 1: conversion done.
- */
- static bool sensor_adc_done(void)
- {
- return !(ADCSRA & (1 << ADSC));
- }
- /* Get the port access values for a sensor "A" supply.
- * sensor_nr: The sensor number to get the values for.
- * bitmask: Pointer to the returned bitmask.
- * ddr: Pointer to the returned DDR register.
- * port: Pointer to the returned PORT register.
- */
- static inline void get_sensor_a_supply(uint8_t sensor_nr,
- uint8_t *bitmask,
- volatile uint8_t **ddr,
- volatile uint8_t **port)
- {
- uint8_t bitnr;
- PANIC_ON(sensor_nr >= SENSOR_COUNT);
- /* Read the bit number, DDR and PORT pointers from the tables. */
- bitnr = pgm_read_byte(&sensor_a_bit[sensor_nr]);
- *bitmask = BITMASK8(bitnr);
- *ddr = (volatile uint8_t *)pgm_read_word(&sensor_a_ddr[sensor_nr]);
- *port = (volatile uint8_t *)pgm_read_word(&sensor_a_port[sensor_nr]);
- }
- /* Enable the power supply of a sensor.
- * nr: The sensor number.
- * polarity: The supply polarity.
- * Polarity 0 means: Vcc on supply "B" and
- * GND on supply "A".
- * Polarity 1 means: Vcc on supply "A" and
- * GND on supply "B".
- */
- static void sensor_enable(uint8_t nr, bool polarity)
- {
- uint8_t sreg, a_mask;
- volatile uint8_t *a_ddr, *a_port;
- /* Get the supply-A credentials. */
- get_sensor_a_supply(nr, &a_mask, &a_ddr, &a_port);
- /* Enable A- and B-supply */
- sreg = irq_disable_save();
- if (polarity) {
- _MMIO_BYTE(a_ddr) |= a_mask;
- _MMIO_BYTE(a_port) |= a_mask;
- SENSOR_SUPPLY_B_DDR |= (1 << SENSOR_SUPPLY_B_BIT);
- SENSOR_SUPPLY_B_PORT &= ~(1 << SENSOR_SUPPLY_B_BIT);
- } else {
- _MMIO_BYTE(a_ddr) |= a_mask;
- _MMIO_BYTE(a_port) &= ~a_mask;
- SENSOR_SUPPLY_B_DDR |= (1 << SENSOR_SUPPLY_B_BIT);
- SENSOR_SUPPLY_B_PORT |= (1 << SENSOR_SUPPLY_B_BIT);
- }
- irq_restore(sreg);
- }
- /* Disable the power supply of a sensor.
- * nr: The sensor number.
- */
- static void sensor_disable(uint8_t nr)
- {
- uint8_t sreg, a_mask;
- volatile uint8_t *a_ddr, *a_port;
- /* Get the supply-A credentials. */
- get_sensor_a_supply(nr, &a_mask, &a_ddr, &a_port);
- /* Disable A- and B-supply. */
- sreg = irq_disable_save();
- _MMIO_BYTE(a_ddr) &= ~a_mask;
- _MMIO_BYTE(a_port) &= ~a_mask;
- SENSOR_SUPPLY_B_DDR &= ~(1 << SENSOR_SUPPLY_B_BIT);
- SENSOR_SUPPLY_B_PORT &= ~(1 << SENSOR_SUPPLY_B_BIT);
- irq_restore(sreg);
- }
- /* Start the warmup-cycle of the current sensor. */
- static void sensor_warmup_begin(void)
- {
- /* Set the warmup-end time and set
- * warmup-polarity-0 state. */
- sensor.timer = jiffies_get() + WARMUP_TIME;
- sensor.stat = STAT_WARMUP_P0;
- /* Enable the sensor with 0-polarity. */
- sensor_enable(sensor.nr, 0);
- }
- /* Start a measurement on a sensor.
- * nr: The sensor number.
- */
- void sensor_start(uint8_t nr)
- {
- if (sensor.stat != STAT_IDLE) {
- /* Current status is not idle, cannot start. */
- return;
- }
- if (nr >= SENSOR_COUNT) {
- /* Invalid sensor number. */
- return;
- }
- /* Reset the stored values to zero. */
- memset(sensor.values, 0, sizeof(sensor.values));
- sensor.value_count = 0;
- /* Store the sensor number. */
- sensor.nr = nr;
- /* Start the warmup sequence. */
- sensor_warmup_begin();
- }
- /* Cancel the currently running measurement. */
- void sensor_cancel(void)
- {
- if (sensor.stat == STAT_IDLE)
- return;
- /* Wait for possibly running ADC to finish. */
- while (!sensor_adc_done());
- /* Disable the supplies and reset the state machine. */
- sensor_disable(sensor.nr);
- sensor.stat = STAT_IDLE;
- }
- /* Returns 1, if no measurement is running.
- * Returns 0 otherwise.
- */
- bool sensors_idle(void)
- {
- return sensor.stat == STAT_IDLE;
- }
- /* Poll the current measurement state.
- * Returns 1, if the measurement is finished.
- * Returns 0, if the measurement is still in progress.
- * If the measurement is finished (1), it puts the
- * measurement result into "res".
- */
- bool sensor_poll(struct sensor_result *res)
- {
- jiffies_t now;
- uint16_t a, b, c, median;
- if (sensor.stat == STAT_IDLE) {
- /* No measurement running. Return early. */
- return 0;
- }
- /* Get the current time. */
- now = jiffies_get();
- /* Run the sensor statemachine. */
- switch (sensor.stat) {
- case STAT_IDLE:
- break;
- case STAT_WAIT:
- if (time_before(now, sensor.timer)) {
- /* Wait time not finished, yet. */
- break;
- }
- /* Wait finished. Switch to warmup. */
- sensor_warmup_begin();
- break;
- case STAT_WARMUP_P0:
- if (time_before(now, sensor.timer)) {
- /* Warmup with polarity 0 not finished, yet. */
- break;
- }
- /* Warmup with polarity 0 done.
- * Start warmup phase with polarity 1. */
- sensor_enable(sensor.nr, 1);
- sensor.timer = now + WARMUP_TIME;
- sensor.stat = STAT_WARMUP_P1;
- break;
- case STAT_WARMUP_P1:
- if (time_before(now, sensor.timer)) {
- /* Warmup with polarity 1 not finished, yet. */
- break;
- }
- /* Warmup with polarity 1 done.
- * Start ADC conversion. */
- sensor_adc_start();
- sensor.stat = STAT_ADC_CONV;
- break;
- case STAT_ADC_CONV:
- if (!sensor_adc_done()) {
- /* ADC conversion not finised, yet. */
- break;
- }
- /* ADC conversion done. Disable sensor. */
- sensor_disable(sensor.nr);
- /* Store the measured value. */
- sensor.values[sensor.value_count] = sensor_adc_read_value();
- sensor.value_count++;
- if (sensor.value_count >= 3) {
- /* All measurements done. */
- a = sensor.values[0];
- b = sensor.values[1];
- c = sensor.values[2];
- /* Get the median of all measurements.
- * 'b' will be the result. */
- if (a > b)
- swap_values(a, b);
- if (b > c)
- swap_values(b, c);
- median = b;
- /* Store the result. */
- res->nr = sensor.nr;
- res->value = median;
- sensor.stat = STAT_IDLE;
- /* Whole measurement done. */
- return 1;
- } else {
- /* Schedule the next measurement. */
- sensor.timer = now + WAIT_TIME;
- sensor.stat = STAT_WAIT;
- }
- break;
- }
- /* Measurement still in progress. */
- return 0;
- }
- /* Initialize the sensor unit. */
- void sensor_init(void)
- {
- uint8_t nr;
- build_assert(ARRAY_SIZE(sensor_a_bit) == ARRAY_SIZE(sensor_a_ddr));
- build_assert(ARRAY_SIZE(sensor_a_bit) == ARRAY_SIZE(sensor_a_port));
- /* Reset the sensor context. */
- memset(&sensor, 0, sizeof(sensor));
- /* Disable all sensors. */
- for (nr = 0; nr < SENSOR_COUNT; nr++)
- sensor_disable(nr);
- /* Perform one ADC conversion, as per AtMega datasheet the
- * very first conversion has less precision.
- * Discard the result.
- */
- sensor_adc_start();
- while (!sensor_adc_done());
- }
|