Au début, je voulais juste le télécharger à nouveau, et pour cela, j'ai obtenu le code source du projet Grbl CNC. Mais la curiosité a vaincu et je me suis plongé dans l'étude de ces sources ...
elles construisent une manière très simple et logique, mais
En fait, l'idée d'un contrôleur pour une machine CNC est assez simple et intéressante. Il existe plusieurs threads de traitement - l'un lit les données (gcode) et les analyse, le second transforme les commandes en blocs d'exécution, et le troisième (stepper) exécute ces blocs. Ce troisième volet sera discuté.
Le moteur pas à pas traite une liste de commandes individuelles de la forme - prendre des étapes (X, Y, Z) pour les trois (au moins) moteurs pas à pas, et dans un temps spécifié et dans une direction spécifiée (enfin, c'est tellement simpliste). Je dois dire qu'un moteur pas à pas avec son pilote est une chose assez simple à contrôler - vous définissez (0 ou 1) le sens de rotation, puis le moteur essaie de faire un pas par un différentiel d'entrée positif (0 -> 1) (et il y a généralement 200 pas par tour). Les données sont déjà préparées, il vous suffit donc de corréler les 3 entiers avec le temps donné.
Dans l'original, l'auteur a utilisé le contrôleur atmega328p, mais pratiquement inchangé, tout est facilement transféré sur le bras (par exemple, stm32). Mais l'algorithme lui-même ne peut que soulever des questions.
D'une part, un algorithme de Bresenham très parfait est utilisé, ou plutôt sa version d'Adaptive Multi-Axis Step-Smoothing. Mais d'un autre côté, tout est compliqué et surtout, la fluidité du moteur pas à pas et la précision du routeur dépendent directement de la précision des signaux de contrôle. Dans ce cas, cela est dû à la fréquence à laquelle la minuterie fonctionne et au temps de traitement des interruptions - et cela ne donne pas plus de 40 à 50 kHz au mieux, et généralement encore moins - eh bien, la précision du réglage de contrôle est de 20 à 50 microsecondes.
Mais il est tout à fait évident que lorsque nous traitons une commande à partir du tampon, nous devons simplement calculer les moments de commutation du signal au port de sortie et ces moments et effectuer le changement.
Puisque j'envisageais de passer à cortex-m (enfin, plus précisément, à stm32h750, que j'aime beaucoup et qui est devenu très moins cher), une telle tâche peut être résolue du tout sans impliquer le processeur, en utilisant uniquement deux canaux DMA et un compteur 32 bits.
L'idée est très simple. Laissez un canal écrire de nouvelles données sur le port en cas de dépassement du compteur, et le deuxième canal écrit une nouvelle valeur de compteur maximale (il est raisonnable de le faire au tout premier cycle d'horloge du compteur). Ensuite, pour traiter la commande de la liste, vous devez vous préparer à un tableau de valeurs de modification pour le port et les délais entre eux.
Il en résultera quelque chose comme ça.
Gestion des interruptions - passage à un nouveau tampon (double tampon).
#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;
}
Vous pouvez initier de cette manière:
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;
Eh bien, le début est:
program_down(NULL);
Qu'est ce que ça fait? Calculons en utilisant l'exemple du même stm32h750. La minuterie (TIM2) y fonctionne à une fréquence de 200 MHz, la latence minimale est de deux cycles d'horloge, mais le DMA ne peut pas envoyer de données à plus de 50 MHz, c'est-à-dire qu'entre deux commandes de commutation de port, vous pouvez mettre (en tenant compte de l'emploi possible du bus) 40 nsec (25 MHz) - c'est 1000 fois mieux que l'implémentation d'origine!
Par contre, la largeur du port est de 16 bits, vous pouvez donc contrôler 8 moteurs pas à pas en même temps au lieu de 3
Dans ce cas, le remplissage des données réelles ne pose pas de problème (avec telle ou telle résolution!) - simple interpolation linéaire pour chaque moteur séparément avec en combinant (pour l'optimisation) des événements plus proches de 40 nsec.
Les véritables conclusions.
Dans l'atelier, il y a une machine CNC finie mesurant 1,2 mètre sur 0,8 mètre avec des moteurs et des pilotes, mais pas de contrôleur. On dirait que nous devons terminer le travail et essayer à quel point il sera épique. Si je le fais, j'écrirai certainement une suite. En attendant, je ne comprends pas pourquoi les contrôleurs font cela sur atmega et ils grincent sur toutes les imprimantes 3D et routeurs cnc sur ces interruptions brutes ...
Et bien sûr, ayant probablement la puissance de Cortex-M7, vous pouvez implémenter un contrôle de trajectoire plus fluide avec toutes les restrictions , mais c'est un article complètement différent.
PS Apparemment, il est nécessaire de donner un exemple hypothétique pourquoi il est si important d'avoir un temps aussi court.
Supposons que la machine ait besoin de se déplacer de 100 mm en X et de 11 mm en Y, et que le logiciel ait tout divisé en sections d'accélération et de mouvement uniforme - il y a de nombreuses sections de 100 pas par 11 étapes et elles sont parcourues à la vitesse maximale, laissez-le correspondre à 10 kHz. Eh bien, 10 étapes en Y correspondent exactement à 100 étapes en X, mais des problèmes peuvent arriver au 11ème - cela peut être ignoré, car cela provoque un doublement de la fréquence. En conséquence, le mouvement sera de 100 mm en X et de 10 à 11 mm en Y. Et c'est avec un mouvement linéaire, où, en fait, même les restrictions sur les accélérations et les vitesses autorisées sont simples. Et si cela se fait en zigzag? Eh bien, par exemple, il y a une zone balayant 100 par 110 mm en 10 passes - alors on manque généralement beaucoup de choses ... L'
algorithme proposé est utilisé pour éliminer cette erreur, et pas du tout pour les super broyeurs, etc.