> I'm sure you are going to have lots of fun with this. I suspect you might
> be over simplifying it too.
>
> It's not really that important that you can generate 1 Mhz stepping
> frequencies using the hardware timer on the micro-processor. Nowadays
> that's probably a first or second year college lab assignment -- rather
> trivial really. I'd be more concerned about dealing with synchronizing
> steps between motors or a spindle.
No it's not that important but this is what you get then switching to hardware
timer. It is however always nice with a low jitter square wave output. For
timing of the toggles hardware is superior to software.
> Now use that same approach trying to move three motors to move a tool on a
> curve through 3D space and since each motor will run various speeds through
> this path you will find all sorts of rounding and truncating errors.
Synchronization is not a problem since all three square pulses are generated
within the same device running from the same clock. There will however be a
time lag.
> And I haven't even touched on accelerating/deceerating from a stopped
> position and maintaining synchronization.
accelerating/decelerating is not a problem at all. As is now stepper generate
get the position it should be at the next time and simply generate this number
of pulses.
> John Dammeyer
Attached, this is a first try probably with some bugs and x-axis only. It is
mostly run from it's own clock but adapt to linuxcnc with some averaging.
I could swear it made more noise before but did not record the sound. Now it is
running rather quiet, at least I think for a stepper.
Regards Nicklas Karlsson
// @Author Nicklas SB Karlsson
// © Copyright Nicklas SB Karlsson or GNU GPL as is 2016-04-13 is also OK even though it is not attached here
#include <stdint.h>
extern "C" {
#include "inverter1.h"
#include "stm32f7xx.h"
#include "io.h"
}
extern "C" void DMA2_Stream1_IRQHandler(void);
extern "C" void DMA2_Stream2_IRQHandler(void);
extern "C" void DMA2_Stream6_IRQHandler(void);
static void dmaConfig(DMA_Stream_TypeDef* dmaStream, void* m0ar, volatile void* ccr);
static void stepX(uint32_t t, int32_t dx, const uint32_t dt);
static void stepY(int32_t pulsesSpeed);
static void stepZ(int32_t pulsesSpeed);
#define PULSES_LEN 64
#define PULSES_LEN_BIT_MASK 63
static uint16_t pulsesX[PULSES_LEN];
static uint16_t pulsesY[PULSES_LEN];
static uint16_t pulsesZ[PULSES_LEN];
static positions_type positions;
static int tArrivalsPos = 0; // Last arrival stored here
#define T_ARRIVALS_LEN 32
static uint32_t tArrivals[T_ARRIVALS_LEN]{}; // Arrival times
volatile static unsigned int tPeriods = 0; // Number of timer overflows
volatile static int watchdog = 0; // Count down to zero periodically
extern "C" void TIM1_UP_TIM10_IRQHandler(void);
void inverter1Init(void){
positions.x = 0;
positions.y = 0;
positions.z = 0;
{
int n;
for(n = 0; n < PULSES_LEN; n++){
pulsesX[n] = 0xFFFF;
pulsesY[n] = 0xFFFF;
pulsesZ[n] = 0xFFFF;
}
}
pulsesX[PULSES_LEN/2] = 0x4000;
dmaConfig(DMA2_Stream1, pulsesX, &TIM1->CCR1);
dmaConfig(DMA2_Stream2, pulsesY, &TIM1->CCR2);
dmaConfig(DMA2_Stream6, pulsesZ, &TIM1->CCR3);
/* Timer initialization */
TIM1->CR1 = TIM_CR1_ARPE | TIM_CR1_URS;
TIM1->CR2 = TIM_CR2_MMS_1 | TIM_CR2_MMS_0;
TIM1->SMCR = 0;
TIM1->PSC = 0;
TIM1->ARR = 0X7FFF;
TIM1->RCR = 0;
TIM1->CCR1 = 0xFFFF;
TIM1->CCR2 = 0xFFFF;
TIM1->CCR3 = 0xFFFF;
TIM1->DIER = TIM_DIER_CC3DE | TIM_DIER_CC2DE | TIM_DIER_CC1DE | TIM_DIER_UIE;
TIM1->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_0 |
TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0;
TIM1->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0 |
TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_0;
TIM1->CCER = TIM_CCER_CC3E |
TIM_CCER_CC2E |
TIM_CCER_CC1E;
TIM1->BDTR = TIM_BDTR_MOE | TIM_BDTR_OSSR | TIM_BDTR_OSSI | (TIM_BDTR_DTG & 0x95);
TIM1->CR1 = TIM_CR1_ARPE | TIM_CR1_URS | TIM_CR1_CEN;
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
}
void inverter1SetPosition(positions_type p){
static uint32_t t = 0;
int dt;
{
const uint32_t tNow = (tPeriods << 15) | (TIM1->CNT & 0x7FFF);
tArrivalsPos++;
if(tArrivalsPos >= T_ARRIVALS_LEN){
tArrivalsPos = 0;
}
dt = ((tNow - tArrivals[tArrivalsPos]) & ((1<<23) - 1)) >> 5;
tArrivals[tArrivalsPos] = tNow;
{
static unsigned int valid = 0;
if(watchdog == 0){
t = tNow;
valid = 1;
}
else{
if(valid < 32){
t = tNow;
valid++;
}
else{
t = (t + dt) & ((1<<23) - 1);
}
}
}
watchdog = 10;
stepX(tNow + dt, p.x - positions.x, dt);
}
//stepY(p.y);
//stepZ(p.z);
positions = p;
}
static void stepX(uint32_t t, int32_t dx, const uint32_t dt){
int32_t dt_dx;
static int on = 0;
if(dx < 0){ // Move "backwards"?
dx = -dx; // Use absolute value
IO_PWM11_Dir_Low(); // Set direction to "backwards"
}
else{ // Move forward?
IO_PWM11_Dir_High(); // Set direction to "forward"
}
if(dx == 0){ // Zero pulses?
dt_dx = 0xFFFFFFFF; // Infinite distance
if(on == 0){
IO_PWM11_Stepper_Disable(); // Disable stepper driver
}
else{
on--;
}
}
else{ // At least one pulse ?
on = 100;
dt_dx = dt/dx; // pulse distance in (counts/puls)
IO_PWM11_Stepper_Enable(); // Enable stepper driver
}
{
static unsigned int pos = 0;
int n;
uint32_t tDelta = dt_dx;
for(n = 0; n < PULSES_LEN/2; n++){
pos = (pos + 1) & PULSES_LEN_BIT_MASK; // Next position in circular buffer
if(tDelta > 0x7FFF){ // Overflow ?
tDelta -= 0x8000; // Count down
pulsesX[pos] = 0xFFFF; // Set outside counter range
}
else{ // No overflow => within this period
t = (t + dt_dx);
pulsesX[pos] = t & 0x7FFF; // Set toggle time
tDelta = dt_dx;
}
}
}
}
static void stepY(int32_t pulsesSpeed){
if(pulsesSpeed < 0){
pulsesSpeed = -pulsesSpeed;
IO_PWM12_Dir_Low();
}
else{
IO_PWM12_Dir_High();
}
int32_t u_stored;
if(pulsesSpeed == 0){
u_stored = 0xFFFFFFFF;
IO_PWM12_Stepper_Disable();
}
else{
u_stored = 1000*1000/pulsesSpeed; // pulsesPer in (pulses/s), 1/(pulsesPer/1000)=1000/pulsesPer
IO_PWM12_Stepper_Enable();
}
{
unsigned int pos = (PULSES_LEN - 1 - (DMA2_Stream2->NDTR & PULSES_LEN_BIT_MASK));
int n;
uint16_t t = 0xFFFF;
for(n = 0; n < 10; n++){
t = pulsesY[pos];
if(t != 0xFFFF){
break;
}
else{
pos = (pos - 1) & PULSES_LEN_BIT_MASK;
}
}
uint32_t tDelta = u_stored;
for(n = 0; n < PULSES_LEN/2; n++){
pos = (pos + 1) & PULSES_LEN_BIT_MASK; // Next position in circular buffer
if(tDelta > 0x7FFF){ // Overflow ?
tDelta -= 0x8000; // Count down
pulsesY[pos] = 0xFFFF; // Set outside counter range
}
else{ // No overflow => within this period
t = (t + u_stored);
pulsesY[pos] = t & 0x7FFF; // Set toggle time
tDelta = u_stored;
}
}
}
u_stored = 0;
}
static void stepZ(int32_t pulsesSpeed){
if(pulsesSpeed < 0){
pulsesSpeed = -pulsesSpeed;
IO_PWM13_Dir_Low();
}
else{
IO_PWM13_Dir_High();
}
int32_t u_stored;
if(pulsesSpeed == 0){
u_stored = 0xFFFFFFFF;
IO_PWM13_Stepper_Disable();
}
else{
u_stored = 1000*1000/pulsesSpeed; // pulsesPer in (pulses/s), 1/(pulsesPer/1000)=1000/pulsesPer
IO_PWM13_Stepper_Enable();
}
{
unsigned int pos = (PULSES_LEN - 1 - (DMA2_Stream6->NDTR & PULSES_LEN_BIT_MASK));
int n;
uint16_t t = 0xFFFF;
for(n = 0; n < 10; n++){
t = pulsesZ[pos];
if(t != 0xFFFF){
break;
}
else{
pos = (pos - 1) & PULSES_LEN_BIT_MASK;
}
}
uint32_t tDelta = u_stored;
for(n = 0; n < PULSES_LEN/2; n++){
pos = (pos + 1) & PULSES_LEN_BIT_MASK; // Next position in circular buffer
if(tDelta > 0x7FFF){ // Overflow ?
tDelta -= 0x8000; // Count down
pulsesZ[pos] = 0xFFFF; // Set outside counter range
}
else{ // No overflow => within this period
t = (t + u_stored);
pulsesZ[pos] = t & 0x7FFF; // Set toggle time
tDelta = u_stored;
}
}
}
u_stored = 0;
}
extern "C" void DMA2_Stream1_IRQHandler(void){
if(DMA2->LISR & (DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2)){
#warning ignore errors
}
DMA2->LIFCR = DMA_LIFCR_CTCIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTEIF1 | DMA_LIFCR_CDMEIF1;
}
extern "C" void DMA2_Stream2_IRQHandler(void){
if(DMA2->LISR & (DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2)){
#warning ignore errors
}
DMA2->LIFCR = DMA_LIFCR_CTCIF2 | DMA_LIFCR_CHTIF2 | DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2;
}
extern "C" void DMA2_Stream6_IRQHandler(void){
if(DMA2->HISR & (DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2)){
#warning ignore errors
}
DMA2->HIFCR = DMA_HIFCR_CTCIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6;
}
static void dmaConfig(DMA_Stream_TypeDef* dmaStream, void* m0ar, volatile void* ccr){
const uint32_t cr = DMA_CHANNEL_6 |
DMA_MEMORY_TO_PERIPH |
DMA_PINC_DISABLE |
DMA_MINC_ENABLE |
DMA_PDATAALIGN_HALFWORD |
DMA_MDATAALIGN_HALFWORD |
DMA_NORMAL |
DMA_PRIORITY_LOW |
DMA_FIFOMODE_DISABLE |
DMA_SxCR_CIRC |
DMA_SxCR_TCIE;
dmaStream->CR = 0;
dmaStream->CR = cr;
dmaStream->FCR &= (uint32_t)~(DMA_SxFCR_DMDIS);
dmaStream->M0AR = (uint32_t)m0ar;
dmaStream->NDTR = PULSES_LEN;
dmaStream->PAR = (uint32_t)ccr;
dmaStream->CR |= DMA_SxCR_EN;
}
const positions_type* inverter1GetPosition(void){
return &positions;
}
extern "C" void TIM1_UP_TIM10_IRQHandler(void){
const unsigned int tmp = tPeriods;
TIM1->SR = ~(TIM_SR_UIF);
tPeriods = (tmp + 1) & 0xFF;
if(watchdog > 0){
watchdog--;
}
else{
IO_PWM11_Stepper_Disable();
}
}
------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z
_______________________________________________
Emc-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/emc-users