From 4b0babc1b1daaece0d8de672f98adaf6cff5cdac Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sat, 18 Oct 2008 23:47:05 +0200 Subject: Add a loooot of stuff to pressure_control Signed-off-by: Michael Buesch --- pressure_control/firmware/Makefile | 16 +-- pressure_control/firmware/main.c | 195 +++++++++++++++++++++-------- pressure_control/firmware/main.h | 36 ++++++ pressure_control/firmware/remote.c | 243 +++++++++++++++++++++++++++++++++++++ pressure_control/firmware/remote.h | 68 +++++++++++ pressure_control/firmware/sensor.c | 55 +++++++++ pressure_control/firmware/sensor.h | 4 + pressure_control/firmware/util.c | 37 ++++-- pressure_control/firmware/util.h | 3 + 9 files changed, 589 insertions(+), 68 deletions(-) create mode 100644 pressure_control/firmware/main.h create mode 100644 pressure_control/firmware/remote.c create mode 100644 pressure_control/firmware/remote.h (limited to 'pressure_control/firmware') diff --git a/pressure_control/firmware/Makefile b/pressure_control/firmware/Makefile index 4024f02..a725d24 100644 --- a/pressure_control/firmware/Makefile +++ b/pressure_control/firmware/Makefile @@ -17,7 +17,7 @@ CFLAGS += "-Dinline=inline __attribute__((__always_inline__))" LFUSE = 0xE0 HFUSE = 0xD9 -OBJECTS = main.o util.o valves.o sensor.o +OBJECTS = main.o util.o valves.o sensor.o remote.o NAME = pressure_control BIN = $(NAME).bin @@ -26,13 +26,15 @@ EEP = $(NAME).eep.hex all: $(HEX) -main.o: util.h calibration.h valves.h sensor.h +main.o: util.h calibration.h valves.h sensor.h remote.h main.h util.o: util.h calibration.h -valves.o: valves.h +valves.o: util.h valves.h -sensor.o: sensor.h +sensor.o: util.h sensor.h + +remote.o: util.h remote.h calibration.h main.h %.s: %.c $(CC) $(CFLAGS) -S $*.c @@ -42,8 +44,8 @@ $(BIN): $(OBJECTS) $(HEX): $(BIN) $(OBJCOPY) -R.eeprom -O ihex $(BIN) $(HEX) -# $(OBJCOPY) -j.eeprom --set-section-flags=.eeprom="alloc,load" \ -# --change-section-lma .eeprom=0 -O ihex $(BIN) $(EEP) + $(OBJCOPY) -j.eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O ihex $(BIN) $(EEP) $(SIZE) $(BIN) avrdude: @@ -58,7 +60,7 @@ install_eeprom: $(AVRDUDE) -B $(AVRDUDE_SPEED) -p $(AVRDUDE_ARCH) \ -c $(PROGRAMMER) -P $(PROGPORT) -U eeprom:w:$(EEP) -install: all install_flash +install: all install_flash install_eeprom # Reset the microcontroller through avrdude reset: diff --git a/pressure_control/firmware/main.c b/pressure_control/firmware/main.c index 02272ef..3dab38e 100644 --- a/pressure_control/firmware/main.c +++ b/pressure_control/firmware/main.c @@ -17,87 +17,180 @@ * along with this program. If not, see . */ +#include "main.h" #include "util.h" #include "calibration.h" #include "sensor.h" #include "valves.h" +#include "remote.h" +#include +#include -static inline void usart_tx(uint8_t data) +#include + + +struct eeprom_data { + struct pressure_config cfg; +}; + +/* The pressure configuration data. */ +struct pressure_config cfg; +/* The pressure state data. */ +struct pressure_state state; + +/* EEPROM contents */ +static struct eeprom_data EEMEM eeprom = { + .cfg = { + .desired = 4000, /* 4 Bar */ + .hysteresis = 300, /* 0.3 Bar */ + .autoadjust_enable = 1, + }, +}; + + +void get_pressure_config(struct pressure_config *ret) { - while (!(UCSRA & (1 << UDRE))) - ; - UDR = data; + uint8_t sreg; + + sreg = irq_disable_save(); + memcpy(ret, &cfg, sizeof(*ret)); + irq_restore(sreg); } -static void __print(const prog_char *msg) +void get_pressure_state(struct pressure_state *ret) { - uint8_t c; + uint8_t sreg; - for ( ; ; msg++) { - c = pgm_read_byte(msg); - if (c == '\0') - break; - usart_tx(c); - } + sreg = irq_disable_save(); + memcpy(ret, &state, sizeof(*ret)); + irq_restore(sreg); } -#define print(msg) __print(PSTR(msg)) -#define ERXFE 1 /* USART RX frame error */ -#define ERXPE 2 /* USART RX parity error */ -#define ERXOV 3 /* USART RX hardware buffer overflow */ -#define ENODATA 4 /* No data available */ +/* Load the configuration from the EEPROM. */ +static void eeprom_load_config(void) +{ + eeprom_busy_wait(); + eeprom_read_block(&cfg, &eeprom.cfg, sizeof(cfg)); + eeprom_busy_wait(); +} -static inline int8_t usart_rx(uint8_t *data) +/* Store the configuration to the EEPROM. */ +static void eeprom_store_config(void) { - uint8_t status; - - status = UCSRA; - if (!(status & (1 << RXC))) - return -ENODATA; - if (unlikely(status & ((1 << FE) | (1 << PE) | (1 << DOR)))) { - if (status & (1 << FE)) - return -ERXFE; - if (status & (1 << PE)) - return -ERXPE; - if (status & (1 << DOR)) - return -ERXOV; - } - *data = UDR; + eeprom_busy_wait(); + eeprom_write_block(&cfg, &eeprom.cfg, sizeof(cfg)); + eeprom_busy_wait(); +} - return 0; +/* Sensor measurement completed. + * Called in IRQ context. */ +void sensor_result(uint16_t mbar) +{ + /* Defer processing of the value to the mainloop, so we can do it with + * interrupts enabled. */ + state.mbar = mbar; + mb(); + state.needs_checking = 1; } -#define BAUDRATE 9600 +/* 1kHz system timer. */ +ISR(TIMER1_COMPA_vect) +{ + if (state.sensor_trigger_cnt > 0) + state.sensor_trigger_cnt--; +} -static void usart_init(void) +void system_timer_init(void) { - uint8_t dummy; - - /* Set baud rate */ - UBRRL = lo8((CPU_HZ / 16 / BAUDRATE) * 2); - UBRRH = hi8((CPU_HZ / 16 / BAUDRATE) * 2) & ~(1 << URSEL); - UCSRA = (1 << U2X); - /* 8 Data bits, 2 Stop bits, Even parity */ - UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1) | (1 << UPM1) | (1 << USBS); - /* Enable transceiver and RX IRQs */ - UCSRB = (1 << RXEN) | (1 << TXEN);// | (1 << RXCIE); - /* Drain the RX buffer */ - while (usart_rx(&dummy) != -ENODATA) - mb(); + TCCR1B = (1 << WGM12) | (1 << CS10) | (1 << CS11); /* prescaler 64 */ + OCR1A = 250; /* 1kHz timer at 16MHz crystal */ + TIMSK |= (1 << OCIE1A); +} + +static void valves_force_state(uint8_t new_state) +{ + if (state.valves == new_state) + return; + valves_global_switch(new_state); + state.valves = new_state; +} + +static void adjust_pressure(uint16_t abs_offset, bool raise_pressure) +{ + if (0) { + //TODO if offset= 0); + + if (abs_offset > cfg.hysteresis) { + /* Adjust the pressure */ + adjust_pressure(abs_offset, !is_too_big); + } else { + /* The pressure is OK. Make sure the valves are + * all idle. */ + valves_force_state(VALVES_IDLE); + } } int main(void) { cli(); + /* It's OK to init the remote interface that early, as we + * have IRQs disabled throughout the init process. So we can't + * receive any remote commands, yet. But early init allows us + * to send error messages early. */ + remote_init(); + print("Pressure control initializing...\n"); + valves_init(); - usart_init(); + state.valves = VALVES_IDLE; + sensor_init(); + eeprom_load_config(); + system_timer_init(); sei(); + print("Monitoring...\n"); while (1) { - print("Hallo!\n"); - //TODO + mb(); + if (state.sensor_trigger_cnt == 0) { + /* It's time for triggering another sensor measurement. */ + state.sensor_trigger_cnt = -1; + mb(); + sensor_trigger_read(); + } + if (state.needs_checking) { + check_pressure(); + /* Trigger another measurement in 50 milliseconds. */ + state.sensor_trigger_cnt = 50; + mb(); + } + remote_work(); } } diff --git a/pressure_control/firmware/main.h b/pressure_control/firmware/main.h new file mode 100644 index 0000000..abdfa8f --- /dev/null +++ b/pressure_control/firmware/main.h @@ -0,0 +1,36 @@ +#ifndef MAIN_H_ +#define MAIN_H_ + +#include "util.h" + +#include + + +struct pressure_config { + /* Desired pressure in mBar */ + uint16_t desired; + /* Pressure hysteresis in mBar */ + uint16_t hysteresis; + /* Auto-adjustment is enabled. */ + bool autoadjust_enable; +}; + +struct pressure_state { + /* Current pressure in the tank (in mBar) */ + uint16_t mbar; + /* True, if the current pressure value needs checking against + * the desired pressure config. */ + bool needs_checking; + /* Trigger count: + * >0 = waiting + * 0 = trigger now + * -1 = triggered and running. */ + int8_t sensor_trigger_cnt; + /* Current valves state (enum valves_global_state) */ + uint8_t valves; +}; + +void get_pressure_config(struct pressure_config *cfg); +void get_pressure_state(struct pressure_state *state); + +#endif /* MAIN_H_ */ diff --git a/pressure_control/firmware/remote.c b/pressure_control/firmware/remote.c new file mode 100644 index 0000000..d759e7a --- /dev/null +++ b/pressure_control/firmware/remote.c @@ -0,0 +1,243 @@ +/* + * Pneumatic pressure controller. + * Remote control. + * + * Copyright (C) 2008 Michael Buesch + * + * 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 3 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, see . + */ + +#include "remote.h" +#include "util.h" +#include "calibration.h" +#include "main.h" + +#include + +#include + + +#define BAUDRATE 9600 + + +static struct remote_message rx_msg; +static uint8_t rx_msg_count; +static bool rx_msg_valid; + + +static inline void usart_tx(uint8_t data) +{ + while (!(UCSRA & (1 << UDRE))) + ; + UDR = data; +} + +static void usart_tx_buf(const void *_buf, uint8_t size) +{ + const uint8_t *buf = _buf; + + while (size) { + usart_tx(*buf); + buf++; + size--; + } +} + +#define ERXFE 1 /* USART RX frame error */ +#define ERXPE 2 /* USART RX parity error */ +#define ERXOV 3 /* USART RX hardware buffer overflow */ +#define ENODATA 4 /* No data available */ + +static inline int8_t usart_rx(uint8_t *data) +{ + uint8_t status; + + status = UCSRA; + if (!(status & (1 << RXC))) + return -ENODATA; + if (unlikely(status & ((1 << FE) | (1 << PE) | (1 << DOR)))) { + if (status & (1 << FE)) + return -ERXFE; + if (status & (1 << PE)) + return -ERXPE; + if (status & (1 << DOR)) + return -ERXOV; + } + *data = UDR; + + return 0; +} + +static void send_message(struct remote_message *msg) +{ + /* Calculate the CRC. */ + msg->crc = crc16_block_update(0xFFFF, msg, + sizeof(*msg) - sizeof(msg->crc)); + msg->crc ^= 0xFFFF; + /* And transmit the bits. */ + usart_tx_buf(msg, sizeof(*msg)); +} + +static void send_message_error(uint8_t error_code) +{ + struct remote_message msg; + + memset(&msg, 0, sizeof(msg)); + msg.id = MSG_ERROR; + msg.error.code = error_code; + + send_message(&msg); +} + +static void handle_received_message(void) +{ + struct remote_message reply; + uint16_t calc_crc; + + calc_crc = crc16_block_update(0xFFFF, &rx_msg, + sizeof(rx_msg) - sizeof(rx_msg.crc)); + calc_crc ^= 0xFFFF; + if (calc_crc != rx_msg.crc) { + /* CRC mismatch. */ + send_message_error(MSG_ERR_CHKSUM); + return; + } + memset(&reply, 0, sizeof(reply)); + + switch (rx_msg.id) { + case MSG_PING: + reply.id = MSG_PONG; + send_message(&reply); + break; + case MSG_GET_CURRENT_PRESSURE: { + struct pressure_state state; + + get_pressure_state(&state); + reply.id = MSG_CURRENT_PRESSURE; + reply.pressure.mbar = state.mbar; + send_message(&reply); + break; + } + case MSG_GET_DESIRED_PRESSURE: { + struct pressure_config conf; + + get_pressure_config(&conf); + reply.id = MSG_DESIRED_PRESSURE; + reply.pressure.mbar = conf.desired; + send_message(&reply); + break; + } + case MSG_GET_HYSTERESIS: { + struct pressure_config conf; + + get_pressure_config(&conf); + reply.id = MSG_HYSTERESIS; + reply.pressure.mbar = conf.hysteresis; + send_message(&reply); + break; + } + case MSG_GET_CONFIG_FLAGS: { + struct pressure_config conf; + + get_pressure_config(&conf); + reply.id = MSG_CONFIG_FLAGS; + if (conf.autoadjust_enable) + reply.config.flags |= (1 << CFG_FLAG_AUTOADJUST_ENABLE); + send_message(&reply); + break; + } } +} + +/* RX interrupt */ +ISR(USART_RXC_vect) +{ + uint8_t *rxbuf = (uint8_t *)&rx_msg; + int8_t err; + uint8_t data; + + if (rx_msg_valid) + return; + + while (1) { + err = usart_rx(&data); + if (err == -ENODATA) + break; + if (unlikely(err)) { + //TODO other error + data = 0; + } + rxbuf[rx_msg_count++] = data; + if (rx_msg_count == sizeof(struct remote_message)) { + rx_msg_count = 0; + mb(); + rx_msg_valid = 1; + } + } +} + +void print_pgm(const prog_char *str) +{ + struct remote_message msg; + uint8_t c, i; + + do { + memset(&msg, 0, sizeof(msg)); + msg.id = MSG_LOGMESSAGE; + + for (i = 0; i < sizeof(msg.logmessage.str); i++) { + c = pgm_read_byte(str); + if (c == '\0') + break; + str++; + msg.logmessage.str[i] = c; + } + + send_message(&msg); + } while (c != '\0'); +} + +/* Maintanance work. Called with IRQs enabled. */ +void remote_work(void) +{ + if (rx_msg_valid) { + handle_received_message(); + mb(); + rx_msg_valid = 0; + } +} + +static void usart_init(void) +{ + uint8_t dummy; + + /* Set baud rate */ + UBRRL = lo8((CPU_HZ / 16 / BAUDRATE) * 2); + UBRRH = hi8((CPU_HZ / 16 / BAUDRATE) * 2) & ~(1 << URSEL); + UCSRA = (1 << U2X); + /* 8 Data bits, 2 Stop bits, Even parity */ + UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1) | (1 << UPM1) | (1 << USBS); + /* Enable transceiver and RX IRQs */ + UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE); + /* Drain the RX buffer */ + while (usart_rx(&dummy) != -ENODATA) + mb(); +} + +void remote_init(void) +{ + /* The remote tool depends on the exact size (and layout). */ + BUILD_BUG_ON(sizeof(struct remote_message) != 38); + + usart_init(); +} diff --git a/pressure_control/firmware/remote.h b/pressure_control/firmware/remote.h new file mode 100644 index 0000000..af18abf --- /dev/null +++ b/pressure_control/firmware/remote.h @@ -0,0 +1,68 @@ +#ifndef REMOTE_H_ +#define REMOTE_H_ + +#include + +#include + + +enum remote_message_id { + MSG_INVALID = 0, /* Discard me */ + MSG_ERROR, + MSG_LOGMESSAGE, + MSG_PING, + MSG_PONG, + MSG_GET_CURRENT_PRESSURE, + MSG_CURRENT_PRESSURE, + MSG_GET_DESIRED_PRESSURE, + MSG_DESIRED_PRESSURE, + MSG_SET_DESIRED_PRESSURE, + MSG_GET_HYSTERESIS, + MSG_HYSTERESIS, + MSG_SET_HYSTERESIS, + MSG_GET_CONFIG_FLAGS, + MSG_CONFIG_FLAGS, + MSG_SET_CONFIG_FLAGS, +}; + +enum remote_message_error { + MSG_ERR_NONE = 0, + MSG_ERR_CHKSUM, +}; + +enum remote_message_config_flags { + CFG_FLAG_AUTOADJUST_ENABLE = 0, +}; + +struct remote_message { + uint8_t id; + uint8_t __padding0[3]; + + union { + struct { + uint8_t code; + } __attribute__((packed)) error; + struct { + char str[32]; + } __attribute__((packed)) logmessage; + struct { + uint16_t mbar; + } __attribute__((packed)) pressure; + struct { + uint32_t flags; + } __attribute__((packed)) config; + + uint8_t __padding1[32]; + } __attribute__((packed)); + + uint16_t crc; +} __attribute__((packed)); + + +void print_pgm(const prog_char *msg); +#define print(string_literal) print_pgm(PSTR(string_literal)) + +void remote_work(void); +void remote_init(void); + +#endif /* REMOTE_H_ */ diff --git a/pressure_control/firmware/sensor.c b/pressure_control/firmware/sensor.c index e663d7a..2ed36f3 100644 --- a/pressure_control/firmware/sensor.c +++ b/pressure_control/firmware/sensor.c @@ -19,10 +19,65 @@ */ #include "sensor.h" +#include "util.h" #include +#include +/*** The sensor enable signal ***/ +#define SENSOR_ENABLE_DDR DDRC +#define SENSOR_ENABLE_PORT PORTC +#define SENSOR_ENABLE_BIT 1 + + +static inline void sensor_enable(void) +{ + SENSOR_ENABLE_PORT |= (1 << SENSOR_ENABLE_BIT); +} + +static inline void sensor_disable(void) +{ + SENSOR_ENABLE_PORT &= ~(1 << SENSOR_ENABLE_BIT); +} + +ISR(ADC_vect) +{ + uint16_t val; + + val = ADC; + sensor_disable(); + //TODO process value + sensor_result(val); +} + +static inline void adc_trigger(bool with_irq) +{ + /* Set the multiplexer to ADC-0, AVcc Ref. */ + ADMUX = (1 << REFS0); + /* Start ADC with a prescaler of 128. That's a ADC freq + * of 125kHz on a 16MHz crystal. */ + ADCSRA = (1 << ADEN) | (1 << ADSC) | + (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | + (with_irq ? (1 << ADIE) : 0); +} + +void sensor_trigger_read(void) +{ + /* Enable the sensor and wait a dwell time for the + * sensor to stabilize. */ + sensor_enable(); + udelay(500); + /* Finally trigger the ADC conversion. */ + adc_trigger(1); +} + void sensor_init(void) { + SENSOR_ENABLE_DDR |= (1 << SENSOR_ENABLE_BIT); + sensor_disable(); + /* Discard the first ADC result. */ + adc_trigger(0); + while (ADCSRA & (1 << ADSC)) + mb(); } diff --git a/pressure_control/firmware/sensor.h b/pressure_control/firmware/sensor.h index dd03e29..a6230ad 100644 --- a/pressure_control/firmware/sensor.h +++ b/pressure_control/firmware/sensor.h @@ -4,6 +4,10 @@ #include +void sensor_trigger_read(void); void sensor_init(void); +/* Callback for sensor value reporting. */ +extern void sensor_result(uint16_t millibar_result_value); + #endif /* SENSOR_H_ */ diff --git a/pressure_control/firmware/util.c b/pressure_control/firmware/util.c index 3cecc46..0056cbd 100644 --- a/pressure_control/firmware/util.c +++ b/pressure_control/firmware/util.c @@ -1,22 +1,26 @@ /* - * Utility functions + * Utility functions. * - * Copyright (C) 2008 Michael Buesch + * Copyright (C) 2008 Michael Buesch * - * 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 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 3 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. + * 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, see . */ #include "util.h" #include "calibration.h" +#include #include #include @@ -76,3 +80,16 @@ void infinite_sleep(void) while (1) sleep_mode(); } + +uint16_t crc16_block_update(uint16_t crc, const void *_data, uint16_t size) +{ + const uint8_t *data = _data; + + while (size) { + crc = _crc16_update(crc, *data); + data++; + size--; + } + + return crc; +} diff --git a/pressure_control/firmware/util.h b/pressure_control/firmware/util.h index 11c5f27..0c37990 100644 --- a/pressure_control/firmware/util.h +++ b/pressure_control/firmware/util.h @@ -72,4 +72,7 @@ static inline void irq_restore(uint8_t sreg_flags) #define irqs_disabled() (!(SREG & (1 << SREG_I))) + +uint16_t crc16_block_update(uint16_t crc, const void *data, uint16_t size); + #endif /* UTIL_H_ */ -- cgit v1.2.3