Firmware for single step driver with fast step (up to 340kHz) and simple gcode interpreter.
Dependencies: FastPWM SimpleIOMacros mbed
main.cpp
- Committer:
- daapp
- Date:
- 2013-03-03
- Revision:
- 2:33a99f65551d
- Parent:
- 1:86997189bb6b
File content as of revision 2:33a99f65551d:
/* picocom -b 115200 -c --imap lfcrlf --omap crlf /dev/ttyACM0 */ /* todo: when command complete it should print "ok" todo: when value of parameter changed print "ok saved" todo: when value of parameter is invalid print "err invalid value" todo: add G command for set zero here */ #include "mbed.h" #include "FastPWM.h" #include "IOMacros.h" #define VERSION "0.1" #define DEBUG #define FILESYSTEM_ROOT "local" #define SETTINGS_FILE ("/" FILESYSTEM_ROOT "/settings.txt") #define SETTINGS_BACKUP_FILE ("/" FILESYSTEM_ROOT "/settings.bak") LocalFileSystem local(FILESYSTEM_ROOT); /* p30 - P0_4 */ // const uint32_t counterPin = 4; const uint32_t count_int_mask = p30_SET_MASK; volatile int32_t position; FastPWM stepper(p21); // Dir pin is p22 - P2.4 /* G code interpreter state*/ bool abs_movement_mode = true; double position_mm = 0.0; // mm double feed_rate = 1.0; // mm/sec^2 /* *** Serial port *** */ #define INPUT_BUFFER_SIZE 40 Serial pc(USBTX, USBRX); char input_buffer[INPUT_BUFFER_SIZE+1]; int input_position = 0; /* *** Settings *** */ typedef struct { double steps_per_mm; double acceleration; // mm/sec^2 double start_speed; // mm/sec^2 } settings_t; settings_t settings; // return true - if parsed succesfully // return false - if error during parsing bool parse_settings(FILE *fp, settings_t *settings) { // todo: write parser return true; } void load_settings(char *filename) { //FILE *fp; #ifdef DEBUG pc.printf("debug load settings from file \"%s\"\n", filename); #endif //fp = fopen(filename, "r"); //if (fp == NULL) { settings.steps_per_mm = 12800.0 / 150.0; // 170.666667; settings.acceleration = 2000.000; // mm/sec^2 settings.start_speed = 100.0; //} else { // parse_settings(fp, &settings); // fclose(fp); //} } void save_settings(char *filename) { FILE *fp; fp = fopen(SETTINGS_FILE, "w"); if (fp != NULL) { fprintf(fp, "$0=%f\n", settings.steps_per_mm); fprintf(fp, "$1=%f\n", settings.acceleration); fprintf(fp, "$2=%f\n", settings.start_speed); fclose(fp); #ifdef DEBUG pc.printf("ok settings saved to %s\n", SETTINGS_FILE); #endif } else { pc.printf("err unable to open %s for writing\n", SETTINGS_FILE); } } void print_settings() { pc.printf("$0=%f (x, steps/mm)\n", settings.steps_per_mm); pc.printf("$1=%f (x accel, mm/sec^2)\n", settings.acceleration); pc.printf("$2=%f (x start acceleration, mm/sec)\n", settings.start_speed); pc.printf("ok\n"); } // return true if no error // return false if error (invalid parameter or value) bool set_param(char name, char *value) { if (name >= '0' && name <= '2') { char *rest = value; double dbl_value; bool error = false; dbl_value = strtod(value, &rest); if (value == rest || *rest != '\0') { pc.printf("err invalid value for command $%c: %s\n", name, value); error = true; } else { #ifdef DEBUG pc.printf("debug $%c=%f -> %s\n", name, dbl_value, rest); #endif switch (name) { case '0': if (dbl_value > 0.0) { settings.steps_per_mm = dbl_value; } else { pc.printf("err value should be > 0.0, but %f\n", dbl_value); error = true; } break; case '1': if (dbl_value > 0.0) { settings.acceleration = dbl_value; } else { pc.printf("err value should be > 0.0, but %f\n", dbl_value); error = true; } break; case '2': if (dbl_value >= 0.0) { settings.start_speed = dbl_value; } else { pc.printf("err value should be >= 0.0, but %f\n", dbl_value); error = true; } break; default: pc.printf("err parameter %c unrecognized\n", name); error = true; break; } } return !error; } else { pc.printf("err invalid parameter %c\n", name); return false; } } void invalid_command() { pc.printf("err invalid command %s\n", input_buffer); } enum MOVE_STATE { STOP, ACCELERATION, MOVE, DECELERATION }; enum MOVE_DIR { CW = 1, CCW = -1 }; volatile double current_freq, max_freq; volatile double delta_freq; volatile uint64_t accel_pulses = 0; volatile uint32_t accel_pulses_left, decel_pulses_left, move_pulses_left; // pulses left until end of movement volatile MOVE_DIR dir; // todo: remove, because useless volatile double last_current_freq; volatile MOVE_STATE last_move_state; volatile MOVE_STATE move_state = STOP; void move_module(double new_position_mm) { uint32_t distance_pulses; LED1_OFF; LED2_OFF; LED3_OFF; new_position_mm = abs_movement_mode ? new_position_mm : position_mm + new_position_mm; distance_pulses = (uint32_t) ceil(fabs(new_position_mm - position_mm) * settings.steps_per_mm); #ifdef DEBUG pc.printf("debug move from %f to %f mm\n", position_mm, new_position_mm); pc.printf("debug move from %f to %f pulses\n", position_mm * settings.steps_per_mm, new_position_mm * settings.steps_per_mm); #endif if (new_position_mm > position_mm) { p22_CLR; // positive dir dir = CW; } else { p22_SET; // negative dir dir = CCW; } // todo: remove //position = 0; double start_freq = settings.start_speed * settings.steps_per_mm ; // = freeRate / 60sec / acc double accTime = feed_rate / 60.0 / settings.acceleration; // = pulse/mm mm/sec max_freq = settings.steps_per_mm * (feed_rate/60.0); if (max_freq < start_freq) { delta_freq = 0.0; } else { delta_freq = (max_freq - start_freq) / (((start_freq + max_freq) / 2.0) * accTime); } accel_pulses = (uint32_t) ceil((max_freq - start_freq) / delta_freq); accel_pulses_left = accel_pulses; decel_pulses_left = accel_pulses; move_pulses_left = distance_pulses - accel_pulses_left - decel_pulses_left; current_freq = start_freq > 1.0 ? start_freq : 1.0; #ifdef DEBUG pc.printf("debug frequency from %f to %f step %f time %f\n", start_freq, max_freq, delta_freq, accTime); pc.printf("debug distance_pulses %u\n", distance_pulses); pc.printf("debug %u + %u + %u\n", accel_pulses_left, move_pulses_left, decel_pulses_left); pc.printf("debug current_freq %f Hz\n", current_freq); #endif LED1_ON; __disable_irq(); move_state = ACCELERATION; stepper.period(1.0/current_freq); //1.0/ (settings.steps_per_mm * 1000)); stepper.write(0.50); __enable_irq(); //position_mm = newposition_mm; } void stop_module() { if (move_state != STOP) { move_state = DECELERATION; #ifdef DEBUG pc.printf("position = %d steps\n", position); pc.printf("debug STOP: %u %u %u %f\n", accel_pulses_left, move_pulses_left, decel_pulses_left,current_freq); pc.printf("debug last_current_freq = %f\tlast_move_state=%d\n", last_current_freq, last_move_state); #endif position_mm = position / settings.steps_per_mm; pc.printf("ok manual stop position %f mm\n", position_mm); } } void update_position() { // position += dir; // __disable_irq(); switch (move_state) { case ACCELERATION: accel_pulses_left--; if (accel_pulses_left > 0) { current_freq += delta_freq; } else { current_freq = max_freq; move_state = MOVE; LED2_ON; } stepper.period(1.0/current_freq); break; case MOVE: move_pulses_left--; if (move_pulses_left > 0) { } else { move_state = DECELERATION; LED3_ON; } break; case DECELERATION: decel_pulses_left--; if (decel_pulses_left > 0) { current_freq -= delta_freq; } else { move_state = STOP; current_freq = 0.0; } if (fabs(current_freq) > 0.0001) { stepper.period(1.0/current_freq); } else { stepper.period(0.0); } last_current_freq = current_freq; last_move_state = move_state; break; case STOP: stepper.period(0.0); stepper.write(0.0); p27_SET; LED1_OFF; LED2_OFF; LED3_OFF; //pc.printf("ok stop position %f mm\n", position_mm); break; } // __enable_irq(); } void process_command(void) { #ifdef DEBUG pc.printf("debug serial buffer <%s>\n", input_buffer); #endif if (input_buffer[0] == '\0') { // todo: empty command stop the stage stop_module(); } else if (input_buffer[0] == '$') { // '$' - print or change settings if (input_buffer[1] == '\0') { print_settings(); } else if (input_buffer[1] >= '0' and input_buffer[1] <='9' and input_buffer[2] == '=') { // todo: save settings to file if (set_param(input_buffer[1], &input_buffer[3])) { save_settings(SETTINGS_FILE); } } else { invalid_command(); } } else if (input_buffer[0] == '?' && input_buffer[1] == '\0') { pc.printf("ok %f\n", position_mm); } else { char *p = input_buffer, *rest = input_buffer; uint32_t ul_value; double dbl_value; bool error = false; bool newabs_movement_mode = abs_movement_mode; double newposition_mm = position_mm; double new_feed_rate = feed_rate; bool move = false; while (*p != '\0') { switch (*p) { case 'G': p++; ul_value = strtoul(p, &rest, 10); if (p == rest) { pc.printf("err invalid value for command G: %s\n", p); error = true; } else { #ifdef DEBUG pc.printf("debug G%u -> %s\n", ul_value, rest); #endif p = rest; switch (ul_value) { case 0: // todo: implement break; case 1: // todo: implement break; case 90: newabs_movement_mode = true; break; case 91: newabs_movement_mode = false; break; default: pc.printf("err invalid value for command G: %u\n", ul_value); error = true; break; } } break; case 'X': p++; dbl_value = strtod(p, &rest); if (p == rest) { pc.printf("err invalid value for command X: %s\n", p); error = true; } else { #ifdef DEBUG pc.printf("debug X%f -> %s\n", dbl_value, rest); #endif p = rest; newposition_mm = dbl_value; move = true; } break; case 'F': p++; dbl_value = strtod(p, &rest); if (p == rest || dbl_value < 0.0) { pc.printf("err invalid value for command F: %s\n", p); error = true; } else { #ifdef DEBUG pc.printf("debug parse F%f => %s\n", dbl_value, rest); #endif p = rest; new_feed_rate = dbl_value; } break; default: pc.printf("err invalid command %s\n", p); error = true; break; } if (error) { break; } } if (!error) { abs_movement_mode = newabs_movement_mode; feed_rate = new_feed_rate; if (move) { move_module(newposition_mm); } } } } void read_char(void) { int ch; #ifdef DEBUG LED4_ON; #endif ch = pc.getc(); if (input_position < INPUT_BUFFER_SIZE) { } else { pc.printf("\nToo long string, should be <= %d characters.\n", INPUT_BUFFER_SIZE); input_position = 0; } if (ch == ' ' || ch == '\t') { // ignore space characters } else { if (ch == '\n') { input_buffer[input_position] = '\0'; process_command(); input_position = 0; input_buffer[input_position] = '\0'; } else { if (ch >= 'a' and ch <= 'z') { ch = 'A' + (ch - 'a'); // convert to upper case } input_buffer[input_position++] = ch; } } #ifdef DEBUG LED4_OFF; #endif } extern "C" void EINT3_IRQHandler (void) __irq { if (LPC_GPIOINT->IntStatus & 0x1) { if (LPC_GPIOINT->IO0IntStatF & count_int_mask) { update_position(); } } LPC_GPIOINT->IO2IntClr = (LPC_GPIOINT->IO2IntStatR | LPC_GPIOINT->IO2IntStatF); LPC_GPIOINT->IO0IntClr = (LPC_GPIOINT->IO0IntStatR | LPC_GPIOINT->IO0IntStatF); } void event_irq_init(void) { p30_AS_INPUT; // p30_MODE(PIN_REPEAT); // Enable p30 is P0_4 for rising edge interrupt generation. LPC_GPIOINT->IO0IntEnF |= count_int_mask; NVIC_SetPriority(EINT3_IRQn, 1); // Enable the interrupt NVIC_EnableIRQ(EINT3_IRQn); } int main() { LED1_USE; // acceleration LED2_USE; // move LED3_USE; // deceleration LED4_USE; // serial port receive p22_AS_OUTPUT; // dir pin p27_AS_OUTPUT; position = 0; pc.baud(115200); pc.attach(read_char); pc.printf("IGNB-SM %s ['$' for help]\n", VERSION); load_settings(SETTINGS_FILE); event_irq_init(); while(1) {} }