Como operar uma fresadora CNC sem chamar atenção ...

Meu roteador CNC funcionou fielmente por dois anos, mas algo deu errado, o firmware disparou e foi o pica-pau 0.9.



A princípio, eu só queria fazer o upload novamente e, para isso, peguei os códigos-fonte do Projeto CNC Grbl. Mas a curiosidade venceu e mergulhei no estudo dessas fontes ...



elas constroem um muito simples e lógico, mas o que o russo não gosta de dirigir rápido é possível passar pela possibilidade de um sapato melhorar! Com base no que aconteceu, esta curta postagem de domingo.



Na verdade, a ideia de um controlador para uma máquina CNC é bastante simples e interessante. Existem vários threads de processamento - um lê os dados (gcode) e os analisa, o segundo transforma os comandos em blocos de execução e o terceiro (stepper) realmente executa esses blocos. Este terceiro fluxo será discutido.



O passo-a-passo lida com uma lista de comandos individuais na forma - tomar (X, Y, Z) etapas para todos os três (pelo menos) motores de passo, e por um tempo especificado e em uma direção especificada (bem, isso é tão simplista). Devo dizer que um motor de passo com seu driver é uma coisa bastante simples de controlar - você define (0 ou 1) a direção de rotação e, em seguida, o motor tenta dar um passo por uma queda de entrada positiva (0 -> 1) (e geralmente há 200 passos por revolução). Os dados já estão preparados, então você só precisa correlacionar de alguma forma os 3 inteiros com o tempo determinado.



No original, o autor usava o controlador atmega328p, mas praticamente inalterado tudo é facilmente transferido para o braço (por exemplo, stm32). Mas o próprio algoritmo não pode deixar de levantar questões.



Por um lado, um algoritmo de Bresenham muito perfeito é usado, ou melhor, sua versão de Adaptive Multi-Axis Step-Smoothing. Mas, por outro lado, de alguma forma tudo é complicado e, o mais importante, a suavidade do motor de passo e a precisão do roteador dependem diretamente da precisão dos sinais de controle. Nesse caso, isso se deve à frequência com que o temporizador opera e ao tempo de processamento da interrupção - e isso não dá mais do que 40-50 kHz na melhor das hipóteses, e geralmente até menos - bem, ou seja, a precisão do ajuste do controle é de 20-50 microssegundos.



Mas é bastante óbvio que, de fato, ao processar um comando do buffer, simplesmente precisamos calcular os momentos dos sinais de comutação na porta de saída e esses momentos e realizar a comutação.



Como eu estava pensando em mudar para o cortex-m (bem, mais precisamente, para stm32h750, que adoro muito e que se tornou muito mais barato), tal tarefa pode ser resolvida sem envolver a CPU, usando apenas dois canais DMA e um contador de 32 bits.



A ideia é muito simples. Deixe um canal gravar novos dados na porta em estouro do contador, e o segundo canal grava um novo valor máximo do contador (é razoável fazer isso no primeiro ciclo de clock do contador). Então, para processar o comando da lista, você precisa se preparar para uma matriz de valores de alteração para a porta e os tempos limites entre eles.



Vai acabar sendo algo assim.



Tratamento de interrupções - mudar para um novo buffer (buffer duplo).



#define MAX_PGM 32
typedef struct _pgm_buffer {
        uint32_t data[MAX_PGM];
        uint32_t delta[MAX_PGM];
} pgm_buffer;
pgm_buffer buf[2];
uint32_t current_buf = 1;
uint32_t flags = 0;
void program_down(DMA_HandleTypeDef *_hdma) {
        TIM2->CR1 &= ~TIM_CR1_CEN;
        if ((flags & BUF_RUNNING) == 0)
                return;
        current_buf ^= 1;
        DMA1_Channel5->CCR &= ~1;
        DMA1_Channel2->CCR &= ~1;
        DMA1_Channel5->CNDTR = MAX_PGM;
        DMA1_Channel2->CNDTR = MAX_PGM;
        DMA1_Channel5->CMAR = (uint32_t) (buf[current_buf].delta);
        DMA1_Channel2->CMAR = (uint32_t) (buf[current_buf].data);
        DMA1_Channel5->CCR |= 1;
        DMA1_Channel2->CCR |= 1;
        TIM2->CNT = 0;
        TIM2->ARR = 8;
        TIM2->EGR |= TIM_EGR_UG;
        TIM2->CR1 |= TIM_CR1_CEN;
}


Você pode iniciar desta forma:



       HAL_DMA_RegisterCallback(&hdma_tim2_up, HAL_DMA_XFER_CPLT_CB_ID,
                        program_down);
        HAL_DMA_Start_IT(&hdma_tim2_up, buf, &GPIOA->BSRR, MAX_PGM);
        DMA1_Channel5->CCR &= ~1;
        DMA1_Channel5->CPAR = &TIM2->ARR;
        DMA1_Channel5->CCR |= 1;
        TIM2->CCR1 = 1;
        TIM2->DIER |= TIM_DIER_UDE | TIM_DIER_CC1DE;
        flags |= BUF_RUNNING;


Bem, o começo é:



        program_down(NULL);


O que isso faz? Vamos calcular usando o exemplo do mesmo stm32h750. O temporizador (TIM2) opera ali na frequência de 200 MHz, a latência mínima é de dois ciclos de clock, mas o DMA não pode enviar dados a mais de 50 MHz, ou seja, entre dois comandos de troca de porta, pode-se colocar (levando em consideração a possível ocupação do barramento) 40 nseg (25 MHz) - isto é 1000 vezes melhor do que a implementação original!



Por outro lado, a largura da porta é de 16 bits, de modo que você pode controlar simultaneamente 8 motores de passo em vez de 3, você ainda saberia o porquê ...



Neste caso, o preenchimento dos dados em si não causa problemas (com tal e tal resolução!) - interpolação linear simples para cada motor separadamente com combinando (para otimização) eventos próximos a 40 nseg.



As conclusões reais.



Na oficina há uma máquina CNC acabada medindo 1,2 metros por 0,8 metros com motores e drivers, mas sem controlador. Parece que precisamos terminar o trabalho e ver como será épico. Se o fizer, definitivamente escreverei uma sequência. Nesse ínterim, não entendo por que os controladores estão fazendo isso no atmega e rangem em todas as impressoras 3D e roteadores cnc nessas interrupções bruscas ...



E, claro, provavelmente tendo o poder do Cortex-M7, você pode implementar um controle de trajetória mais suave com todas as restrições , mas esse é um artigo completamente diferente.



PS Aparentemente, é necessário dar algum exemplo hipotético de por que é tão importante ter um tempo tão curto.



Digamos que a máquina precise se mover 100 mm em X e 11 mm em Y, e o software dividiu tudo em seções de aceleração e movimento uniforme - há muitas seções de 100 etapas em 11 etapas e são percorridas na velocidade máxima, correspondendo a 10 kHz. Bem, 10 passos em Y se encaixam exatamente em 100 passos em X, mas problemas podem acontecer no 11º - pode ser pulado, pois causa uma duplicação da frequência. Com isso, o movimento será de 100 mm em X e de 10 a 11 mm em Y. E isso com movimento linear, onde, de fato, até as restrições de acelerações e velocidades permitidas são simples. E se for em ziguezague? Bem, por exemplo, há uma varredura de uma área de 100 por 110 mm em 10 passagens - então geralmente perdemos muito ... O



algoritmo proposto é usado para eliminar este erro, e de forma alguma para super-moinhos, etc.



All Articles