From 5eb5a1d895695f7821868b34732d0d6eca2fdb23 Mon Sep 17 00:00:00 2001 From: Bernhard Tittelbach Date: Tue, 15 Oct 2013 00:53:11 +0000 Subject: various bugfixes and improvements + documentation git-svn-id: https://svn.spreadspace.org/avr/trunk@245 aa12f405-d877-488e-9caf-2d797e2a1cc7 --- pcr-controller/Makefile | 2 +- pcr-controller/cmd_queue.c | 74 ++++++++++------- pcr-controller/cmd_queue.h | 7 ++ pcr-controller/pcr-controller.c | 172 +++++++++++++++++++++++++--------------- pcr-controller/pid_control.c | 6 +- pcr-controller/protocol_uc.txt | 166 +++++++++++++++++++++++--------------- pcr-controller/temp_curve.c | 40 +++++++++- pcr-controller/temp_curve.h | 2 + 8 files changed, 302 insertions(+), 167 deletions(-) (limited to 'pcr-controller') diff --git a/pcr-controller/Makefile b/pcr-controller/Makefile index 1ed2909..e5d923c 100644 --- a/pcr-controller/Makefile +++ b/pcr-controller/Makefile @@ -26,7 +26,7 @@ OBJ := $(NAME).o pwm.o pid_control.o temp_curve.o cmd_queue.o LIBS := util led lufa-descriptor-usbserial anyio onewire ds1820 EXTERNAL_LIBS := lufa RESET_FUNC := ../tools/reset_lufa_cdc -RESET_PARAM := 'r' +RESET_PARAM := 'R' LUFA_PATH := ../contrib/LUFA-120219 LUFA_OPTS = -D USB_DEVICE_ONLY diff --git a/pcr-controller/cmd_queue.c b/pcr-controller/cmd_queue.c index 65cf001..6d12296 100644 --- a/pcr-controller/cmd_queue.c +++ b/pcr-controller/cmd_queue.c @@ -19,59 +19,61 @@ */ #include +#include #include "cmd_queue.h" +//static assert: +//~ static int SDFLKJ[sizeof(int) == sizeof(int16_t)] = { 0 }; + +#define CMDQ_INPUT_LIST_LEN 2 typedef struct { - uint8_t read_num_numbers; + uint8_t num_args; void* function_ptr; + int16_t input_list[CMDQ_INPUT_LIST_LEN]; + uint8_t num_args_read; } cmd_queue_item; #define CMQ_QUEUE_LENGTH 4 cmd_queue_item cmd_queue_[CMQ_QUEUE_LENGTH]; uint8_t cmd_queue_todo_pos_ = 0; +uint8_t cmd_queue_fillargs_pos_ = 0; uint8_t cmd_queue_next_here_ = 0; -#define CMDQ_INPUT_LIST_LEN 2 -int16_t input_list_[CMDQ_INPUT_LIST_LEN]; -uint8_t num_args_read_ = 0; + #define CMDQ_READBUFFER_LEN 20 char cmdq_readbuffer_[CMDQ_READBUFFER_LEN]; -char *cmdq_readbuffer_pos_ = cmdq_readbuffer_; -char *const cmdq_readbuffer_end_ = cmdq_readbuffer_ + (CMDQ_READBUFFER_LEN - 1); +uint8_t cmdq_readbuffer_pos_ = 0; inline void cmdq_finishReadingArgument(void) { - *cmdq_readbuffer_pos_ = '\0'; - cmdq_readbuffer_pos_ = cmdq_readbuffer_; - input_list_[num_args_read_++] = atoi(cmdq_readbuffer_); + cmdq_readbuffer_[cmdq_readbuffer_pos_] = '\0'; + cmdq_readbuffer_pos_ = 0; + cmd_queue_[cmd_queue_fillargs_pos_].input_list[cmd_queue_[cmd_queue_fillargs_pos_].num_args_read++] = atoi(cmdq_readbuffer_); } //call regularily if in state CQ_READING uint8_t cmdq_addCharToArgumentBuffer(uint8_t c) { if ( cmd_queue_todo_pos_ == cmd_queue_next_here_ ) - return 1; //nothing to do with us - if (num_args_read_ >= CMDQ_INPUT_LIST_LEN) + return 1; //nothing to do with us, since no cmd's queued + if (cmd_queue_[cmd_queue_fillargs_pos_].num_args_read >= CMDQ_INPUT_LIST_LEN) return 1; //nothing to do with us - if (num_args_read_ >= cmd_queue_[cmd_queue_todo_pos_].read_num_numbers ) + if (cmd_queue_[cmd_queue_fillargs_pos_].num_args_read >= cmd_queue_[cmd_queue_fillargs_pos_].num_args ) return 1; //nothing to do with us - //if input terminated by \n or \r then addArgument - if (c == '\n' || c == '\r') + if (c == '\n' || c == '\r' || c == ',' || c == 0x1B) { cmdq_finishReadingArgument(); return 0; } else { //continue reading - *cmdq_readbuffer_pos_ = (char) c; - cmdq_readbuffer_pos_++; + cmdq_readbuffer_[cmdq_readbuffer_pos_++] = (char) c; } - //if numlen of readbuffer reached, addArgument as well - if (cmdq_readbuffer_pos_ == cmdq_readbuffer_end_) + //if numlen +1 of readbuffer reached, addArgument as well (leave one char free to terminate with \0) + if (cmdq_readbuffer_pos_ +1 >= CMDQ_READBUFFER_LEN) { cmdq_finishReadingArgument(); - return 0; } return 0; } @@ -79,35 +81,47 @@ uint8_t cmdq_addCharToArgumentBuffer(uint8_t c) void cmdq_queueCmdWithNumArgs(void* fptr, uint8_t num_args) { if (num_args > CMDQ_INPUT_LIST_LEN) + { + printf("{\"cmd_ok\":false,\"error\":\"max args == %d\"}\r\n", CMDQ_INPUT_LIST_LEN); return; //can't do that Would hang cmdq - cmd_queue_[cmd_queue_next_here_].read_num_numbers=num_args; - cmd_queue_[cmd_queue_next_here_].function_ptr=fptr; + } + if ((cmd_queue_next_here_ +1) % CMQ_QUEUE_LENGTH == cmd_queue_todo_pos_) //for this check REQUIRED: CMQ_QUEUE_LENGTH > 2 + cmdq_doWork(); //no more space in queue -> execute now ! + cmd_queue_[cmd_queue_next_here_].num_args = num_args; + cmd_queue_[cmd_queue_next_here_].function_ptr = fptr; + cmd_queue_[cmd_queue_next_here_].num_args_read = 0; + cmd_queue_fillargs_pos_ = cmd_queue_next_here_; cmd_queue_next_here_++; cmd_queue_next_here_ %= CMQ_QUEUE_LENGTH; } void cmdq_doWork(void) { - if ( cmd_queue_todo_pos_ == cmd_queue_next_here_ ) - return; - - // is num_args_read_ now equal to num args we expect ? - if (num_args_read_ == cmd_queue_[cmd_queue_todo_pos_].read_num_numbers ) + while ( cmd_queue_todo_pos_ != cmd_queue_next_here_ ) { - switch (num_args_read_) + // is num_args_read_ now equal to num args we expect ? + if (cmd_queue_[cmd_queue_todo_pos_].num_args_read != cmd_queue_[cmd_queue_todo_pos_].num_args ) + break; + if (cmd_queue_[cmd_queue_todo_pos_].function_ptr == 0 ) + break; + + switch (cmd_queue_[cmd_queue_todo_pos_].num_args) { case 0: ((void(*)(void)) cmd_queue_[cmd_queue_todo_pos_].function_ptr)(); break; case 1: - ((void(*)(int16_t)) cmd_queue_[cmd_queue_todo_pos_].function_ptr)(input_list_[0]); + ((void(*)(int16_t)) cmd_queue_[cmd_queue_todo_pos_].function_ptr)(cmd_queue_[cmd_queue_todo_pos_].input_list[0]); break; case 2: - ((void(*)(int16_t,int16_t)) cmd_queue_[cmd_queue_todo_pos_].function_ptr)(input_list_[0], input_list_[1]); + ((void(*)(int16_t,int16_t)) cmd_queue_[cmd_queue_todo_pos_].function_ptr)(cmd_queue_[cmd_queue_todo_pos_].input_list[0], cmd_queue_[cmd_queue_todo_pos_].input_list[1]); break; } - num_args_read_ = 0; + cmd_queue_[cmd_queue_todo_pos_].num_args_read = 0; + cmd_queue_[cmd_queue_todo_pos_].num_args = 0; + cmd_queue_[cmd_queue_todo_pos_].function_ptr = 0; cmd_queue_todo_pos_++; cmd_queue_todo_pos_ %= CMQ_QUEUE_LENGTH; + printf("{\"cmd_ok\":true}\r\n"); } } \ No newline at end of file diff --git a/pcr-controller/cmd_queue.h b/pcr-controller/cmd_queue.h index 84b8a01..d632f43 100644 --- a/pcr-controller/cmd_queue.h +++ b/pcr-controller/cmd_queue.h @@ -23,6 +23,13 @@ #include +/* The only actual reason for this queue is to enable non-blocking reads. +* I.e. we want to avoid blocking the main loop while waiting on a user-input terminating '\n', '\r' or ',' . +* As we could (and DO if the queue is full) execute a command right away once all arguments have been read, +* the queue really does not need to be 4 long and the whole handling could me much much simpler. +* I.e. only ever remember ONE command and execute it as soon as all args are present! +*/ + uint8_t cmdq_addCharToArgumentBuffer(uint8_t c); void cmdq_queueCmdWithNumArgs(void* fptr, uint8_t num_args); void cmdq_doWork(void); diff --git a/pcr-controller/pcr-controller.c b/pcr-controller/pcr-controller.c index 95a3c19..1b8741b 100644 --- a/pcr-controller/pcr-controller.c +++ b/pcr-controller/pcr-controller.c @@ -53,23 +53,42 @@ #define HIGHv OP_SETBIT #define LOWv OP_CLEARBIT -#define PUMP_PIN PINB3 -#define PELTIER_INA PINF7 -#define PELTIER_INB PINB6 -#define PELETIER_PWM_EN PINB5 +#define PELTIER_INA_PIN PINF7 +#define PELTIER_INA_PORT PORTF +#define PELTIER_INA_DDR DDRF + +#define PELTIER_INB_PIN PINB6 +#define PELTIER_INB_PORT PORTB +#define PELTIER_INB_DDR DDRB + +#define PELETIER_PWM_EN_PIN PINB5 +#define PELETIER_PWM_EN_PORT PORTB +#define PELETIER_PWM_EN_DDR DDRB + #define TOPHEAT_PIN PIND7 +#define TOPHEAT_PORT PORTD +#define TOPHEAT_DDR DDRD + +#define PUMP_PIN PINC6 +#define PUMP_PORT PORTC +#define PUMP_DDR DDRC + +#define ONEWIRE_PIN PINC7 +#define ONEWIRE_PINBASE PINC uint8_t num_temp_sensors_ = 0; int16_t raw_temp_ = 0; +uint8_t temp_is_fresh_ = 0; uint8_t debug_ = 0; uint8_t monitor_temp_ = 0; // at f_system_clk = 10Hz, system_clk_ will not overrun for at least 13 years. PCR won't run that long -uint32_t system_clk_ = 0; +volatile uint32_t system_clk_ = 0; //with F_CPU = 16MHz and TIMER3 Prescaler set to /1024, TIMER3 increments with f = 16KHz. Thus if TIMER3 reaches 16, 1ms has passed. #define T3_MS *16 //set TICK_TIME to 1/10 of a second -#define TICK_TIME (100 T3_MS) +#define SYSCLKTICK_DURATION_IN_MS 100 +#define TICK_TIME (SYSCLKTICK_DURATION_IN_MS T3_MS) ISR(TIMER3_COMPA_vect) { @@ -77,6 +96,8 @@ ISR(TIMER3_COMPA_vect) system_clk_++; //set up "clock" comparator for next tick OCR3A = (OCR3A + TICK_TIME) & 0xFFFF; + if (debug_) + led_toggle(); } void initSysClkTimer3(void) @@ -96,6 +117,7 @@ void initSysClkTimer3(void) void queryAndSaveTemperature(uint8_t bit_resolution) { + static uint32_t conversion_start_time = 0; uint8_t sensor_index = 0; if (num_temp_sensors_ == 0) @@ -103,21 +125,31 @@ void queryAndSaveTemperature(uint8_t bit_resolution) num_temp_sensors_ = ds1820_discover(); } - for (sensor_index=0; sensor_index < num_temp_sensors_; sensor_index++) + if (conversion_start_time == 0) { - ds1820_set_resolution(sensor_index, bit_resolution); - ds1820_start_measuring(sensor_index); - } - - ds1820_wait_conversion_time(bit_resolution); - - for (sensor_index=0; sensor_index < num_temp_sensors_; sensor_index++) + // -- trigger temperatur conversion in sensor + for (sensor_index=0; sensor_index < num_temp_sensors_; sensor_index++) + { + ds1820_set_resolution(sensor_index, bit_resolution); + ds1820_start_measuring(sensor_index); + } + conversion_start_time = system_clk_; + // -- end trigger + } else + if ( (system_clk_ - conversion_start_time)*SYSCLKTICK_DURATION_IN_MS > ds1820_get_conversion_time_ms(bit_resolution)) { - raw_temp_ = ds1820_read_temperature(sensor_index); - if (raw_temp_ != DS1820_ERROR) - { - break; //we need only one successfully read value - } + // -- receive temperatur after conversion time has passed + for (sensor_index=0; sensor_index < num_temp_sensors_; sensor_index++) + { + raw_temp_ = ds1820_read_temperature(sensor_index); + if (raw_temp_ != DS1820_ERROR) + { + temp_is_fresh_ = 1; + break; //we need only one successfully read value + } + } + conversion_start_time = 0; + // -- end receive } } @@ -182,16 +214,16 @@ void setPeltierCoolingDirectionPower(int16_t value) if (value >= 0) { - PIN_HIGH(PORTF, PELTIER_INA); - PIN_LOW(PORTB, PELTIER_INB); + PIN_HIGH(PELTIER_INA_PORT, PELTIER_INA_PIN); + PIN_LOW(PELTIER_INB_PORT, PELTIER_INB_PIN); pwm_set((uint8_t) value); } else { - PIN_LOW(PORTF, PELTIER_INA); - PIN_HIGH(PORTB, PELTIER_INB); + PIN_LOW(PELTIER_INA_PORT, PELTIER_INA_PIN); + PIN_HIGH(PELTIER_INB_PORT, PELTIER_INB_PIN); pwm_set((uint8_t) (-1 * value)); } if (debug_) - printf("Peltier value: %d, INA: %d, INB: %d\r\n", value, (PORTF & _BV(PELTIER_INA)) > 0, (PORTB & _BV(PELTIER_INB)) > 0); + printf("Peltier value: %d, INA: %d, INB: %d\r\n", value, (PELTIER_INA_PORT & _BV(PELTIER_INA_PIN)) > 0, (PELTIER_INB_PORT & _BV(PELTIER_INB_PIN)) > 0); } void handle_cmd(uint8_t cmd) @@ -210,29 +242,31 @@ void handle_cmd(uint8_t cmd) case 't': case 's': printStatus(); return; case 'L': led_toggle(); break; - case 'l': cmdq_queueCmdWithNumArgs(led_toggle, 0); break; + case 'l': cmdq_queueCmdWithNumArgs((void*) led_toggle, 0); return; case 'p': case 'i': case 'd': pid_printVars(); return; - case 'T': cmdq_queueCmdWithNumArgs(pid_setTargetValue, 1); break; - case 'P': cmdq_queueCmdWithNumArgs(pid_setP, 1); break; - case 'I': cmdq_queueCmdWithNumArgs(pid_setI, 1); break; - case 'D': cmdq_queueCmdWithNumArgs(pid_setD, 1); break; - case 'A': PIN_HIGH(PORTB, PUMP_PIN); break; - case 'a': PIN_LOW(PORTB, PUMP_PIN); break; - case 'B': PIN_HIGH(PORTD, TOPHEAT_PIN); break; - case 'b': PIN_LOW(PORTD, TOPHEAT_PIN); break; + case 'T': cmdq_queueCmdWithNumArgs((void*) pid_setTargetValue, 1); return; + case 'P': cmdq_queueCmdWithNumArgs((void*) pid_setP, 1); return; + case 'I': cmdq_queueCmdWithNumArgs((void*) pid_setI, 1); return; + case 'D': cmdq_queueCmdWithNumArgs((void*) pid_setD, 1); return; + case 'A': PIN_HIGH(PUMP_PORT, PUMP_PIN); break; + case 'a': PIN_LOW(PUMP_PORT, PUMP_PIN); break; + case 'B': PIN_HIGH(TOPHEAT_PORT, TOPHEAT_PIN); break; + case 'b': PIN_LOW(TOPHEAT_PORT, TOPHEAT_PIN); break; + case '.': tcurve_printCurve(); return; case '-': //reset temp curve tcurve_reset(); break; case '+': //add temp curve entry //~ tcurve_add(readNumber(), readNumber()); - cmdq_queueCmdWithNumArgs(tcurve_add, 2); - break; - case 'Z': cmdq_queueCmdWithNumArgs(tcurve_setRepeats, 1); break; - case 'E': cmdq_queueCmdWithNumArgs(tcurve_setPostCycleTargetTemp, 1); break; + cmdq_queueCmdWithNumArgs((void*) tcurve_add, 2); + return; + case '!': cmdq_queueCmdWithNumArgs((void*) tcurve_setRepeatStartPosToLatestEntry, 0); return; + case 'Z': cmdq_queueCmdWithNumArgs((void*) tcurve_setRepeats, 1); return; + case 'E': cmdq_queueCmdWithNumArgs((void*) tcurve_setPostCycleTargetTemp, 1); return; default: printf("{\"cmd_ok\":false,\"error\":\"unknown cmd\"}\r\n"); return; } printf("{\"cmd_ok\":true}\r\n"); @@ -250,13 +284,13 @@ int main(void) sei(); led_off(); - owi_init(PINC7, &PINC); - PINMODE_OUTPUT(DDRB, PUMP_PIN); - PIN_LOW(PORTB, PUMP_PIN); - PINMODE_OUTPUT(DDRB, PELETIER_PWM_EN); - PINMODE_OUTPUT(DDRB, PELTIER_INB); - PINMODE_OUTPUT(DDRF, PELTIER_INA); - PINMODE_OUTPUT(DDRD, TOPHEAT_PIN); + owi_init(ONEWIRE_PIN, &ONEWIRE_PINBASE); + PINMODE_OUTPUT(PUMP_DDR, PUMP_PIN); + PIN_LOW(PUMP_PORT, PUMP_PIN); + PINMODE_OUTPUT(PELETIER_PWM_EN_DDR, PELETIER_PWM_EN_PIN); + PINMODE_OUTPUT(PELTIER_INB_DDR, PELTIER_INB_PIN); + PINMODE_OUTPUT(PELTIER_INA_DDR, PELTIER_INA_PIN); + PINMODE_OUTPUT(TOPHEAT_DDR, TOPHEAT_PIN); pwm_init(); pwm_set(0); @@ -286,34 +320,40 @@ int main(void) cmdq_doWork(); //may call queued functions - queryAndSaveTemperature(11); //at 11bit resolution, this takes at least 390ms - - if (monitor_temp_) - printStatus(); + queryAndSaveTemperature(11); //at 11bit resolution - if (tcurve_isSet()) + if (temp_is_fresh_) { - uint16_t time_elapsed = (uint16_t) (system_clk_ - last_time); - last_time = system_clk_; - //PID_DISABLED == TCURVE_ERROR so this works out fine and we disable heating and PID in this case - pid_setTargetValue(tcurve_getTempToSet(raw_temp_, time_elapsed)); - if (debug_) + temp_is_fresh_ = 0; //once used, temp is used up ;-> + + if (monitor_temp_) + printStatus(); + + if (tcurve_isSet()) { - printf("t: %lu, elapsed: %u, target_temp: %d\r\n", system_clk_, time_elapsed, pid_getTargetValue()); + uint16_t time_elapsed = (uint16_t) (system_clk_ - last_time); + last_time = system_clk_; + //PID_DISABLED == TCURVE_ERROR so this works out fine and we disable heating and PID in this case + pid_setTargetValue(tcurve_getTempToSet(raw_temp_, time_elapsed)); } - } - // PID control - // make sure this is called at exact periodic intervals (i.e. make sure there are no large variable delays in for loop) - // e.g. enable periodic temp monitoring 'm' rather than querying temp at some intervall 's' - if (pid_isEnabled()) - { - while (system_clk_ - last_time2 < 5 * TICK_TIME); //wait until at least 500ms have passed since last time. Should be enough time for everything else to finish. (after 13 years, code will hang here) - last_time2 = system_clk_; - setPeltierCoolingDirectionPower(pid_calc(raw_temp_)); + // PID control + // make sure this is called at exact periodic intervals (i.e. make sure there are no large variable delays in for loop) + // e.g. enable periodic temp monitoring 'm' rather than querying temp at some intervall 's' + if (pid_isEnabled()) + { + if (debug_) + printf("pid_calc.."); + while (system_clk_ - last_time2 < 5); //wait until at least 500ms have passed since last time. Should be enough time for everything else to finish. (after 13 years, code will hang here) + if (debug_) + printf(" clk: %lu, elaps: %lu\r\n",system_clk_ , system_clk_ - last_time2); + last_time2 = system_clk_; + temp_is_fresh_ = 0; + setPeltierCoolingDirectionPower(pid_calc(raw_temp_)); + } + else + setPeltierCoolingDirectionPower(0); } - else - setPeltierCoolingDirectionPower(0); anyio_task(); } diff --git a/pcr-controller/pid_control.c b/pcr-controller/pid_control.c index f1d7d3f..95d190e 100644 --- a/pcr-controller/pid_control.c +++ b/pcr-controller/pid_control.c @@ -85,12 +85,12 @@ int16_t pid_calc(int16_t current_value) if (pid_target_value_ == PID_DISABLED) return PID_DISABLED; - int32_t error = pid_target_value_ - current_value; + int32_t error = (int32_t) pid_target_value_ - (int32_t) current_value; // derivative // instead of derivative of error we take derivative on measurement // since dError/dt = - dInput/dt (note the - ) - int32_t d_measure = current_value - pid_last_input_; - pid_last_input_ = current_value; + int32_t d_measure = (int32_t) current_value - pid_last_input_; + pid_last_input_ = (int32_t) current_value; // integral (bring pid_i_ into integral, so we get smooth transfer if pid_i_ suddenly changes) pid_i_integralsum_ += pid_i_ * error; diff --git a/pcr-controller/protocol_uc.txt b/pcr-controller/protocol_uc.txt index 68c4c90..18a2465 100644 --- a/pcr-controller/protocol_uc.txt +++ b/pcr-controller/protocol_uc.txt @@ -1,72 +1,110 @@ -Command bytes: -'s' Temperatursensor lesen -> parse_float(string("%3.2f")) + \r\n -'T' Temperatursoll setzen <- string (int(float(temp)*16)) -'t' Temperatursoll lesen -> parse_float(string("%3.2f")) + \r\n -'P' P-Wert setzen <- string (uint16_t x * 1024) -'p' Alle PID Werte ausgeben -> string (uint16_t) + \r\n -'I' I-Wert setzen <- string (uint16_t x * 1024) -'i' Alle PID Werte ausgeben -> string (uint16_t) + \r\n -'D' D-Wert setzen <- string (uint16_t x * 1024) -'d' Alle PID Werte ausgeben -> string (uint16_t p / 1024, i / 1024, d / 1024) + \r\n -'A' Pumpe on -'a' Pumpe off -'B' Deckelheizung on -'b' Deckelheizung off -'L' Toggle LED (Debug) -'l' Toggle LED by using queuing system (Debug) -'R' Reset (Alle Register zurücksetzen und neu initialisieren) -'=' Halte aktuelle Temperatur (PID Ziel gleich aktuelle Temp messen) -'-' Lösche gesetzte Temperaturkurve -'+' Füge einen Punkt zur Temperaturkurve hinzu. erwartet Zieltemperatur und zeit (in10tel sekunden) für wie lange die temperatur gehalten werden soll bevor zur nächsten gesprungen wird. <- string(int(float(temp)*16)) \n <- string(int(seconds*10)) \n -'Z' setzte Anzahl Zyklen in denen die Temperaturkurve wiederholt wird (0 == default == keine Wiederholung == 1 Zyklus, 1 == 1 Wiederholung == 2 Zyklen) <- string(int(repeats)) -'E' Temperatursoll das nach Ende des Temperaturkurvenzyklus gesetzt werden soll <- string (int(float(temp)*16)) -'m' Schalte ständige Ausgabe der aktuellen Temperatur ein/aus - -Nach jedem Setz-Command: Bestätigung mit "OK\r\n" (oder so) - - - -Das folgende Beispiel -fragt die Temperatur ab, -löscht eine evtl gesetzte Temperaturkurve -Hält für's Erste die aktuelle Temperatur -Schaltet die Pumpe ein -Programmiert eine Kurve von 95°C (15min) -> 65°C (1s) -> 60°C (30s) -> 70°C (30s) -Wiederholt die Kurve 30 mal -Kühlt die Proben nach dem Ende auf 5°C -Gibt periodisch die aktuelle Temperatur aus. - -Example PCR: +==== Command bytes ==== +'s' Temperatursensor lesen -> parse_float(string("%3.2f")) + \r\n +'t' Same as above +'T' Set PID target temperature. <-TempValue + (Only works if no temperature curve is set.) +'P' Set PID value P <-PIDValue +'I' Set PID value I <-PIDValue +'D' Set PID value D <-PIDValue +'p' Print all PID values as JSON string +'i' Same as above +'d' Same as above +'A' Switch on pump. +'a' Switch pump off. +'B' Switch on top heater. +'b' Switch top heater off. +'L' Toggle LED (Debug) +'l' Toggle LED by using queuing system (Debug) +'R' Reset microcontroller +'r' Same as above +'=' Set PID target temperature to current temperature. +'-' Clear temperature curve. +'.' Print out currently programmed temperature curve as JSON string. +'+' Add a temperature curve point. <-TempValue <-Duration + Excpects 2 arguments: Targettemperature and duration for which that temperature is to be held after it has been reached. +'!' Set the last added (with '+') temperature curve point to be the cycle start point. + E.g. if >0 curve repetitions are set (with 'Z') the repetitions will start here, instead of at the first added curve point. +'Z' Set number of cycle repetitions <-IntValue + E.g: 0 == default == no repetitions == 1 cycle + 1 == 1 repetitions == 2 cycles) + .... +'E' Program a final hold temperature. PID will try to hold this value after all cycles have finished. <-TempValue +'m' Toggle periodic status output as JSON string. +'?' Toggle Debug Output. +'#' Disable PID (i.e. set PID target temperature to -2048.00) + (Only works if no temperature curve is set. If needed use '-' first.) + + +==== Command arguments ==== +If arguments are required they are to be given as numeric string. + +Temperature is given in degrees celcsius multiplied by 16, thus allowing us to set decimal degrees by storing and transmitting integer values. +Negative temperatures are prepended by '-'. +E.g. a temperature of -20 °C would be input as -320. + +PID parpameters are multiplied by 1024 (or whatever PID_SCALE is set to). +E.g. an I value of 0.5 would be input as 512. + +Duration or time values are input as 100ms (1/10th second) integer values. +E.g. a duration of 2 seconds would be input as 20. + +If multiple arguments are required, they are to be separated by the comma character ','. + +<-IntValue ...means: string(uint16_t x) +<-TempValue ...means: string(int16_t(float temp * 16)) +<-PIDValue ...means: string(uint16_t x * 1024) +<-Duration ...means: string(uint16_t s * 10) + + +==== Command responses ==== +are formated as JSON values. +PID Values are output just like they are expected to be input. +Temperatures are output as formated float values in °C, expect when printing the current temperature curve when temperatures are printed as internal integer values (see TempValue) + +A value of -2048.00 or -32768 generally means something is DISABLED or inapplicable in the current state. + + +==== Example uc Answers ==== +Responses to "t" or "s": +{"t":503, "currtemp":20.34, "targettemp":-2048.00, "curve":false, "curve_t_elapsed":0, "cycles_left":0} //curve and pid disabled (pid disabel when targettemp == -2048.00 +{"t":97000, "currtemp":40.34, "targettemp":70.00, "curve":true, "curve_t_elapsed":14, "cycles_left":11} +{"t":97200, "currtemp":69.72, "targettemp":70.00, "curve":true, "curve_t_elapsed":214, "cycles_left":11} +{"t":110123, "currtemp":30.14, "targettemp":30.00, "curve":true, "curve_t_elapsed":10, "cycles_left":9} +{"t":9999999, "currtemp":5.02, "targettemp":5.00, "curve":true, "curve_t_elapsed":65535, "cycles_left":0} //we know all is finished because targettemp is hold-temp + +Response to "." +{"curve":[{"temp":1520,"duration":9000,"is_curr":1,"is_loop_start":1},{"temp":1040,"duration":10,"is_curr":0,"is_loop_start":0},{"temp":960,"duration":300,"is_curr":0,"is_loop_start":0},{"temp":1120,"duration":300,"is_curr":0,"is_loop_start":0},0],"end_temp:":80} + +Response to other commands: +{"cmd_ok":false,"error": "No DS1820 sensors on 1wire bus, thus no temperature"} +{"cmd_ok":false,"error":"talking to DS18b20, no valid temperature!"} +{"cmd_ok":true} + + +==== Example Program ==== s - = A b -+ -1520 -9000 -+ -1040 -10 -+ -960 -300 -+ -1120 -300 -Z -30 -E -80 ++1520,9000 ++1040,10 +! ++960,300 ++1120,300 +Z30 +E80 m +The sequence of above commands +- prints current temperature +- deletes any currenlty set temperature curve +- instructs the PID controller to hold the current temperature +- enables the pump +- programms the following temperature curve: 95°C (15min) -> [ -> 65°C (1s) -> 60°C (30s) -> 70°C (30s) ] +- sets the curve to repeat the items in [] brackets 30 times +- programms final holding temperature of 5°C (after temperature curve and all cylces have finished). +- activates periodic status output. -Example uc Answers: -{"cmd_ok":false,"error": "No DS1820 sensors on 1wire bus, thus no temperature"} -{"cmd_ok":false,"error":"talking to DS18b20, no valid temperature!"} -{"cmd_ok":true} -{"t":503, "currtemp":20.34, "targettemp":-2048.00, "curve":false, "curve_t_elapsed":0, "cycles_left":0} //curve and pid disabled (pid disabel when targettemp == -2048.00 -{"t":97000, "currtemp":40.34, "targettemp":70.00, "curve":true, "curve_t_elapsed":14, "cycles_left":11} -{"t":97200, "currtemp":69.72, "targettemp":70.00, "curve":true, "curve_t_elapsed":214, "cycles_left":11} -{"t":110123, "currtemp":30.14, "targettemp":30.00, "curve":true, "curve_t_elapsed":10, "cycles_left":9} -{"t":9999999, "currtemp":5.02, "targettemp":5.00, "curve":true, "curve_t_elapsed":65535, "cycles_left":0} //we know all is finished because targettemp is hold-temp + +(c) Bernhard Tittelbach, 2013 diff --git a/pcr-controller/temp_curve.c b/pcr-controller/temp_curve.c index 534ddfa..36ec55a 100644 --- a/pcr-controller/temp_curve.c +++ b/pcr-controller/temp_curve.c @@ -20,6 +20,9 @@ #include "temp_curve.h" #include +#include + +extern uint8_t debug_; const int16_t temp_margin_ = 8; // 0.5 °C @@ -33,6 +36,7 @@ struct tc_entry { tc_entry *temp_curve_ = 0; tc_entry *temp_curve_end_ = 0; tc_entry *temp_curve_current_ = 0; +tc_entry *temp_curve_restart_pos_ = 0; uint16_t temp_stable_time_ = 0; @@ -45,15 +49,22 @@ void tcurve_reset(void) tc_entry *curr = temp_curve_; tc_entry *next = 0; while (curr != 0) { - next = temp_curve_->next; + next = curr->next; + if (debug_) + printf("tcreset: curr: 0x%x, next: 0x%x, end: 0x%x\r\n",(uint16_t)curr,(uint16_t)next,(uint16_t)temp_curve_end_); free(curr); + if (curr == temp_curve_end_) + break; //just to be sure curr = next; } temp_curve_ = 0; temp_curve_end_ = 0; temp_curve_current_ = 0; curve_num_repeats_ = 0; + temp_curve_restart_pos_ = temp_curve_; temp_curve_finished_ = 0; + if (debug_) + printf("tcreset: done\n\n"); } uint8_t tcurve_hasFinished(void) @@ -99,12 +110,35 @@ void tcurve_add(int16_t temp, uint16_t hold_for_ticks) temp_curve_end_ = new_entry; temp_curve_ = new_entry; temp_curve_current_ = new_entry; + temp_curve_restart_pos_ = new_entry; } else { temp_curve_end_->next = new_entry; temp_curve_end_ = new_entry; } } +void tcurve_setRepeatStartPosToLatestEntry(void) +{ + temp_curve_restart_pos_= temp_curve_end_; +} + +void tcurve_printCurve(void) +{ + if (temp_curve_ == 0) + { + printf("{\"cmd_ok\":false,\"error\":\"No curve set\"}\r\n"); + return; + } + printf("{\"curve\":["); + for (tc_entry *ce = temp_curve_; ; ce=ce->next) + { + printf("{\"temp\":%d,\"duration\":%u,\"is_curr\":%d,\"is_loop_start\":%d},",ce->target_temp, ce->hold_for_timeticks, ce == temp_curve_current_,ce == temp_curve_restart_pos_); + if (ce == temp_curve_end_) + break; + } + printf("0],\"end_temp:\":%d}\r\n", post_cycle_target_temp_); +} + int16_t tcurve_getTempToSet(int16_t current_temp, uint16_t ticks_elapsed) { if (temp_curve_current_ == 0) @@ -129,8 +163,8 @@ int16_t tcurve_getTempToSet(int16_t current_temp, uint16_t ticks_elapsed) temp_curve_current_ = temp_curve_current_->next; } else if (curve_num_repeats_) { - //restart temp curve from the beginning - temp_curve_current_ = temp_curve_; + //restart temp curve from the beginning (or the set position) + temp_curve_current_ = temp_curve_restart_pos_; curve_num_repeats_--; } else temp_curve_finished_ = 1; diff --git a/pcr-controller/temp_curve.h b/pcr-controller/temp_curve.h index 86d5000..104ecb3 100644 --- a/pcr-controller/temp_curve.h +++ b/pcr-controller/temp_curve.h @@ -27,6 +27,7 @@ void tcurve_reset(void); void tcurve_setRepeats(uint8_t r); +void tcurve_setRepeatStartPosToLatestEntry(void); uint8_t tcurve_isSet(void); uint8_t tcurve_hasFinished(void); uint16_t tcurve_getTimeElapsed(void); @@ -34,5 +35,6 @@ uint8_t tcurve_getRepeatsLeft(void); void tcurve_setPostCycleTargetTemp(int16_t v); void tcurve_add(int16_t temp, uint16_t hold_for_s); int16_t tcurve_getTempToSet(int16_t current_temp, uint16_t time_elapsed); +void tcurve_printCurve(void); #endif \ No newline at end of file -- cgit v1.2.3