![]() | |
| Home Page | Mark Forums Read | Today's Posts | My Replies | Classifieds | Reviews | Photo Gallery | Web Links | Share Files | Advertise With Us | Ad List |
| |||||||
| PIC Programing / Design Discuss programing of PIC chips here and design of electronics using PIC chips. |
![]() |
| | LinkBack | Thread Tools | Search this Thread | Display Modes |
|
#1
| ||||
| ||||
Hi, I've been dreaming for a long time to change some linistepper code just to add a few "features". But, i'm nowhere an expert in PIC assembly. So, i decide to rewrite linistepper to C. Thanks to Roman Black for good code commenting, making me who's bad in assembly can easily rewrite the thing in C ![]() Whats changed: - Move high/low power table from code to EEPROM lookup - Ignore ENABLE pin input. - Use step signal timeout to switch between low and high power. - Store stepping table to EEPROM. - Use inline assembly for pwm loop (maintains 333kHz pwm) - Disable runtime stepping mode change. - Stepping mode is initialized on startup only to improve calculation time between step. - Use quadrature input instead of STEP/DIR Simply put, this version will drop motor current after ~1 second if there is no signal received in that timeframe. Motor current will go to full power when next stepping is received. This 1 second timeout is changeable in source code. This linistepper-c also support quadrature input (Phase-A and Phase-B) From EMC2 manual:
Also, it's possible to detect a single miss steps with quadrature, as it is a form of Gray-code. Btw, the step/dir code is also included in zip, but it's never tested. The bad things: 1) The source code looks "hack"ish. This is due to: - My incompetence - SDCC workaround. 2) The code generated by SDCC is slow. It insert BANKSEL instruction every here and there. I've included hand edited assembly generated by SDCC to remove this unnecessary call, just as an example on how to improve further. 3)It's slower than the original linistepper in asm. With linistepper-asm, there's only ~20us delay before it output a new motors' current value. With SDCC compiled code, there's around ~50us before it output a new motors' current value. By editing the sdcc code and remove unneeded BANKSEL, "thinking" time now drop from 50us to ~30us. 4)It might have bugs. This software is still untested by many, so there's probably a few bugs creeping inside. If you find any, please update it here so we can share the fixes. Thanks in advance. |
|
#2
| |||
| |||
| Very interesting! I recently did a simple conversion of the Lini v2 code from ASM to C although I am not sure if James put it up on the PICList page yet. My conversion was done quite quickly and doesn't have the PWM range so no 18th steps. Why use EEPROM for the tables? Good call on dropping the ability to change step modes when running, nobody uses that feature anyway. I'm not sure about the quadrature input, it should be pretty fast using standard step signal anyway if you are using a 16MHz or 20MHz xtal. Do you have a link to a text file for your source code? Many people don't like downloading a ZIP because of potential viruses etc, and the source code should only be tiny anyway so it doesn't need ZIP compression. |
|
#3
| |||
| |||
| Reason for eeprom: - most pic have it and end up not used anyway. - speed wise, it's similar to reading from array in flash. (i don't see any datasheet about flash vs eeprom data access, so this is my assumption) - the only way to improve speed is to follow just like what you did in assembly, make switch case, then declare the value 1 by 1. - the other "unrelated" reason is to make it tunable. For example, use serial port to change the value, etc (eeprom is writable on runtime). For the quadrature, it's an advantage if : - Slow PC. To make 1200 steps per second, pc need to toggle the 1 single bit for 1200 time in 1 second. With quadrature, it only need to toggle 2 bit, each for 600time/seconds (2x600=1200 step). - Slow PIC (the case of this code that is). If the PC is too fast, miss steps can happen. With quadrature, it can detect 1 miss steps (though it do nothing right now. Should be easy to trigger fault, etc). And because quadrature signal doesn't return zero (NRZ), user doesn't need to mess with stepspace (min time for '1'), steplength (min time for '0'), etc. As for the zip file, it got others like Makefile, hex file, linkers, etc. Code: /**
* LiniStepper.c
*
* For PIC16F628A code
*
* LiniStepper.c Changelog
* - Literal conversion from assembly to C
* - Move high/low power table from code to EEPROM lookup
* - Ignore ENABLE pin input.
* - Use step signal timeout to switch between low and high power.
* - Store stepping table to EEPROM.
* - Use inline assembly for pwm loop
* - Disable runtime stepping mode change.
* - Stepping mode is initialized on startup only to improve calculation time between step.
* - Use quadrature input instead of STEP/DIR
*
* Based on LiniStepper.asm v1.0
* Copyright Aug 2002 Roman Black http://www.romanblack.com
*
* PIC assembler code for the LiniStepper stepper motor driver board.
* 200/400/1200/3600 steps
*
* LiniStepper.asm Changelog
* v1.0 Seems to be working ok for now, few minor things need improving;
* * touch up phase switching for direction -> 0
* * table system is messy, can reduce in size a LOT if needed
* * low-power mode doesn't microstep, only halfstep
* * no easy way to step motor from within PIC softweare
*/
#include "pic16f628a.h"
unsigned int __at 0x2007 __CONFIG = _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _MCLRE_ON & _BODEN_OFF & _LVP_OFF;
// Switch to low power after TIMEOUT x 128milisecond
#define TIMEOUT 10
// Stepping mode
#define MODE_3600 0b0011
#define MODE_1200 0b0010
#define MODE_400 0b0001
#define MODE_200 0b0000
/** Input pinout
* ---x---- RA4 * mode bit1 ( 10=1200 step 11=3600 step
* ----x--- RA3 * mode bit0 00=200 step 01=400 step )
* -----x-- RA2 * power (ignored, use timeout instead)
* ------x- RA1 * direction
* -------x RA0 * step
*/
#define MASK_MODE1 0b00010000
#define MASK_MODE0 0b00001000
#define MASK_POWER 0b00000100
#define MASK_B 0b00000010
#define MASK_A 0b00000001
// Overflow bit for step calculation
#define STEP_OVERFLOW 0b10000000
// Relative Address of high power table from EEPROM
#define ADDR_POWER_HI 0x00
// Relative Address of low power table from EEPROM
#define ADDR_POWER_LO 2 * 36 + ADDR_POWER_HI
// Relative Address of stepping table from EEPROM
#define ADDR_STEPPING 36 + ADDR_POWER_LO
// Column of stepping table as formatted in EEPROM
#define TBL_STEP_MIN 0
#define TBL_STEP_MAX 1
#define TBL_STEP_INC 2
/**
* Correct sequence for reading the EEPROM is:
* @ Set address
* @ Set RD bit
* @ Read value from EEDATA
*
* This expression does exactly that, first setting EEADR and RD
* before returning the value of EEDATA.
*
* Source: http://burningsmell.org/pic16f628/src/0009-eeprom.c
*/
#define EEPROM_READ(ADDR) (EEADR=ADDR,RD=1,EEDATA)
/**
* NOTE: This variable must be store in BANK 0 for pwm_loop to works.
* SDCC 3.0 crash with internal compiler error when absolute addressing is used.
* Workaround: use linker trick.
* See section "UDL_linistepper_0" in 16f628a.lkr to define RAM address
*/
__data static unsigned char inputs; // store new input pins
__data static unsigned char inputs_last; // store last states of input pins
__data static unsigned char current1; // for current tween pwm
__data static unsigned char current2; // for current tween pwm
__data static unsigned char phase; // stores the 4 motor phase pins
volatile __data static unsigned char timer_count; // count timer1 overflow for larger delay
// NOTE: declared volatile due to sdcc optimizer
// can't track value changed from inline assembly
__data static unsigned char step; // (0-71) ustep position
__data static unsigned char steptemp; // for step calcs
__data static unsigned char mode; // store stepping mode
__data static unsigned char eeprom_addr; // for eeprom table lookup (max addressable=0xff)
__data static unsigned char step_max; // max step for selected mode
__data static unsigned char step_min; // min step for selected mode
__data static unsigned char step_inc; // step increment for selected mode
__data static unsigned char phase_a; // for calcs
__data static unsigned char phase_b; // for calcs
void handler(void) __interrupt 0 {
// put additional interrupt code here...
}
/**
* pwm() the fast pwm loop
*
* the 2 target currents were set in the move_motor code.
*
* what this function does is spend 2 time units at current2,
* and 1 time unit at current1.
* actual is 8 clocks at current2
* and 4 clocks at current 1
* total 12 cycles, so 333 kHz with 16MHz resonator.
*
* this gives an average pwm current of 2/3 the way between
* current2 and current1.
*
* the routine is kept short to keep pwm frequency high, so it
* is easy to smooth in hardware by the ramping caps.
*
* IMPORTANT! is timed by clock cycles, don't change this code!
* it also checks for any change in input pins here
*
* the modified code here was originally supplied by Eric Bohlman (thanks!)
*
* Code below is the equivalent pwm_loop in C (without timeout count).
* (SDCC 3.0) : ~24 cycles for current2,
* ~12 cycles for current1
*
* do {
* PORTB = current2; // send current2 to motor
* inputs = PORTA; // read input
* if((inputs_last ^ inputs) & ~MASK_POWER) { // break loop except power pin change
* break;
* }
* PORTB = current1; // send current1 to motor
* } while (!TMR1IF); // loop unless input timeout
*
*/
static void pwm(void) {
timer_count = TIMEOUT; // reload delay counter
TMR1H = 0; // reset timer value
TMR1L = 0;
/* T1CON
; x------- ; bit7 unimplemented
; -x------ ; bit6 unimplemented
; --x----- ; bit5 TMR1 prescale (10=1:4, 11=1:8)
; ---x---- ; bit4 TMR1 prescale (00=1:1, 01=1:2)
; ----x--- ; bit3 timer 1 osc (0=osc on, 1=osc off)
; -----x-- ; bit2 tmr1 sync (ignored when tmr1 source=0)
; ------x- ; bit1 tmr1 source (0=internal, 1=external)
; -------x ; bit0 tmr1 on (1=on, 0=off) */
T1CON=0b00110001; // start timer 1 with prescaler 1:8
__asm // NOTE:make sure we're already in BANK0 before start
pwm_loop: // main entry!
// better to enter at current2 for motor power.
movf _current2,w // output current2 to motor
movwf PORTB // send to motor!
nop // (8 cycle for current2)
btfsc PIR1,0 // check for timer1 overflow
goto pwm_timeout
// now test input pins
movf PORTA,w // get pin values from port
xorwf _inputs_last,w // xor to compare new inputs with last values
andlw b'00000011' // only check for step change (MASK_A | MASK_B)
skpz
goto pwm_final // not zero, one or more input pins have changed!
// else, output current1 to motor
movf _current1,w // get currents and phase switching
movwf PORTB // send to motor! (4 cycles for current1)
goto pwm_loop // keep looping
pwm_final:
xorwf _inputs_last,w
movwf _inputs // restore xored value back to the orig inputs value
goto pwm_exit
pwm_timeout: // count how many time timer1 have overflow
bcf PIR1,0 // clear timer1 overflow flag
decfsz _timer_count,f // loop until timer_count reach zero
goto pwm_loop
pwm_exit:
__endasm;
if(!timer_count) {
inputs &= ~MASK_POWER; // drop to low power
} else {
inputs |= MASK_POWER; // turn on high power
}
TMR1ON = 0; // stop the timer
}
/**
* new_inputs() input change was detected
*
* when we enter here:
* - inputs have just changed
* - inputs_last contains last PORTA inputs values
* - inputs contains new PORTA inputs values
*
* Quadrature is simply 2 bit gray code
* Only 1 bit is changed for every step
* Forward sequence = AB:00->01->11->10->00->01...
*/
static void new_inputs(void) {
phase_a = (inputs & MASK_A); // phase_a=1 if A=1, else 0
phase_b = (inputs & MASK_B) >> 1; // phase_b=1 if B=1, else 0
if((inputs ^ inputs_last) & MASK_A) { // Check if A changed from previous
if((inputs ^ inputs_last) & MASK_B) { // A changed from previous. Check for B
// invalid state or missing step 00->11, 11->00, 01->10, 10->01
// B should not change in single step
// TODO: trigger fault/emergency stop, etc
} else if(phase_b == phase_a) {
step += step_inc; // forward AB:10->00 or 01->11
} else {
step -= step_inc; // reverse AB:00->10 or 11->01
}
} else {
if((inputs ^ inputs_last) & MASK_A) { // B changed from previous, Check for A
// invalid state or missing step 00->11, 11->00, 01->10, 10->01
// A should not change in single step
// TODO: trigger fault/emergency stop, etc
} else if(phase_b == phase_a) {
step -= step_inc; // reverse AB:10->11 or 01->00
} else {
step += step_inc; // forward AB:11->10 or 00->01
}
}
if(step & STEP_OVERFLOW) { // test for roll under <0
step = step_max; // rolled under so force to max step
} else if(step > step_max) { // test for roll over >max step
step = step_min; // rolled over so force to step minimum
}
inputs_last = inputs; // save a copy of the inputs
}
/**
* move_motor() sets 8 output pins to control motor
*
* this code controls the phase sequencing and current
* settings for the motor.
*
* there are always 72 steps (0-71)
*
* we can split the main table into 2 halves, each have identical
* current sequencing. That is only 12 entries for hardware current.
*
* Then can x3 the table to get 36 table entries which cover all 72 steps.
* the 36 entries jump to 36 code pieces, which set the current values
* for the 2 possible tween steps... We need 2 current values, one
* for the x2 value and one for the x1 value.
*
* PHASE SEQUENCING (switch the 4 coils)
*
* there are 4 possible combinations for the phase switching:
* each have 18 steps, total 72 steps:
*
* A+ B+ range 0 step 0-17
* A- B+ range 1 18-35
* A- B- range 2 36-53
* A+ B- range 3 54-71
*
*
*/
static void move_motor(void) {
steptemp = step; // find which of the 4 ranges we are in
if(steptemp - 35 > 0) { // step is between 36-71
steptemp -= 36;
if(steptemp - 17 > 0) { // step is in range 3
phase = 0b00000110; // 0110 = A+ B-
} else { // step is in range 2
phase = 0b00001010; // 1010 = A- B-
}
} else { // step is between 0-35
if(steptemp - 17 > 0) { // step is in range 1
phase = 0b00001001; // 1001 = A- B+
} else { // step is in range 0
phase = 0b00000101; // 0101 = A+ B+
}
}
/*-------------------------------------------------
; at this point we have the phasing done and stored as the last
; 4 bits in var phase; 0000xxxx
; now we have 36 possible current combinations, which we can do
; by reading from EEPROM.
; as we have 2 power modes; full and low power, we need 2 tables.
-------------------------------------------------*/
if(inputs & MASK_POWER) { // high power selected
eeprom_addr = (steptemp + steptemp) + ADDR_POWER_HI;
current2 = phase | EEPROM_READ(eeprom_addr);
current1 = phase | EEPROM_READ(eeprom_addr + 1);
} else { // low power is selected
eeprom_addr = steptemp + ADDR_POWER_LO;
current2 = phase | EEPROM_READ(eeprom_addr);
current1 = current2;
}
}
/**
* setup() sets port directions and interrupt stuff etc,
*/
static void setup(void) {
/* OPTION_REG
; x------- ; 7, 0=enable, 1=disable, portb pullups
; -x------ ; 6, 1=/, int edge select bit
; --x----- ; 5, timer0 source, 0=internal clock, 1=ext pin.
; ---x---- ; 4, timer0 ext edge, 1=\
; ----x--- ; 3, prescaler assign, 1=wdt, 0=timer0
; -----x-- ; 2,1,0, timer0 prescaler rate select
; ------x- ; 000=2, 001=4, 010=8, 011=16, etc.
; -------x ; */
OPTION_REG = 0b10000010;
TRISA = 0b00011111; // all 5 port A pins are inputs (1=input, 0=output)
TRISB = 0; // all 8 port B are outputs (1=input, 0=output)
VRCON = 0; // disable Vref
/* INTCON
; x------- ; bit7 GIE global int enable, 1=enabled
; -x------ ; bit6 EE write complete enable, 1=en
; --x----- ; bit5 TMR0 overflow int enable, 1=en
; ---x---- ; bit4 RB0/INT enable, 1=en
; ----x--- ; bit3 RB port change int enable, 1=en
; -----x-- ; bit2 TMR0 int flag bit, 1=did overflow and get int
; ------x- ; bit1 RB0/INT flag bit, 1=did get int
; -------x ; bit0 RB port int flag bit, 1=did get int */
INTCON=0;
PIE1 = 0; // disable pi etc
T1CON = 0; // disable timer1
T2CON = 0; // disable timer2
CCP1CON = 0; // disable CCP module
CMCON = 0b00000111; // disable comparators
PORTB = 0; // clear PORTB
PORTA = 0; // clear PORTA
step = 0; // reset step value
inputs_last = 0;
inputs = PORTA; // get initial value for inputs
mode = (inputs & (MASK_MODE1 | MASK_MODE0)) >> 3; // find which of the 4 modes we are in
eeprom_addr = (mode + mode + mode) + ADDR_STEPPING; // each mode store 3 value: min,max,increment
step_inc = EEPROM_READ(eeprom_addr + TBL_STEP_INC);
step_min = EEPROM_READ(eeprom_addr + TBL_STEP_MIN);
step_max = EEPROM_READ(eeprom_addr + TBL_STEP_MAX);
}
/**
* Main
*/
void main(void) {
setup(); // do initial setup for ports and ints and stuff
while(1) {
move_motor(); // will set the motor current based on step value
pwm(); // move the motor and loop until input changed
new_inputs(); // read new inputs and set the step value
}
} Code: list p=16f628a include "p16f628a.inc" org 0x2100 ; change this to eeprom address of your processor ;------------------------------------------------- ; HIGH POWER TABLE ;------------------------------------------------- ; here are the 36 value pairs for the high power table. ; ; CURRENT INFO. ; hardware requires that we send the entire 8 bits to the motor ; at one time, to keep pwm fast. ; ----xxxx, where xxxx is the coils on/off phasing (done) ; xxxx----, where xxxx is the current settings for the A and B phases; ; xx------, where xx is current for A phase ; --xx----, where xx is current for B phase ; hardware currents for 6th stepping have 4 possible values; ; 00 = 0% current ; 01 = 25% current ; 10 = 55% current ; 11 = 100% current ; ;------------------------------------------------- ; PWM INFO. ; hardware gives us 6th steps, or 1200 steps/rev. ; to get 3600 steps/rev we need TWO more ; "tween" steps between every proper hardware 6th step. ; to do this we set 2 currents, current1 and current2. ; then we do FAST pwm, with 2 time units at current2, ; and 1 time unit at current1. ; this gives a current which is between the two currents, ; proportionally closer to current2. (2/3 obviously) ; this gives the ability to get 2 evenly spaced "tween" currents ; between our hardware 6th step currents, and go from 1200 to 3600. ; the next 36 value pairs set the 2 currents desired, then ; loaded to a fast-pwm loop (same loop used for all currents) ; which modulates between the 2 currents and gives final ; output current. ;------------------------------------------------- ; High Power Table current: current2 : current1 de b'11000000',b'11000000' ; 00: (6th step), current: 100,000 : 100,000 de b'11000000',b'11010000' ; 01: (tween step), current: 100,000 : 100,025 de b'11010000',b'11000000' ; 02: (tween step), current: 100,025 : 100,000 de b'11010000',b'11010000' ; 03: (6th step), current; 100,025 : 100,025 de b'11010000',b'11100000' ; 04: (tween step), current: 100,025 : 100,055 de b'11100000',b'11010000' ; 05: (tween step), current: 100,055 : 100,025 de b'11100000',b'11100000' ; 06: (6th step), current; 100,055 : 100,055 de b'11100000',b'11110000' ; 07: (tween step), current: 100,055 : 100,100 de b'11110000',b'11100000' ; 08: (tween step), current: 100,100 : 100,055 de b'11110000',b'11110000' ; 09: (6th step), current; 100,100 : 100,100 de b'11110000',b'10110000' ; 10: (tween step), current: 100,100 : 055,100 de b'10110000',b'11110000' ; 11: (tween step), current: 055,100 : 100,100 de b'10110000',b'10110000' ; 12: (6th step), current; 055,100 : 055,100 de b'10110000',b'01110000' ; 13: (tween step), current; 055,100 : 025,100 de b'01110000',b'10110000' ; 14: (tween step), current; 025,100 : 055,100 de b'01110000',b'01110000' ; 15: (6th step), current; 025,100 : 025,100 de b'01110000',b'00110000' ; 16: (tween step), current; 025,100 : 000,100 de b'00110000',b'01110000' ; 17: (tween step), current; 000,100 : 025,100 de b'00110000',b'00110000' ; 18: (6th step), current; 000,100 : 000,100 de b'00110000',b'01110000' ; 19: (tween step), current; 000,100 : 025,100 de b'01110000',b'00110000' ; 20: (tween step), current; 025,100 : 000,100 de b'01110000',b'01110000' ; 21: (6th step), current; 025,100 : 025,100 de b'01110000',b'10110000' ; 22: (tween step), current; 025,100 : 055,100 de b'10110000',b'01110000' ; 23: (tween step), current; 055,100 : 025,100 de b'10110000',b'10110000' ; 24: (6th step), current; 055,100 : 055,100 de b'10110000',b'11110000' ; 25: (tween step), current; 055,100 : 100,100 de b'11110000',b'10110000' ; 26: (tween step), current; 100,100 : 055,100 de b'11110000',b'11110000' ; 27: (6th step), current; 100,100 : 100,100 de b'11110000',b'11100000' ; 28: (tween step), current; 100,100 : 100,055 de b'11100000',b'11110000' ; 29: (tween step), current; 100,055 : 100,100 de b'11100000',b'11100000' ; 30: (6th step), current; 100,055 : 100,055 de b'11100000',b'11010000' ; 31: (tween step), current; 100,055 : 100,025 de b'11010000',b'11100000' ; 32: (tween step), current; 100,025 : 100,055 de b'11010000',b'11010000' ; 33: (6th step), current; 100,025 : 100,025 de b'11010000',b'11000000' ; 34: (tween step), current; 100,025 : 100,000 de b'11000000',b'11010000' ; 35: (tween step), current; 100,000 : 100,025 ;------------------------------------------------- ; LOW POWER TABLE ;------------------------------------------------- ; as low power mode is for wait periods we don't need to ; maintain the full step precision and can wait on the ; half-step (400 steps/rev). This means much easier code tables. ; The nature of the board electronics is not really suited ; for LOW power microstepping, but it could be programmed here ; if needed. ; ; NOTE!! uses my hi-torque half stepping, not normal half step. ; ; doing half stepping with the 55,25 current values gives; ; 55+25 = 80 ; max current 100+100 = 200 ; typical (high) current 100+50 = 150 ; so low power is about 1/2 the current of high power mode, ; giving about 1/4 the motor heating and half the driver heating. ; for now it uses only half-steps or 8 separate current modes. ; we only have to use 4 actual current modes as ; the table is doubled like the table_highpower is. ; ; NOTE!! This table is truncated to single value due to out of space in EEPROM ; Expand to value pairs if you want to use full size table. ;------------------------------------------------- ; current : current1 = current2 de b'10010000' ; 0-8: current: 55.25 de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'01100000' ; 9-17: current 25.55 de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' ; 18-26: current 25.55 de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'01100000' de b'10010000' ; 19-26: current 55.25 de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' de b'10010000' ;------------------------------------------------- ; STEPPING TABLE ;------------------------------------------------- ; This table store stepping value for various mode ; Mode 00 : 200 steps (72/18),4 valid step: 9,27,45,63 ; Mode 01 : 400 steps (72/9),8 valid step: 4,13,22,31,40,49,58,67 ; Mode 10 : 1200 steps (72/3), valid step is mod 3 (0,3,6,9 etc) ; Mode 11 : 3600 steps (72/1), step increment by 1 ;------------------------------------------------- ; step min, step max, step increment de d'9',d'63',d'18' de d'4',d'67',d'9' de d'0',d'69',d'3' de d'0',d'71',d'1' end |
|
#4
| |||
| |||
| I've found 1 bug, the power keep toggling between high and low when idle, and this might cause the stepper to rotate. The fix is simple, don't exit pwm_loop when timeout if we're already in low power mode. Code: pwm_loop: // main entry!
// better to enter at current2 for motor power.
movf _current2,w // output current2 to motor
movwf PORTB // send to motor!
nop // (8 cycle for current2)
btfsc PIR1,0 // check for timer1 overflow
goto pwm_timeout
// now test input pins
movf PORTA,w // get pin values from port
xorwf _inputs_last,w // xor to compare new inputs with last values
andlw b'00000011' // only check for step change (MASK_A | MASK_B)
skpz
goto pwm_final // not zero, one or more input pins have changed!
// else, output current1 to motor
movf _current1,w // get currents and phase switching
movwf PORTB // send to motor! (4 cycles for current1)
goto pwm_loop // keep looping
pwm_final:
xorwf _inputs_last,w
movwf _inputs // restore xored value back to the orig inputs value
goto pwm_exit
pwm_timeout: // count how many time timer1 have overflow
bcf PIR1,0 // clear timer1 overflow flag
decfsz _timer_count,f // loop until timer_count reach zero
goto pwm_loop
btfss _inputs,2 // continue loop, already in low power
goto pwm_loop
pwm_exit:
__endasm; |
|
#5
| |||
| |||
Being able to write different tables to the eeprom is a great idea. Might I suggest that it could be synergised with the ability to write the number of usteps as an eeprom value too, so a user could load in a 10 ustep value and a table of 10th usteps, instead of the standard 18 ustep and table of 18ths? Just an idea.If the SDCC compiler can't load eeprom data from a text file and you are using MPASM anyway for the programming of the PIC, could you include the eeprom table as a text file etc and just get MPASM to load it into the PIC? However that could cause the project to be more complex and people really like simplicity, one of the benefits of the .ASM version is they only need 1 file to have the whole project. Anyway I appreciate and respect the large amount of work you have done, would you like it added to the Linistepper open-source pages on the Massmind (PIClist) website? I'm sure if you email James there he would put your code up and/or include a link to your page (as you prefer). You should also add yor name into the source code as the author of that C version. How committed are you to the SDCC compiler? I prefer the MikroC PRO compiler for PIC 16F and 18F, it is free for small projects (under 2k ROM) which will do all your PC16F628A work. Actually there is a limit on the RAM as well as the ROM in the free version but it still does 16F628 pretty good if I remember. The MikroC one is quite powerful. ![]() And, is that your web page at Burningsmell.org? If so, here's a for making such an impressive site! |
| Sponsored Links |
|
#6
| |||
| |||
| Yes, it's possible to specify arbitrary table, though one might need to modify hard-coded definition of table address. Or better yet, moved it and store in eeprom (ie: start address for high power, end address of high power table, etc). Currently, linistepper-c only capable to use custom step size,max step, etc. But, for the time being i think the capability of PIC16F628A is already fully utilized so one might need to move on to higher end of microcontroller to add more features. One of the reason being is, the pwm loop already operate on tight clock cycle. There's simply no way for interrupt to happen without disturbing the "averaging" current circuit. So, one either need to : - remove the pwm loop and operate with limit of 1200 steps, or - offload pwm to hardware. The simplest circuit is probably using quad 2:1 multiplexer (something like 74hc157). This idea need to use 9 bit output from microcontroller. 4 bit store current1, 4bit store current2. The single 1 bit is pwm with 33% duty cycle at freq 333kHz, connected to multiplexer select bit. This multiplexer then will do the bit flipping (4 bit current1 and 4 bit current2) based on pwm cycle. As everything is done in hardware, there should be lot of clock cycle available to waste The downside is it need microcontroller with lot of i/o, more than what pic16f628a currently provides.The other reason is again, lack of i/o. There's no more i/o available for communication. It would be nice if linistepper could be use for wider range of hardware. Eg: able use it from usb,serial, etc. Or, maybe there's someone creative enough to make close loop stepper... As for sdcc, i use it because it's already installed on my pc. After struggling a while with sdcc, I do tried to use free cc5x, but the code quality is just as good as sdcc (if not worse). In the end, i just use sdcc and try to live with it ![]() The good thing with sdcc is it's processor neutral (or atleast that what i think it's supposed to be). So, it should be easy if ones decide to upgrade to PIC18Fxxx, or perhaps other kind of processor (ie: 8051, etc). Of course, the register and config is changed but the syntax should stay (i hope...). Either way, i think i just stay with sdcc for a time being. Besides, it's still on "experimental status" with pic16fxxx. The pic18fxxx series should generate better code quality. As for MikroC PRO, that's the first time i heard about it. Sure, by looking screenshot from website it's a powerful tools. But it is a bit pricy for hobby things. Right now, i only use kwrite (similar to notepad/wordpad in windows), command console to compile and simulator from oshonsoft (trial 40 times). Should be enough for now, as i just started with this pic thing. As for the burningsmell website, no it's not from me. Just a random website from google as i search for guide. And as i do copy his macro, i should to give him proper credits ![]() Oh, for the author name, i should put proper legal license too (MIT license). And as this is a derivative works, I'll also include your name in the copyright. This is MIT license template: Code: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Finally, feel free for anyone to mirror the works. I would be happy if more people interested and use this code. Also, i've attached a "cleaned" version. With proper copyright and license. Please use this file for mirrors and distribution. Thanks. Oh, i also included linistepper-stepdir.hex in those zipfile. If there's no bug, it should work just like linistepper asm version, except the motor will be cooler. But that can only happen if the motors has lot of idle in between operation. Last edited by hazimin; 01-23-2011 at 11:46 AM. |
|
#10
| ||||
| ||||
| Nice work! I've added both versions to the modifications page at: piclist.com/techref/io/stepper/linistep/mods.htm And I will post a quick note on the facebook group. Thanks for sharing! |
| Sponsored Links |
![]() |
| Tags |
| linistepper |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | Search this Thread |
| Display Modes | |
| |
Similar Threads | ||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Linistepper new board | salvado | Open Source Controller Boards | 24 | 03-06-2012 05:12 PM |
| My linistepper-a true GEM | student1616 | Open Source Controller Boards | 0 | 12-02-2008 10:59 AM |
| FYI Linistepper Experience | flyboy1015 | DIY-CNC Router Table Machines | 6 | 11-12-2008 03:38 PM |
| 'Lil Linistepper Lathe | epineh | Mini Lathe | 37 | 02-24-2008 08:03 AM |
| Linistepper | tante | Open Source Controller Boards | 7 | 12-19-2005 09:24 AM |