Jump to content
43oh

terjeio

Members
  • Content Count

    145
  • Joined

  • Last visited

  • Days Won

    27

Reputation Activity

  1. Like
    terjeio got a reaction from greeeg in PCB Laser Exposer/Printer   
    Here are the files for my PCB Exposer/Printer, it is the complete package including mechanical design files.
     

    The printer itself.
     

    Example - a power control PCB for Raspberry Pi - 40 x 40 mm.
     
    Code includes driver for MCP4725 DAC, buffered serial port driver, stepper motor control and command parsing for the MSP430G2553 used as the main controller.
     
    Code and design files:
     
    PCB Exposer - controller code for MSP430G2553.zip
    PCB Exposer - desktop application.zip
    PCB Exposer - mechanical design files in Vectric format.zip
    PCB Exposer - schematics and PCBs.zip
     
    Desktop application is coded in C#, schematics and PCBs in KiCad format.
     
    There is some more information to be found in this tread:
    http://forum.43oh.com/topic/4990-what-are-you-doing-right-now/page-5
     
    Terje
     
     
  2. Like
    terjeio got a reaction from colotron in PCB Laser Exposer/Printer   
    @@colotron The machine has an inherent resolution of 1200 dpi (~0.02mm) so I am able to work with 0.2mm tracks - however I use 0.25mm as default for most designs. The PCB outline (fom Edge.Cuts i KiCad) in the example board above is ~0.1mm. For consistent results I think presensitized boards will be the best choice. I am using Riston which is not always easy to attach perfectly - sometimes I have to touch up some tracks due to dust under the film causing breaks. So I think the practical limit is not what the machine is capable of but rather the properties of the emulsion used.
     
    Etching is definitely a lot better than what I could achieve with toner transfer, and soldering is easier but could be due to the fact that I now add a solder mask which I also expose in the printer. The solder mask is cured at 140 deg. C for about 1 hour. I have found that it is important to vigorously rinse the mask in running water after development to reduce/avoid oxidation during curing. I have also tried tinning solution, when fresh it gives a nice shiny finish that is easy to solder, but it does not store well...
     
    All in all I find it a lot easier to make my own boards now - no more frustration from toner transfer failures and it is great to be able to add a solder mask.
     
    You will find more photos and details in the referred tread.
     
    Terje
  3. Like
    terjeio got a reaction from agaelema in PCB Laser Exposer/Printer   
    Here are the files for my PCB Exposer/Printer, it is the complete package including mechanical design files.
     

    The printer itself.
     

    Example - a power control PCB for Raspberry Pi - 40 x 40 mm.
     
    Code includes driver for MCP4725 DAC, buffered serial port driver, stepper motor control and command parsing for the MSP430G2553 used as the main controller.
     
    Code and design files:
     
    PCB Exposer - controller code for MSP430G2553.zip
    PCB Exposer - desktop application.zip
    PCB Exposer - mechanical design files in Vectric format.zip
    PCB Exposer - schematics and PCBs.zip
     
    Desktop application is coded in C#, schematics and PCBs in KiCad format.
     
    There is some more information to be found in this tread:
    http://forum.43oh.com/topic/4990-what-are-you-doing-right-now/page-5
     
    Terje
     
     
  4. Like
    terjeio got a reaction from roadrunner84 in PCB Laser Exposer/Printer   
    Here are the files for my PCB Exposer/Printer, it is the complete package including mechanical design files.
     

    The printer itself.
     

    Example - a power control PCB for Raspberry Pi - 40 x 40 mm.
     
    Code includes driver for MCP4725 DAC, buffered serial port driver, stepper motor control and command parsing for the MSP430G2553 used as the main controller.
     
    Code and design files:
     
    PCB Exposer - controller code for MSP430G2553.zip
    PCB Exposer - desktop application.zip
    PCB Exposer - mechanical design files in Vectric format.zip
    PCB Exposer - schematics and PCBs.zip
     
    Desktop application is coded in C#, schematics and PCBs in KiCad format.
     
    There is some more information to be found in this tread:
    http://forum.43oh.com/topic/4990-what-are-you-doing-right-now/page-5
     
    Terje
     
     
  5. Like
    terjeio got a reaction from zeke in PCB Laser Exposer/Printer   
    Here are the files for my PCB Exposer/Printer, it is the complete package including mechanical design files.
     

    The printer itself.
     

    Example - a power control PCB for Raspberry Pi - 40 x 40 mm.
     
    Code includes driver for MCP4725 DAC, buffered serial port driver, stepper motor control and command parsing for the MSP430G2553 used as the main controller.
     
    Code and design files:
     
    PCB Exposer - controller code for MSP430G2553.zip
    PCB Exposer - desktop application.zip
    PCB Exposer - mechanical design files in Vectric format.zip
    PCB Exposer - schematics and PCBs.zip
     
    Desktop application is coded in C#, schematics and PCBs in KiCad format.
     
    There is some more information to be found in this tread:
    http://forum.43oh.com/topic/4990-what-are-you-doing-right-now/page-5
     
    Terje
     
     
  6. Like
    terjeio got a reaction from jazz in PCB Laser Exposer/Printer   
    Here are the files for my PCB Exposer/Printer, it is the complete package including mechanical design files.
     

    The printer itself.
     

    Example - a power control PCB for Raspberry Pi - 40 x 40 mm.
     
    Code includes driver for MCP4725 DAC, buffered serial port driver, stepper motor control and command parsing for the MSP430G2553 used as the main controller.
     
    Code and design files:
     
    PCB Exposer - controller code for MSP430G2553.zip
    PCB Exposer - desktop application.zip
    PCB Exposer - mechanical design files in Vectric format.zip
    PCB Exposer - schematics and PCBs.zip
     
    Desktop application is coded in C#, schematics and PCBs in KiCad format.
     
    There is some more information to be found in this tread:
    http://forum.43oh.com/topic/4990-what-are-you-doing-right-now/page-5
     
    Terje
     
     
  7. Like
    terjeio got a reaction from timotet in PCB Laser Exposer/Printer   
    Here are the files for my PCB Exposer/Printer, it is the complete package including mechanical design files.
     

    The printer itself.
     

    Example - a power control PCB for Raspberry Pi - 40 x 40 mm.
     
    Code includes driver for MCP4725 DAC, buffered serial port driver, stepper motor control and command parsing for the MSP430G2553 used as the main controller.
     
    Code and design files:
     
    PCB Exposer - controller code for MSP430G2553.zip
    PCB Exposer - desktop application.zip
    PCB Exposer - mechanical design files in Vectric format.zip
    PCB Exposer - schematics and PCBs.zip
     
    Desktop application is coded in C#, schematics and PCBs in KiCad format.
     
    There is some more information to be found in this tread:
    http://forum.43oh.com/topic/4990-what-are-you-doing-right-now/page-5
     
    Terje
     
     
  8. Like
    terjeio got a reaction from bluehash in Algorithm based stepper motor ramping for Tiva C   
    I have started the process of porting my CO2-laser driver from MSP430 to Tiva C and I am switching to an algorithm based approach for the stepper acceleration/deceleration. As part of testing I have (re)written an implementation of David Austins algorithm as documented here:
     
    http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
     
    The test code may be of use for others so here it is.
     
    PA7: direction
    PF1: step pulse
     
    From what I can glean from my scope the interrupt handler executes in about 2us - so can be used for high pulse rates.
    // // Real-time stepper motor ramp control for TIVA C // // Based on article & code by David Austin // // Terje Io, 2016-03-08 // #include <stdint.h> #include <stdbool.h> #include "inc/hw_gpio.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "driverlib/sysctl.h" #include "driverlib/pin_map.h" #include "driverlib/gpio.h" #include "driverlib/pwm.h" #define STATE_IDLE 0 #define STATE_ACCEL 1 #define STATE_RUN 2 #define STATE_DECEL 3 #define STEPPULSE 50 #define PWMINT PWM_INT_CNT_AD | PWM_INT_CNT_BD typedef struct motor { uint8_t busy; // state machine state int16_t position; // absolute step number uint16_t move; // total steps to move uint16_t midpt; // midpoint of move bool odd; // true if odd number of steps int8_t pos_inc; // position increment uint16_t delay; // integer delay count uint16_t first_delay; // integer delay count uint16_t min_delay; // integer delay count uint16_t step_no; // progress of move uint16_t step_down; // start of down-ramp uint32_t c32; // 24.8 fixed point delay count int16_t denom; // 4.n+1 in ramp algo } motor; struct motor motor1; void pwm_1_2_int_handler (void); void motor_init (motor *motor) { motor->position = 0; motor->busy = STATE_IDLE; motor->pos_inc = 0; motor->first_delay = 30000; motor->min_delay = 1400; } bool motor_calc (motor *motor, int16_t pos_new) { if (pos_new < motor->position) { // get direction & #steps motor->move = motor->position - pos_new; motor->pos_inc = -1; } else if (pos_new != motor->position) { motor->move = pos_new - motor->position; motor->pos_inc = 1; } else return false; // already there motor->odd = motor->move & 0x0001; motor->midpt = (motor->move - (motor->odd ? 1 : 0)) >> 1; motor->step_no = 0; // step counter motor->delay = motor->first_delay; motor->c32 = ((uint32_t)motor->delay) << 8; // keep delay in 24.8 fixed-point format for ramp calcs motor->denom = 1; // 4.n + 1, n = 0 return motor->busy = motor->move == 1 ? STATE_DECEL : STATE_ACCEL; } void motor1_run (int16_t pos_new) { if(motor_calc(&motor1, pos_new)) { GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, motor1.pos_inc > 0 ? GPIO_PIN_7 : 0); PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); PWMGenEnable(PWM1_BASE, PWM_GEN_2); } } void mpu_init (void) { SysCtlPWMClockSet(SYSCTL_PWMDIV_8); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); SysCtlDelay(26); // wait for modules to start GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7); GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); // PWMClockSet(PWM1_BASE, PWM_SYSCLK_DIV_64); GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1); GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); GPIOPinConfigure(GPIO_PF1_M1PWM5); PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_GEN_SYNC_LOCAL); PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true); PWMGenIntRegister(PWM1_BASE, PWM_GEN_2, pwm_1_2_int_handler); PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); PWMGenIntTrigEnable(PWM1_BASE, PWM_GEN_2, PWMINT); PWMIntEnable(PWM1_BASE, PWM_INT_GEN_2); } void main(void) { SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); motor_init(&motor1); mpu_init(); while (true) { // repeat 5 revs forward & back motor1_run(8000); // 1.8 deg/step and 8 microsteps while (motor1.busy); motor1_run(0); while (motor1.busy); } } void pwm_1_2_int_handler (void) { PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); motor1.position += motor1.pos_inc; motor1.step_no++; switch (motor1.busy) { case STATE_ACCEL: if (motor1.step_no == motor1.midpt) { motor1.busy = motor1.odd ? STATE_RUN : STATE_DECEL; motor1.step_down = motor1.step_no + 1; motor1.denom -= 2; } else { motor1.denom += 4; motor1.c32 -= (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 + 128) >> 8; // round 24.8 format -> int16 if (motor1.delay <= motor1.min_delay) { // go to constant speed? motor1.denom -= 6; motor1.busy = STATE_RUN; motor1.step_down = motor1.move - motor1.step_no; motor1.delay = motor1.min_delay; } PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; case STATE_RUN: if (motor1.step_no == motor1.step_down) motor1.busy = STATE_DECEL; break; case STATE_DECEL: if (motor1.denom < 2) // done? motor1.busy = STATE_IDLE; else { motor1.c32 += (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 - 128) >> 8; // round 24.8 format -> int16 motor1.denom -= 4; PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; } // switch (busy) if(!motor1.busy) PWMGenDisable(PWM1_BASE, PWM_GEN_2); }
  9. Like
    terjeio got a reaction from PTB in Algorithm based stepper motor ramping for Tiva C   
    I have started the process of porting my CO2-laser driver from MSP430 to Tiva C and I am switching to an algorithm based approach for the stepper acceleration/deceleration. As part of testing I have (re)written an implementation of David Austins algorithm as documented here:
     
    http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
     
    The test code may be of use for others so here it is.
     
    PA7: direction
    PF1: step pulse
     
    From what I can glean from my scope the interrupt handler executes in about 2us - so can be used for high pulse rates.
    // // Real-time stepper motor ramp control for TIVA C // // Based on article & code by David Austin // // Terje Io, 2016-03-08 // #include <stdint.h> #include <stdbool.h> #include "inc/hw_gpio.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "driverlib/sysctl.h" #include "driverlib/pin_map.h" #include "driverlib/gpio.h" #include "driverlib/pwm.h" #define STATE_IDLE 0 #define STATE_ACCEL 1 #define STATE_RUN 2 #define STATE_DECEL 3 #define STEPPULSE 50 #define PWMINT PWM_INT_CNT_AD | PWM_INT_CNT_BD typedef struct motor { uint8_t busy; // state machine state int16_t position; // absolute step number uint16_t move; // total steps to move uint16_t midpt; // midpoint of move bool odd; // true if odd number of steps int8_t pos_inc; // position increment uint16_t delay; // integer delay count uint16_t first_delay; // integer delay count uint16_t min_delay; // integer delay count uint16_t step_no; // progress of move uint16_t step_down; // start of down-ramp uint32_t c32; // 24.8 fixed point delay count int16_t denom; // 4.n+1 in ramp algo } motor; struct motor motor1; void pwm_1_2_int_handler (void); void motor_init (motor *motor) { motor->position = 0; motor->busy = STATE_IDLE; motor->pos_inc = 0; motor->first_delay = 30000; motor->min_delay = 1400; } bool motor_calc (motor *motor, int16_t pos_new) { if (pos_new < motor->position) { // get direction & #steps motor->move = motor->position - pos_new; motor->pos_inc = -1; } else if (pos_new != motor->position) { motor->move = pos_new - motor->position; motor->pos_inc = 1; } else return false; // already there motor->odd = motor->move & 0x0001; motor->midpt = (motor->move - (motor->odd ? 1 : 0)) >> 1; motor->step_no = 0; // step counter motor->delay = motor->first_delay; motor->c32 = ((uint32_t)motor->delay) << 8; // keep delay in 24.8 fixed-point format for ramp calcs motor->denom = 1; // 4.n + 1, n = 0 return motor->busy = motor->move == 1 ? STATE_DECEL : STATE_ACCEL; } void motor1_run (int16_t pos_new) { if(motor_calc(&motor1, pos_new)) { GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, motor1.pos_inc > 0 ? GPIO_PIN_7 : 0); PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); PWMGenEnable(PWM1_BASE, PWM_GEN_2); } } void mpu_init (void) { SysCtlPWMClockSet(SYSCTL_PWMDIV_8); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); SysCtlDelay(26); // wait for modules to start GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7); GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); // PWMClockSet(PWM1_BASE, PWM_SYSCLK_DIV_64); GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1); GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); GPIOPinConfigure(GPIO_PF1_M1PWM5); PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_GEN_SYNC_LOCAL); PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true); PWMGenIntRegister(PWM1_BASE, PWM_GEN_2, pwm_1_2_int_handler); PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); PWMGenIntTrigEnable(PWM1_BASE, PWM_GEN_2, PWMINT); PWMIntEnable(PWM1_BASE, PWM_INT_GEN_2); } void main(void) { SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); motor_init(&motor1); mpu_init(); while (true) { // repeat 5 revs forward & back motor1_run(8000); // 1.8 deg/step and 8 microsteps while (motor1.busy); motor1_run(0); while (motor1.busy); } } void pwm_1_2_int_handler (void) { PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); motor1.position += motor1.pos_inc; motor1.step_no++; switch (motor1.busy) { case STATE_ACCEL: if (motor1.step_no == motor1.midpt) { motor1.busy = motor1.odd ? STATE_RUN : STATE_DECEL; motor1.step_down = motor1.step_no + 1; motor1.denom -= 2; } else { motor1.denom += 4; motor1.c32 -= (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 + 128) >> 8; // round 24.8 format -> int16 if (motor1.delay <= motor1.min_delay) { // go to constant speed? motor1.denom -= 6; motor1.busy = STATE_RUN; motor1.step_down = motor1.move - motor1.step_no; motor1.delay = motor1.min_delay; } PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; case STATE_RUN: if (motor1.step_no == motor1.step_down) motor1.busy = STATE_DECEL; break; case STATE_DECEL: if (motor1.denom < 2) // done? motor1.busy = STATE_IDLE; else { motor1.c32 += (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 - 128) >> 8; // round 24.8 format -> int16 motor1.denom -= 4; PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; } // switch (busy) if(!motor1.busy) PWMGenDisable(PWM1_BASE, PWM_GEN_2); }
  10. Like
    terjeio got a reaction from zeke in Algorithm based stepper motor ramping for Tiva C   
    I have started the process of porting my CO2-laser driver from MSP430 to Tiva C and I am switching to an algorithm based approach for the stepper acceleration/deceleration. As part of testing I have (re)written an implementation of David Austins algorithm as documented here:
     
    http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
     
    The test code may be of use for others so here it is.
     
    PA7: direction
    PF1: step pulse
     
    From what I can glean from my scope the interrupt handler executes in about 2us - so can be used for high pulse rates.
    // // Real-time stepper motor ramp control for TIVA C // // Based on article & code by David Austin // // Terje Io, 2016-03-08 // #include <stdint.h> #include <stdbool.h> #include "inc/hw_gpio.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "driverlib/sysctl.h" #include "driverlib/pin_map.h" #include "driverlib/gpio.h" #include "driverlib/pwm.h" #define STATE_IDLE 0 #define STATE_ACCEL 1 #define STATE_RUN 2 #define STATE_DECEL 3 #define STEPPULSE 50 #define PWMINT PWM_INT_CNT_AD | PWM_INT_CNT_BD typedef struct motor { uint8_t busy; // state machine state int16_t position; // absolute step number uint16_t move; // total steps to move uint16_t midpt; // midpoint of move bool odd; // true if odd number of steps int8_t pos_inc; // position increment uint16_t delay; // integer delay count uint16_t first_delay; // integer delay count uint16_t min_delay; // integer delay count uint16_t step_no; // progress of move uint16_t step_down; // start of down-ramp uint32_t c32; // 24.8 fixed point delay count int16_t denom; // 4.n+1 in ramp algo } motor; struct motor motor1; void pwm_1_2_int_handler (void); void motor_init (motor *motor) { motor->position = 0; motor->busy = STATE_IDLE; motor->pos_inc = 0; motor->first_delay = 30000; motor->min_delay = 1400; } bool motor_calc (motor *motor, int16_t pos_new) { if (pos_new < motor->position) { // get direction & #steps motor->move = motor->position - pos_new; motor->pos_inc = -1; } else if (pos_new != motor->position) { motor->move = pos_new - motor->position; motor->pos_inc = 1; } else return false; // already there motor->odd = motor->move & 0x0001; motor->midpt = (motor->move - (motor->odd ? 1 : 0)) >> 1; motor->step_no = 0; // step counter motor->delay = motor->first_delay; motor->c32 = ((uint32_t)motor->delay) << 8; // keep delay in 24.8 fixed-point format for ramp calcs motor->denom = 1; // 4.n + 1, n = 0 return motor->busy = motor->move == 1 ? STATE_DECEL : STATE_ACCEL; } void motor1_run (int16_t pos_new) { if(motor_calc(&motor1, pos_new)) { GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, motor1.pos_inc > 0 ? GPIO_PIN_7 : 0); PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); PWMGenEnable(PWM1_BASE, PWM_GEN_2); } } void mpu_init (void) { SysCtlPWMClockSet(SYSCTL_PWMDIV_8); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); SysCtlDelay(26); // wait for modules to start GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7); GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); // PWMClockSet(PWM1_BASE, PWM_SYSCLK_DIV_64); GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1); GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); GPIOPinConfigure(GPIO_PF1_M1PWM5); PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_GEN_SYNC_LOCAL); PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true); PWMGenIntRegister(PWM1_BASE, PWM_GEN_2, pwm_1_2_int_handler); PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); PWMGenIntTrigEnable(PWM1_BASE, PWM_GEN_2, PWMINT); PWMIntEnable(PWM1_BASE, PWM_INT_GEN_2); } void main(void) { SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); motor_init(&motor1); mpu_init(); while (true) { // repeat 5 revs forward & back motor1_run(8000); // 1.8 deg/step and 8 microsteps while (motor1.busy); motor1_run(0); while (motor1.busy); } } void pwm_1_2_int_handler (void) { PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); motor1.position += motor1.pos_inc; motor1.step_no++; switch (motor1.busy) { case STATE_ACCEL: if (motor1.step_no == motor1.midpt) { motor1.busy = motor1.odd ? STATE_RUN : STATE_DECEL; motor1.step_down = motor1.step_no + 1; motor1.denom -= 2; } else { motor1.denom += 4; motor1.c32 -= (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 + 128) >> 8; // round 24.8 format -> int16 if (motor1.delay <= motor1.min_delay) { // go to constant speed? motor1.denom -= 6; motor1.busy = STATE_RUN; motor1.step_down = motor1.move - motor1.step_no; motor1.delay = motor1.min_delay; } PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; case STATE_RUN: if (motor1.step_no == motor1.step_down) motor1.busy = STATE_DECEL; break; case STATE_DECEL: if (motor1.denom < 2) // done? motor1.busy = STATE_IDLE; else { motor1.c32 += (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 - 128) >> 8; // round 24.8 format -> int16 motor1.denom -= 4; PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; } // switch (busy) if(!motor1.busy) PWMGenDisable(PWM1_BASE, PWM_GEN_2); }
  11. Like
    terjeio got a reaction from Fmilburn in Algorithm based stepper motor ramping for Tiva C   
    I have started the process of porting my CO2-laser driver from MSP430 to Tiva C and I am switching to an algorithm based approach for the stepper acceleration/deceleration. As part of testing I have (re)written an implementation of David Austins algorithm as documented here:
     
    http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
     
    The test code may be of use for others so here it is.
     
    PA7: direction
    PF1: step pulse
     
    From what I can glean from my scope the interrupt handler executes in about 2us - so can be used for high pulse rates.
    // // Real-time stepper motor ramp control for TIVA C // // Based on article & code by David Austin // // Terje Io, 2016-03-08 // #include <stdint.h> #include <stdbool.h> #include "inc/hw_gpio.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "driverlib/sysctl.h" #include "driverlib/pin_map.h" #include "driverlib/gpio.h" #include "driverlib/pwm.h" #define STATE_IDLE 0 #define STATE_ACCEL 1 #define STATE_RUN 2 #define STATE_DECEL 3 #define STEPPULSE 50 #define PWMINT PWM_INT_CNT_AD | PWM_INT_CNT_BD typedef struct motor { uint8_t busy; // state machine state int16_t position; // absolute step number uint16_t move; // total steps to move uint16_t midpt; // midpoint of move bool odd; // true if odd number of steps int8_t pos_inc; // position increment uint16_t delay; // integer delay count uint16_t first_delay; // integer delay count uint16_t min_delay; // integer delay count uint16_t step_no; // progress of move uint16_t step_down; // start of down-ramp uint32_t c32; // 24.8 fixed point delay count int16_t denom; // 4.n+1 in ramp algo } motor; struct motor motor1; void pwm_1_2_int_handler (void); void motor_init (motor *motor) { motor->position = 0; motor->busy = STATE_IDLE; motor->pos_inc = 0; motor->first_delay = 30000; motor->min_delay = 1400; } bool motor_calc (motor *motor, int16_t pos_new) { if (pos_new < motor->position) { // get direction & #steps motor->move = motor->position - pos_new; motor->pos_inc = -1; } else if (pos_new != motor->position) { motor->move = pos_new - motor->position; motor->pos_inc = 1; } else return false; // already there motor->odd = motor->move & 0x0001; motor->midpt = (motor->move - (motor->odd ? 1 : 0)) >> 1; motor->step_no = 0; // step counter motor->delay = motor->first_delay; motor->c32 = ((uint32_t)motor->delay) << 8; // keep delay in 24.8 fixed-point format for ramp calcs motor->denom = 1; // 4.n + 1, n = 0 return motor->busy = motor->move == 1 ? STATE_DECEL : STATE_ACCEL; } void motor1_run (int16_t pos_new) { if(motor_calc(&motor1, pos_new)) { GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, motor1.pos_inc > 0 ? GPIO_PIN_7 : 0); PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); PWMGenEnable(PWM1_BASE, PWM_GEN_2); } } void mpu_init (void) { SysCtlPWMClockSet(SYSCTL_PWMDIV_8); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); SysCtlDelay(26); // wait for modules to start GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7); GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); // PWMClockSet(PWM1_BASE, PWM_SYSCLK_DIV_64); GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1); GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD); GPIOPinConfigure(GPIO_PF1_M1PWM5); PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_GEN_SYNC_LOCAL); PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true); PWMGenIntRegister(PWM1_BASE, PWM_GEN_2, pwm_1_2_int_handler); PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); PWMGenIntTrigEnable(PWM1_BASE, PWM_GEN_2, PWMINT); PWMIntEnable(PWM1_BASE, PWM_INT_GEN_2); } void main(void) { SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); motor_init(&motor1); mpu_init(); while (true) { // repeat 5 revs forward & back motor1_run(8000); // 1.8 deg/step and 8 microsteps while (motor1.busy); motor1_run(0); while (motor1.busy); } } void pwm_1_2_int_handler (void) { PWMGenIntClear(PWM1_BASE, PWM_GEN_2, PWMINT); motor1.position += motor1.pos_inc; motor1.step_no++; switch (motor1.busy) { case STATE_ACCEL: if (motor1.step_no == motor1.midpt) { motor1.busy = motor1.odd ? STATE_RUN : STATE_DECEL; motor1.step_down = motor1.step_no + 1; motor1.denom -= 2; } else { motor1.denom += 4; motor1.c32 -= (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 + 128) >> 8; // round 24.8 format -> int16 if (motor1.delay <= motor1.min_delay) { // go to constant speed? motor1.denom -= 6; motor1.busy = STATE_RUN; motor1.step_down = motor1.move - motor1.step_no; motor1.delay = motor1.min_delay; } PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; case STATE_RUN: if (motor1.step_no == motor1.step_down) motor1.busy = STATE_DECEL; break; case STATE_DECEL: if (motor1.denom < 2) // done? motor1.busy = STATE_IDLE; else { motor1.c32 += (motor1.c32 << 1) / motor1.denom; // ramp algorithm motor1.delay = (motor1.c32 - 128) >> 8; // round 24.8 format -> int16 motor1.denom -= 4; PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, motor1.delay); PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, STEPPULSE); } break; } // switch (busy) if(!motor1.busy) PWMGenDisable(PWM1_BASE, PWM_GEN_2); }
  12. Like
    terjeio reacted to Fmilburn in AD9850 Frequency Generator BoosterPack   
    This project uses an inexpensive AD9850 board obtained off eBay to create a frequency generator boosterpack that is especially useful with the MSP-EXP430FR6989 LaunchPad. It features an encoder with a pushbutton to adjust frequency. In the photo below it is generating a 7000 Hz signal which agrees nicely with the oscilloscope.

    The schematic is simple:

    The encoder is a Bourns 652-PEC12R-4225F-S24. I tried various arrangements and value of resistors and capacitors for debouncing the encoder and switch. In the end I just added some capacitance as shown in the schematic. On the oscilloscope it looked pretty clean so I called it good enough. All the parts are through hole as seen in the photograph below:

    The fit is tight but fortunately all the materials were on hand when the PCB was being designed so I printed out a paper PCB outline and checked it before ordering.

    If I were making it again I would increase the size of the board a bit to allow larger/additional labels. Otherwise, everything works and it turned out fine. This is a picture of the unpopulated boards I received from Osh Park.

    While it should work with most LaunchPads I had the FR6989 in mind as I developed it - in particular I wanted to use the integrated LCD display. Accordingly, I was careful not to obscure the display or the reset button. The software was developed with Energia V17. Here is a photo of what it looks like in operation:

    The output from the AD9850 comes out the jumpers attached to the 90 degree male header pins marked with arrow A. The dark jumper wire is GND and the sine wave is coming out of the lighter wire on the right. Adjustments are made with the encoder knob marked by arrow B. Turning it adjusts the frequency up and down and the current value is output on the LCD as shown by the arrow C. The range is from zero to 32 MHz. Since fine adjustment is desired, as well as rapid adjustment, the push button on the encoder is used to change the coarseness of the adjustment. The magnitude is indicated by the battery indicator on the LCD as shown by arrow D. For example, in the photo three battery bars are showing which indicates that each adjustment/click of the encoder will change the output by 1000 Hz. When the encoder is pushed down it will cycle through in this case to 100 Hz adjustment (with two bars showing), 10 Hz (one bar), 1 Hz (no bars), and then up to 1 MHz (six bars), and so on. In practice, it adjusts pretty quickly with good resolution.
     
    One enhancement might be to have the software scroll the output value since as is it won't display full resolution due to the 6 digit screen. The Energia sketch can be found here.
     
    I have an extra PCB so if you are active in the 43oh forums and would like it then send me a personal message and I will mail it gratis.
  13. Like
    terjeio got a reaction from bluehash in Multimedia Center   
    Finally got around to write a touch driver, it supports XPT2046 (and thus implicitly ADS7843?). I am not entirely happy with it as I had to slow down the sampling rate a lot to get consistent results, may be due to the display itself (noise?). Calibration code is by Carlos E. Vidales and routine for finding the median by Nicolas Devillard. I have modified my UI library to work with this as well, it requires a modified version of RobGs driver for Tiva C.
     

     

     
     
  14. Like
    terjeio got a reaction from bluehash in Multimedia Center   
    Code for the demo project attached, lcd driver, touch driver, UI library and a simple drawing program.
     
    UIDemo.zip
  15. Like
    terjeio got a reaction from Fmilburn in Multimedia Center   
    Code for the demo project attached, lcd driver, touch driver, UI library and a simple drawing program.
     
    UIDemo.zip
  16. Like
    terjeio got a reaction from Fmilburn in Multimedia Center   
    @@Fmilburn - the driver is for lcd screens with a XPT2046 or ADS7843 resistive touch screen controller. The touch sensors I made myself is still not working properly, too sensitive and seems to be a tad temperature dependent as well. For these I am using  Atmel AT42QT1011 & 12 touch sensor ICs, I guess I have to try different values for the sample capacitor. Since the prototype is in daily use and is working well I will do the testing when I make the next one - this because I have to completely dismantle the assembly to get access to the caps.
     
    @@chicken - not easy to add caps to thd lcd screen pcb, since I have only tested my driver with one (chinese) display it may be a bad one?
     
    If anybody want to try the code I can post it here - it is basically a simple drawing program, after calibration one can draw on the screen with one of a few different colors selected by pressing a button on the screen.
     

     
    Touch sensors in top of picture, milled from acrylic. The wire is soldered to a small metal plate in a pocket in the clear acrylic.
    Maybe I should have placed the touch sense caps on the other side of the PCB for easier access?
  17. Like
    terjeio got a reaction from Fmilburn in Multimedia Center   
    Finally got around to write a touch driver, it supports XPT2046 (and thus implicitly ADS7843?). I am not entirely happy with it as I had to slow down the sampling rate a lot to get consistent results, may be due to the display itself (noise?). Calibration code is by Carlos E. Vidales and routine for finding the median by Nicolas Devillard. I have modified my UI library to work with this as well, it requires a modified version of RobGs driver for Tiva C.
     

     

     
     
  18. Like
    terjeio reacted to Fmilburn in Getting Started with Printed Circuit Board (PCB) Design   
    This is the first PCB that I have designed and sent off to be manufactured.  Yesterday I received the boards, soldered them up, and they work!

    This write-up outlines the process I used in the hope that it will be useful to other hobbyists and builders.  There are links at the end which provide additional detail. 
     
    Selecting a Project
    The project consists of a small board with a MSP430G2553 microcontroller and an nRF24L01 radio.  I started with a radio attached with jumpers to a LaunchPad quite some time back and then built one on a proto-board.  The photograph below shows a G2553 with radio powered by a buck-boost converter attached to a super capacitor and solar panel.  I used it for a while with my weather station which never was quite completed.

    Although I could have started with that, I actually chose to start with something simpler.  The goal was to focus on the PCB design process and to minimize the issues associated with a new or technically challenging project.  The objectives, strategies, and constraints I decided on included the following:
    Inexpensive






  19. Like
    terjeio got a reaction from Fred in What are you doing right now..?   
    Experimenting with stencil cutting in my CO2 Laser. KiCad -> HPGL -> G-code so using vector cutting, can adjust cutout size for beam width. I am using PPI-mode (Pulses Per Inch) and adjustable pulse width in order to deliver relatively constant energy to the cut. PPI-mode is implemented by two CPLDs for counting stepper pulses and a 2553 for handling calculations and pulsing the laser. This kind of circuitry may be retrofitted to chinese lasers that only uses simple on/off switching resulting in uneven cutting in the corners (in vector mode).
     

  20. Like
    terjeio got a reaction from greeeg in What are you doing right now..?   
    Experimenting with stencil cutting in my CO2 Laser. KiCad -> HPGL -> G-code so using vector cutting, can adjust cutout size for beam width. I am using PPI-mode (Pulses Per Inch) and adjustable pulse width in order to deliver relatively constant energy to the cut. PPI-mode is implemented by two CPLDs for counting stepper pulses and a 2553 for handling calculations and pulsing the laser. This kind of circuitry may be retrofitted to chinese lasers that only uses simple on/off switching resulting in uneven cutting in the corners (in vector mode).
     

  21. Like
    terjeio got a reaction from greeeg in What are you doing right now..?   
    @@timotet - nice result from isolation milling.
     
    The first machine I made is a fairly standard CNC mill, ballscrews, linear rails & steppers, using Mach3 and Vectric Cut2D & VCarve in the toolchain. The design was inspired by Neo7CNCs machines. This is my main workhorse and vital for my projects. It is mainly used for milling aluminium parts and drilling/milling PCBs.
     

     
    The PCB printer was inspired by a youtube video "PCB Laserprinter Version 2" by "Hobby Elektronik" - he has not published any design details so I had to start from the information provided in the video.
     
    The reason for doing this was that toner transfer did not work for me, and I think isolation milling not well suited for SMD components (IMO). I could have used my main mill by adding a laser to it - but it has a fairly heavy gantry/Z-assembly so not well suited for high speed laser "printing".
     
    The "printer" is designed for PCBs up to Eurocard size (100 x 160 mm) and has a moving table mounted on a THK KR33A actuator. The laser sledge is from an old 3.5" disc drive - I thought I needed to control the focus so I choose that because the assemby contains a small stepper. This is not needed in practice - once the focus it set I have found there is no need to adjust it.
     
    I am using GT2 belt for the X-axis, combined with 17 teeth pulleys and microstepping gives it a mechanical resolution of 1196 dpi - very close to the standard 1200 dpi.
     
    The software renders bmp images directly (no conversion to G-code) so it is "pixel perfect". The PCB outline on the board above is only 5 pixels wide - a tad over 0.1mm, not bad I think. It is my first attempt at double sided printing - this was not my initial design goal so I need to add some way of achieving layer alignment. Since the table has well defined inside edges I think I can utilize that. What I will try is to mill the board slightly oversized so it aligns perfectly with the home position of the laser when I turn it. A complicating factor is that I have to accelerate the laser to max speed before I start exposing the PCB - this to ensure I do not overexpose the board on the edges.
     

     

     

  22. Like
    terjeio got a reaction from Fmilburn in What are you doing right now..?   
    Experimenting with stencil cutting in my CO2 Laser. KiCad -> HPGL -> G-code so using vector cutting, can adjust cutout size for beam width. I am using PPI-mode (Pulses Per Inch) and adjustable pulse width in order to deliver relatively constant energy to the cut. PPI-mode is implemented by two CPLDs for counting stepper pulses and a 2553 for handling calculations and pulsing the laser. This kind of circuitry may be retrofitted to chinese lasers that only uses simple on/off switching resulting in uneven cutting in the corners (in vector mode).
     

  23. Like
    terjeio reacted to greeeg in ledClock - dual colour led matrix   
    Yes, I have never tried to run mine that fast yet.
     
    I've done more work on the code. Not worrying about code size. ( It is around 7kb, basically maxing out the poor G2452. )

     
     
    And the forward voltage diode drop voltages for anyone interested.

  24. Like
    terjeio reacted to Fred in ledClock - dual colour led matrix   
    The masking tape sorts out the melted edges on the mylar. I've not tried kapton myself, but I believe it should cut well.
     
    The slightly misshaped holes are entirely down to the fact that I'm cutting small rectangles at very high speed. Asking the head to turn a fast sharp 90 degree corner means there's some overshoot and vibration. You'd never really notice if the shape wasn't so small.
     
    Raster engraving might solve this completely. I've not looked into this as it's certainly good enough for my current needs. LQFP and QFN worked fine last night, despite using crusty old paste that I thinned out with flux and stirred with a screwdriver! I'll try taking some photos.
  25. Like
    terjeio got a reaction from tripwire in Timer works only with CCS debugger   
    @@JohnDough What is your ACLK source and the delayCycles value? Is the LED partly lit?
×
×
  • Create New...