dev
This commit is contained in:
parent
a6584012bb
commit
39cce4bbde
63
README.md
63
README.md
@ -1,8 +1,8 @@
|
||||
# fdr1-modbus-slave
|
||||
|
||||
Firmware Modbus RTU esclave pour encodeur incrémental, conçu pour tourner sur la carte **FDR1 v1.00**.
|
||||
Firmware Modbus RTU esclave pour trancanneur industriel, conçu pour tourner sur la carte **FDR1 v1.00**.
|
||||
|
||||
Le PIC lit la position du codeur QEI et la met à disposition d'un maître Modbus RTU (OpenPLC ou autre) via liaison série RS232/RS485.
|
||||
Le PIC lit la vitesse du fil via le périphérique QEI, pilote le moteur de bobine (SPOOL) et synchronise le guide-fil (ARM) via un virtual gear configurable. La communication avec le maître (OpenPLC ou autre) se fait en Modbus RTU RS232/RS485.
|
||||
|
||||
---
|
||||
|
||||
@ -12,6 +12,7 @@ Le PIC lit la position du codeur QEI et la met à disposition d'un maître Modbu
|
||||
- Microcontrôleur **PIC18F4431**
|
||||
- Quartz **8 MHz** (HSPLL x4 → Fosc = 32 MHz)
|
||||
- Encodeur incrémental raccordé au périphérique QEI
|
||||
- 2 drivers pas à pas Step/Dir (SPOOL + ARM)
|
||||
- Liaison série RS232 (RS485 à venir)
|
||||
|
||||
---
|
||||
@ -19,17 +20,65 @@ Le PIC lit la position du codeur QEI et la met à disposition d'un maître Modbu
|
||||
## Configuration Modbus RTU
|
||||
|
||||
| Paramètre | Valeur |
|
||||
|------------------|------------|
|
||||
|-----------------|------------|
|
||||
| Adresse esclave | 183 |
|
||||
| Vitesse | 115200 bps |
|
||||
| Format | 8N1 |
|
||||
| Function code | FC03 |
|
||||
| Function codes | FC03, FC06, FC16 |
|
||||
|
||||
### Registres
|
||||
---
|
||||
|
||||
## Registres Modbus
|
||||
|
||||
### Lecture (FC03)
|
||||
|
||||
| Adresse | Nom | Type | Description |
|
||||
|----------|-------------|--------|--------------------------|
|
||||
| `0x1700` | QEI_POS | UINT16 | Vitesse fil (position QEI) |
|
||||
|
||||
### Écriture (FC06 / FC16)
|
||||
|
||||
| Adresse | Nom | Type | Accès | Description |
|
||||
|----------|-----------|--------|-------|-----------------|
|
||||
| `0x1700` | HR0 | UINT16 | R | Position QEI |
|
||||
|----------|-------------|--------|-------|----------------------------------------------------|
|
||||
| `0x0800` | REG_CTRL | UINT16 | R/W | Registre de contrôle (voir bits ci-dessous) |
|
||||
| `0x0801` | SPOOL_FREQ | UINT16 | R/W | Fréquence moteur SPOOL (Hz, 0=arrêt) |
|
||||
| `0x0802` | ARM_GEAR | UINT16 | R/W | Virtual gear ARM (µm par tour SPOOL, 0=arrêt) |
|
||||
| `0x0803` | ARM_OFFSET | UINT16 | R/W | Distance capteur home → origine (µm) — EEPROM |
|
||||
| `0x0804` | ARM_LEFT | UINT16 | R/W | Position limite gauche (1/10 mm, défaut: 500) |
|
||||
| `0x0805` | ARM_RIGHT | UINT16 | R/W | Position limite droite (1/10 mm, défaut: 4500) |
|
||||
| `0x0806` | SPOOL_RATIO | UINT16 | R/W | Démultiplication SPOOL (millièmes, 1000=1:1) — EEPROM |
|
||||
|
||||
### Bits REG_CTRL (0x0800)
|
||||
|
||||
| Bit | Nom | Description |
|
||||
|------|-------------------|------------------------------------------------------|
|
||||
| 15 | SPOOL_ENABLE | Active le moteur SPOOL |
|
||||
| 14 | SPOOL_DIR | Sens de rotation SPOOL |
|
||||
| 13 | ARM_HOMING | Lance la procédure de prise d'origine (one-shot) |
|
||||
| 12 | ARM_GO_TO_ZERO | Retour ARM à LEFT, démarre nouveau cycle (one-shot) |
|
||||
| 1 | ARM_FREE | Libère le moteur ARM (désactive vgear + ENABLE=0) |
|
||||
|
||||
### EEPROM interne
|
||||
|
||||
| Adresse | Contenu |
|
||||
|---------|---------------------|
|
||||
| `0x10` | ARM_OFFSET high |
|
||||
| `0x11` | ARM_OFFSET low |
|
||||
| `0x12` | SPOOL_RATIO high |
|
||||
| `0x13` | SPOOL_RATIO low |
|
||||
|
||||
---
|
||||
|
||||
## Fonctionnement normal
|
||||
|
||||
En production, seuls deux registres sont utilisés en continu :
|
||||
|
||||
```
|
||||
FC03 0x1700 → lecture vitesse fil
|
||||
FC06 0x0801 → consigne vitesse SPOOL
|
||||
```
|
||||
|
||||
Les registres de configuration (0x0802~0x0806) sont écrits une fois à la mise en service.
|
||||
|
||||
---
|
||||
|
||||
|
||||
59
includes/arm.h
Normal file
59
includes/arm.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef ARM_H
|
||||
#define ARM_H
|
||||
|
||||
#include <xc.h>
|
||||
#include <stdint.h>
|
||||
#include <macros.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Registres Modbus
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define ARM_REG_GEAR 0x0802 // déplacement en µm par tour SPOOL
|
||||
#define ARM_REG_OFFSET 0x0803 // distance capteur home → origine (µm)
|
||||
#define ARM_REG_LEFT 0x0804 // position limite gauche (1/10 mm)
|
||||
#define ARM_REG_RIGHT 0x0805 // position limite droite (1/10 mm)
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Mécanique
|
||||
// 1 tour ARM = 40mm = 1600 micropas
|
||||
// 1 micropas ARM = 25µm
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define ARM_UM_PER_USTEP 25U // µm par micropas
|
||||
#define ARM_USTEP_PER_REV 1600U // micropas par tour
|
||||
#define ARM_UM_PER_REV ((uint32_t)ARM_UM_PER_USTEP * ARM_USTEP_PER_REV) // 40000µm
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Limites gear
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define ARM_GEAR_MIN 0 // µm (arrêt vgear)
|
||||
#define ARM_GEAR_MAX 65535 // µm = 65.535mm par tour SPOOL
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Valeurs par défaut positions (1/10 mm)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define ARM_LEFT_DEFAULT 500 // 50.0mm
|
||||
#define ARM_RIGHT_DEFAULT 4500 // 450.0mm
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// API publique
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_Init(void);
|
||||
void ARM_Step(void); // appelé depuis l'ISR SPOOL à chaque step
|
||||
void ARM_Enable(uint8_t state); // ENABLE moteur ARM
|
||||
void ARM_SetDir(uint8_t dir); // sens de rotation ARM
|
||||
void ARM_SetFree(uint8_t state); // free ARM + désactive vgear (REG_CTRL bit1)
|
||||
void ARM_GoToZero(void); // retour à LEFT, nouvelle bobine (REG_CTRL bit12)
|
||||
void ARM_SetGear(uint16_t um); // écriture registre 0x0802
|
||||
void ARM_SetOffset(uint16_t um); // écriture registre 0x0803 + sauvegarde EEPROM
|
||||
uint16_t ARM_GetOffset(void); // lecture offset courant
|
||||
void ARM_SetLeft(uint16_t dmm); // écriture registre 0x0804 (1/10 mm)
|
||||
void ARM_SetRight(uint16_t dmm); // écriture registre 0x0805 (1/10 mm)
|
||||
uint16_t ARM_GetLeft(void);
|
||||
uint16_t ARM_GetRight(void);
|
||||
|
||||
#endif // ARM_H
|
||||
23
includes/eeprom.h
Normal file
23
includes/eeprom.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef EEPROM_H
|
||||
#define EEPROM_H
|
||||
|
||||
#include <xc.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Adresses EEPROM
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define EE_ARM_OFFSET_H 0x10 // ARM offset high byte
|
||||
#define EE_ARM_OFFSET_L 0x11 // ARM offset low byte
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// API publique
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
uint8_t EE_ReadByte(uint8_t addr);
|
||||
void EE_WriteByte(uint8_t addr, uint8_t data);
|
||||
uint16_t EE_ReadWord(uint8_t addr_h, uint8_t addr_l);
|
||||
void EE_WriteWord(uint8_t addr_h, uint8_t addr_l, uint16_t data);
|
||||
|
||||
#endif // EEPROM_H
|
||||
@ -3,6 +3,16 @@
|
||||
#ifndef __MACROS_H__
|
||||
#define __MACROS_H__
|
||||
|
||||
#define MOTOR_EN(STATE) {LATBbits.LB4 = !STATE;}
|
||||
#define MOTOR_STATE() (!LATBbits.LB4)
|
||||
#define MOTOR_REV(STATE) {LATBbits.LB2 = STATE;}
|
||||
#define MOTOR_HSTEP() {LATBbits.LB3 = !LATBbits.LB3;}
|
||||
|
||||
#define ARM_EN(STATE) {LATEbits.LE0 = !STATE;}
|
||||
#define ARM_STATE() (!LATEbits.LE0)
|
||||
#define ARM_REV(STATE) {LATEbits.LE2 = STATE;}
|
||||
#define ARM_HSTEP() {LATEbits.LE1 = !LATEbits.LE1;}
|
||||
|
||||
#define RED(STATE) {LATDbits.LD0 = STATE;}
|
||||
#define GREEN(STATE) {LATDbits.LD2 = STATE;}
|
||||
#define BLUE(STATE) {LATDbits.LD1 = STATE;}
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
#define RTU_SLAVE_ADDR 0xB7
|
||||
#define RTU_FC03 0x03
|
||||
#define RTU_FC06 0x06
|
||||
#define RTU_FC16 0x10
|
||||
|
||||
#define RTU_HR0_ADDR 0x1700 // Position QEI
|
||||
|
||||
|
||||
46
includes/spool.h
Normal file
46
includes/spool.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef SPOOL_H
|
||||
#define SPOOL_H
|
||||
|
||||
#include <xc.h>
|
||||
#include <stdint.h>
|
||||
#include <macros.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Registres Modbus
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define REG_CTRL 0x0800 // registre de contrôle commun
|
||||
#define SPOOL_REG_FREQ 0x0801 // fréquence Step (Hz)
|
||||
|
||||
// bits REG_CTRL
|
||||
#define CTRL_SPOOL_ENABLE ((uint16_t)(1U << 15)) // bit15 : SPOOL enable
|
||||
#define CTRL_SPOOL_DIR ((uint16_t)(1U << 14)) // bit14 : SPOOL dir
|
||||
#define CTRL_ARM_HOMING ((uint16_t)(1U << 13)) // bit13 : ARM homing start
|
||||
#define CTRL_ARM_GO_TO_ZERO ((uint16_t)(1U << 12)) // bit12 : ARM retour à LEFT
|
||||
#define CTRL_ARM_FREE ((uint16_t)(1U << 1)) // bit1 : ARM libre
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Limites
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define SPOOL_FREQ_MIN 1 // Hz
|
||||
#define SPOOL_FREQ_MAX 16000 // Hz (600tr/min, 200pas, x8 microstepping)
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Timer5
|
||||
// Fosc/4 = 8MHz, prescaler 1:1
|
||||
// reload = 65536 - (8000000 / (freq × 2))
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define SPOOL_TIMER_PRESCALER 1
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// API publique
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void SPOOL_Init(void);
|
||||
void SPOOL_TimerISR(void); // ISR Timer5 basse priorité
|
||||
void SPOOL_SetCtrl(uint16_t ctrl); // écriture registre 0x0800
|
||||
void SPOOL_SetFreq(uint16_t hz); // écriture registre 0x0801
|
||||
|
||||
#endif // SPOOL_H
|
||||
235
src/arm.c
Normal file
235
src/arm.c
Normal file
@ -0,0 +1,235 @@
|
||||
#include <arm.h>
|
||||
#include <eeprom.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Machine d'état traverse
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
typedef enum {
|
||||
ARM_IDLE,
|
||||
ARM_GO_TO_ZERO, // retour vers LEFT
|
||||
ARM_WAIT_LEFT, // attente 3/4 tour à LEFT
|
||||
ARM_TRAVERSE, // déplacement vers RIGHT
|
||||
ARM_WAIT_RIGHT, // attente 3/4 tour à RIGHT
|
||||
ARM_RETURN // déplacement vers LEFT
|
||||
} arm_state_t;
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Constantes
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
#define ARM_WAIT_STEPS 1200U // 3/4 tour SPOOL (200pas × 8µstep × 3/4)
|
||||
#define ARM_DIR_RIGHT 1
|
||||
#define ARM_DIR_LEFT 0
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// État interne
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
static volatile uint16_t gear_um; // déplacement µm par tour SPOOL
|
||||
static volatile uint32_t accumulator; // accumulateur µm
|
||||
static uint16_t arm_offset; // distance capteur home → origine (µm)
|
||||
static volatile uint8_t free_arm; // 1 = vgear désactivé, moteur libre
|
||||
static uint16_t pos_left; // limite gauche (1/10 mm)
|
||||
static uint16_t pos_right; // limite droite (1/10 mm)
|
||||
static volatile arm_state_t state; // état courant de la machine
|
||||
static volatile uint16_t arm_pos; // position courante en micropas depuis LEFT
|
||||
static volatile uint16_t wait_ctr; // compteur steps SPOOL pour les pauses
|
||||
static uint16_t travel_steps; // micropas pour aller de LEFT à RIGHT
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Calcul du trajet LEFT→RIGHT en micropas
|
||||
// (pos_right - pos_left) en 1/10mm → µm → micropas
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
static uint16_t ARM_CalcTravelSteps(void)
|
||||
{
|
||||
uint32_t um = (uint32_t)(pos_right - pos_left) * 100UL; // 1/10mm → µm
|
||||
return (uint16_t)(um / ARM_UM_PER_USTEP);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Init — lecture offset EEPROM au démarrage
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_Init(void)
|
||||
{
|
||||
gear_um = 0;
|
||||
accumulator = 0;
|
||||
free_arm = 0;
|
||||
pos_left = ARM_LEFT_DEFAULT;
|
||||
pos_right = ARM_RIGHT_DEFAULT;
|
||||
state = ARM_IDLE;
|
||||
arm_pos = 0;
|
||||
wait_ctr = 0;
|
||||
travel_steps = ARM_CalcTravelSteps();
|
||||
|
||||
ARM_EN(0);
|
||||
ARM_REV(0);
|
||||
|
||||
arm_offset = EE_ReadWord(EE_ARM_OFFSET_H, EE_ARM_OFFSET_L);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Enable / Dir — appelés depuis homing ou PLC
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_Enable(uint8_t state_en)
|
||||
{
|
||||
ARM_EN(state_en);
|
||||
}
|
||||
|
||||
void ARM_SetDir(uint8_t dir)
|
||||
{
|
||||
ARM_REV(dir);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Step — appelé depuis l'ISR SPOOL
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_Step(void)
|
||||
{
|
||||
if (free_arm || gear_um == 0)
|
||||
return;
|
||||
|
||||
switch (state) {
|
||||
|
||||
case ARM_IDLE:
|
||||
break;
|
||||
|
||||
case ARM_GO_TO_ZERO:
|
||||
// Déplacement vers LEFT — on décrémente arm_pos
|
||||
accumulator += gear_um;
|
||||
while (accumulator >= ARM_UM_PER_REV) {
|
||||
accumulator -= ARM_UM_PER_REV;
|
||||
if (arm_pos > 0) {
|
||||
ARM_HSTEP();
|
||||
arm_pos--;
|
||||
} else {
|
||||
// Arrivée à LEFT
|
||||
accumulator = 0;
|
||||
wait_ctr = 0;
|
||||
state = ARM_WAIT_LEFT;
|
||||
ARM_REV(ARM_DIR_RIGHT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ARM_WAIT_LEFT:
|
||||
wait_ctr++;
|
||||
if (wait_ctr >= ARM_WAIT_STEPS) {
|
||||
wait_ctr = 0;
|
||||
state = ARM_TRAVERSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARM_TRAVERSE:
|
||||
// Déplacement vers RIGHT
|
||||
accumulator += gear_um;
|
||||
while (accumulator >= ARM_UM_PER_REV) {
|
||||
accumulator -= ARM_UM_PER_REV;
|
||||
if (arm_pos < travel_steps) {
|
||||
ARM_HSTEP();
|
||||
arm_pos++;
|
||||
} else {
|
||||
// Arrivée à RIGHT
|
||||
accumulator = 0;
|
||||
wait_ctr = 0;
|
||||
state = ARM_WAIT_RIGHT;
|
||||
ARM_REV(ARM_DIR_LEFT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ARM_WAIT_RIGHT:
|
||||
wait_ctr++;
|
||||
if (wait_ctr >= ARM_WAIT_STEPS) {
|
||||
wait_ctr = 0;
|
||||
state = ARM_RETURN;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARM_RETURN:
|
||||
// Déplacement vers LEFT
|
||||
accumulator += gear_um;
|
||||
while (accumulator >= ARM_UM_PER_REV) {
|
||||
accumulator -= ARM_UM_PER_REV;
|
||||
if (arm_pos > 0) {
|
||||
ARM_HSTEP();
|
||||
arm_pos--;
|
||||
} else {
|
||||
// Arrivée à LEFT
|
||||
accumulator = 0;
|
||||
wait_ctr = 0;
|
||||
state = ARM_WAIT_LEFT;
|
||||
ARM_REV(ARM_DIR_RIGHT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// GO_TO_ZERO — retour à LEFT, nouvelle bobine
|
||||
// appelé depuis REG_CTRL bit12
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_GoToZero(void)
|
||||
{
|
||||
accumulator = 0;
|
||||
wait_ctr = 0;
|
||||
ARM_REV(ARM_DIR_LEFT);
|
||||
state = ARM_GO_TO_ZERO;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Écriture registre GEAR (0x0802)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_SetGear(uint16_t um)
|
||||
{
|
||||
gear_um = um;
|
||||
accumulator = 0;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Écriture registre OFFSET (0x0803) + EEPROM
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_SetOffset(uint16_t um)
|
||||
{
|
||||
arm_offset = um;
|
||||
EE_WriteWord(EE_ARM_OFFSET_H, EE_ARM_OFFSET_L, um);
|
||||
}
|
||||
|
||||
uint16_t ARM_GetOffset(void)
|
||||
{
|
||||
return arm_offset;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Free ARM — appelé depuis REG_CTRL bit1
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_SetFree(uint8_t s)
|
||||
{
|
||||
free_arm = s;
|
||||
ARM_EN(s ? 0 : 1);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Positions limites gauche/droite (1/10 mm)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void ARM_SetLeft(uint16_t dmm)
|
||||
{
|
||||
pos_left = dmm;
|
||||
travel_steps = ARM_CalcTravelSteps();
|
||||
}
|
||||
|
||||
void ARM_SetRight(uint16_t dmm)
|
||||
{
|
||||
pos_right = dmm;
|
||||
travel_steps = ARM_CalcTravelSteps();
|
||||
}
|
||||
53
src/eeprom.c
Normal file
53
src/eeprom.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include <eeprom.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Lecture d'un octet
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
uint8_t EE_ReadByte(uint8_t addr)
|
||||
{
|
||||
EEADR = addr;
|
||||
EECON1bits.EEPGD = 0;
|
||||
EECON1bits.CFGS = 0;
|
||||
EECON1bits.RD = 1;
|
||||
return EEDATA;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Écriture d'un octet
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void EE_WriteByte(uint8_t addr, uint8_t data)
|
||||
{
|
||||
EEADR = addr;
|
||||
EEDATA = data;
|
||||
EECON1bits.EEPGD = 0;
|
||||
EECON1bits.CFGS = 0;
|
||||
EECON1bits.WREN = 1;
|
||||
INTCONbits.GIE = 0;
|
||||
EECON2 = 0x55;
|
||||
EECON2 = 0xAA;
|
||||
EECON1bits.WR = 1;
|
||||
while (EECON1bits.WR);
|
||||
EECON1bits.WREN = 0;
|
||||
INTCONbits.GIE = 1;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Lecture d'un mot 16 bits (big endian)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
uint16_t EE_ReadWord(uint8_t addr_h, uint8_t addr_l)
|
||||
{
|
||||
return ((uint16_t)EE_ReadByte(addr_h) << 8) | EE_ReadByte(addr_l);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Écriture d'un mot 16 bits (big endian)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void EE_WriteWord(uint8_t addr_h, uint8_t addr_l, uint16_t data)
|
||||
{
|
||||
EE_WriteByte(addr_h, data >> 8);
|
||||
EE_WriteByte(addr_l, data & 0xFF);
|
||||
}
|
||||
10
src/main.c
10
src/main.c
@ -5,6 +5,7 @@
|
||||
#include <qei.h>
|
||||
#include <uart.h>
|
||||
#include <rtu.h>
|
||||
#include <spool.h>
|
||||
|
||||
void main(void) {
|
||||
setup();
|
||||
@ -15,16 +16,15 @@ void main(void) {
|
||||
}
|
||||
|
||||
void __interrupt(low_priority) LOWprio_interrupt(void) {
|
||||
if( PIR1bits.TMR2IF )
|
||||
if (PIR1bits.TMR2IF)
|
||||
RTU_TimerISR();
|
||||
return;
|
||||
if (PIR3bits.TMR5IF)
|
||||
SPOOL_TimerISR();
|
||||
}
|
||||
|
||||
void __interrupt(high_priority) HIGHprio_interrupt(void) {
|
||||
if( PIR1bits.RCIF )
|
||||
if (PIR1bits.RCIF)
|
||||
RTU_RxISR();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
151
src/rtu.c
151
src/rtu.c
@ -1,6 +1,7 @@
|
||||
#include "rtu.h"
|
||||
#include "qei.h"
|
||||
#include <macros.h>
|
||||
#include <rtu.h>
|
||||
#include <qei.h>
|
||||
#include <spool.h>
|
||||
#include <arm.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Buffers & état interne
|
||||
@ -68,9 +69,65 @@ static void RTU_SendFC03(uint16_t value)
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Init
|
||||
// Réponse FC06 — Write Single Register
|
||||
// Echo de la requête (adresse + valeur)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
static void RTU_SendFC06(uint16_t reg_addr, uint16_t value)
|
||||
{
|
||||
uint8_t resp[6];
|
||||
uint16_t crc;
|
||||
|
||||
resp[0] = RTU_SLAVE_ADDR;
|
||||
resp[1] = RTU_FC06;
|
||||
resp[2] = reg_addr >> 8;
|
||||
resp[3] = reg_addr & 0xFF;
|
||||
resp[4] = value >> 8;
|
||||
resp[5] = value & 0xFF;
|
||||
|
||||
crc = RTU_CRC16(resp, 6);
|
||||
|
||||
RTU_SendByte(resp[0]);
|
||||
RTU_SendByte(resp[1]);
|
||||
RTU_SendByte(resp[2]);
|
||||
RTU_SendByte(resp[3]);
|
||||
RTU_SendByte(resp[4]);
|
||||
RTU_SendByte(resp[5]);
|
||||
RTU_SendByte(crc & 0xFF);
|
||||
RTU_SendByte(crc >> 8);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Réponse FC16 — Write Multiple Registers
|
||||
// Echo adresse + nombre de registres écrits
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
static void RTU_SendFC16(uint16_t reg_addr, uint16_t reg_count)
|
||||
{
|
||||
uint8_t resp[6];
|
||||
uint16_t crc;
|
||||
|
||||
resp[0] = RTU_SLAVE_ADDR;
|
||||
resp[1] = RTU_FC16;
|
||||
resp[2] = reg_addr >> 8;
|
||||
resp[3] = reg_addr & 0xFF;
|
||||
resp[4] = reg_count >> 8;
|
||||
resp[5] = reg_count & 0xFF;
|
||||
|
||||
crc = RTU_CRC16(resp, 6);
|
||||
|
||||
RTU_SendByte(resp[0]);
|
||||
RTU_SendByte(resp[1]);
|
||||
RTU_SendByte(resp[2]);
|
||||
RTU_SendByte(resp[3]);
|
||||
RTU_SendByte(resp[4]);
|
||||
RTU_SendByte(resp[5]);
|
||||
RTU_SendByte(crc & 0xFF);
|
||||
RTU_SendByte(crc >> 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RTU_Init(void)
|
||||
{
|
||||
rx_len = 0;
|
||||
@ -138,13 +195,14 @@ void RTU_Task(void)
|
||||
{
|
||||
uint16_t crc_rx, crc_calc;
|
||||
uint16_t reg_addr, reg_count;
|
||||
uint16_t value;
|
||||
|
||||
if (!frame_ready)
|
||||
return;
|
||||
|
||||
frame_ready = 0;
|
||||
|
||||
// Trame FC03 minimum : 8 octets
|
||||
// Trame minimum : 8 octets
|
||||
if (rx_len < 8)
|
||||
goto done;
|
||||
|
||||
@ -152,10 +210,6 @@ void RTU_Task(void)
|
||||
if (rx_buf[0] != RTU_SLAVE_ADDR)
|
||||
goto done;
|
||||
|
||||
// Function code
|
||||
if (rx_buf[1] != RTU_FC03)
|
||||
goto done;
|
||||
|
||||
// Vérification CRC
|
||||
crc_rx = (uint16_t)rx_buf[rx_len - 1] << 8 | rx_buf[rx_len - 2];
|
||||
crc_calc = RTU_CRC16(rx_buf, rx_len - 2);
|
||||
@ -163,16 +217,87 @@ void RTU_Task(void)
|
||||
if (crc_rx != crc_calc)
|
||||
goto done;
|
||||
|
||||
// Adresse et nombre de registres
|
||||
reg_addr = (uint16_t)rx_buf[2] << 8 | rx_buf[3];
|
||||
reg_count = (uint16_t)rx_buf[4] << 8 | rx_buf[5];
|
||||
|
||||
// On ne supporte que HR0, 1 registre
|
||||
GREEN(1);
|
||||
|
||||
switch (rx_buf[1]) {
|
||||
|
||||
// ── FC03 — Read Holding Registers ────────
|
||||
case RTU_FC03:
|
||||
if (reg_addr != RTU_HR0_ADDR || reg_count != 1)
|
||||
goto done;
|
||||
|
||||
GREEN(1);
|
||||
RTU_SendFC03(QEI_ReadPosition());
|
||||
break;
|
||||
|
||||
// ── FC06 — Write Single Register ─────────
|
||||
case RTU_FC06:
|
||||
value = reg_count; // dans FC06, octets 4-5 = valeur
|
||||
if (reg_addr == REG_CTRL) {
|
||||
SPOOL_SetCtrl(value);
|
||||
if (value & CTRL_ARM_HOMING) {
|
||||
// TODO: lancer homing
|
||||
}
|
||||
if (value & CTRL_ARM_GO_TO_ZERO)
|
||||
ARM_GoToZero();
|
||||
} else if (reg_addr == SPOOL_REG_FREQ)
|
||||
SPOOL_SetFreq(value);
|
||||
else if (reg_addr == ARM_REG_GEAR)
|
||||
ARM_SetGear(value);
|
||||
else if (reg_addr == ARM_REG_OFFSET)
|
||||
ARM_SetOffset(value);
|
||||
else if (reg_addr == ARM_REG_LEFT)
|
||||
ARM_SetLeft(value);
|
||||
else if (reg_addr == ARM_REG_RIGHT)
|
||||
ARM_SetRight(value);
|
||||
else
|
||||
goto done;
|
||||
RTU_SendFC06(reg_addr, value);
|
||||
break;
|
||||
|
||||
// ── FC16 — Write Multiple Registers ──────
|
||||
case RTU_FC16: {
|
||||
uint8_t byte_count = rx_buf[6];
|
||||
uint8_t i;
|
||||
|
||||
// On supporte 0x0800~0x0805, registres contigus
|
||||
if (reg_addr < REG_CTRL || reg_addr + reg_count - 1 > ARM_REG_RIGHT)
|
||||
goto done;
|
||||
if (byte_count != reg_count * 2)
|
||||
goto done;
|
||||
|
||||
// Vérification longueur trame : 9 + byte_count + 2 CRC
|
||||
if (rx_len < (uint8_t)(9 + byte_count))
|
||||
goto done;
|
||||
|
||||
for (i = 0; i < reg_count; i++) {
|
||||
value = (uint16_t)rx_buf[7 + i * 2] << 8 | rx_buf[8 + i * 2];
|
||||
switch (reg_addr + i) {
|
||||
case REG_CTRL:
|
||||
SPOOL_SetCtrl(value);
|
||||
if (value & CTRL_ARM_HOMING) {
|
||||
// TODO: lancer homing
|
||||
}
|
||||
if (value & CTRL_ARM_GO_TO_ZERO)
|
||||
ARM_GoToZero();
|
||||
break;
|
||||
case SPOOL_REG_FREQ: SPOOL_SetFreq(value); break;
|
||||
case ARM_REG_GEAR: ARM_SetGear(value); break;
|
||||
case ARM_REG_OFFSET: ARM_SetOffset(value); break;
|
||||
case ARM_REG_LEFT: ARM_SetLeft(value); break;
|
||||
case ARM_REG_RIGHT: ARM_SetRight(value); break;
|
||||
default: goto done;
|
||||
}
|
||||
}
|
||||
RTU_SendFC16(reg_addr, reg_count);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
|
||||
GREEN(0);
|
||||
|
||||
done:
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#include <uart.h>
|
||||
#include <rtu.h>
|
||||
#include <macros.h>
|
||||
#include <spool.h>
|
||||
#include <arm.h>
|
||||
|
||||
void setup(void) {
|
||||
setup_gpio();
|
||||
@ -15,6 +17,8 @@ void setup(void) {
|
||||
QEI_Init();
|
||||
UART_Init();
|
||||
RTU_Init();
|
||||
SPOOL_Init();
|
||||
ARM_Init();
|
||||
|
||||
INTCON = 0b01010000; // GIE disabled for now, PEIE enable, INT0 enable (always highprio), not int on portB, TMR0
|
||||
INTCON2 = 0b00000101; // port B pull-up enabled, INT0/1/2 on falling edge
|
||||
|
||||
100
src/spool.c
Normal file
100
src/spool.c
Normal file
@ -0,0 +1,100 @@
|
||||
#include <spool.h>
|
||||
#include <arm.h>
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// État interne
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
static volatile uint16_t reload;
|
||||
static volatile uint8_t running;
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Calcul du reload Timer5
|
||||
// reload = 65536 - (Fosc/4 / (freq × 2))
|
||||
// Fosc/4 = 8 000 000, prescaler 1:1
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
static uint16_t SPOOL_CalcReload(uint16_t hz)
|
||||
{
|
||||
return (uint16_t)(65536UL - (8000000UL / ((uint32_t)hz * 2)));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Init
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void SPOOL_Init(void)
|
||||
{
|
||||
reload = 0;
|
||||
running = 0;
|
||||
|
||||
MOTOR_EN(0);
|
||||
MOTOR_REV(0);
|
||||
|
||||
// Timer5 off, prescaler 1:1, 16bit, Fosc/4
|
||||
T5CON = 0b00000000;
|
||||
TMR5H = 0;
|
||||
TMR5L = 0;
|
||||
PIR3bits.TMR5IF = 0;
|
||||
IPR3bits.TMR5IP = 0; // basse priorité
|
||||
PIE3bits.TMR5IE = 1;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// ISR Timer5 — basse priorité
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void SPOOL_TimerISR(void)
|
||||
{
|
||||
PIR3bits.TMR5IF = 0;
|
||||
TMR5H = reload >> 8;
|
||||
TMR5L = reload & 0xFF;
|
||||
|
||||
MOTOR_HSTEP();
|
||||
ARM_Step();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Écriture registre CTRL (0x0800)
|
||||
// bit15 = ENABLE, bit14 = DIR
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void SPOOL_SetCtrl(uint16_t ctrl)
|
||||
{
|
||||
MOTOR_EN((ctrl & CTRL_SPOOL_ENABLE) ? 1 : 0);
|
||||
MOTOR_REV((ctrl & CTRL_SPOOL_DIR) ? 1 : 0);
|
||||
ARM_SetFree((ctrl & CTRL_ARM_FREE) ? 1 : 0);
|
||||
|
||||
// CTRL_ARM_HOMING : one-shot, traité dans RTU_Task
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Écriture registre FREQ (0x0801)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void SPOOL_SetFreq(uint16_t hz)
|
||||
{
|
||||
if (hz == 0) {
|
||||
// Arrêt — timer off, position maintenue par ENABLE
|
||||
T5CONbits.TMR5ON = 0;
|
||||
running = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamping
|
||||
if (hz < SPOOL_FREQ_MIN) hz = SPOOL_FREQ_MIN;
|
||||
if (hz > SPOOL_FREQ_MAX) hz = SPOOL_FREQ_MAX;
|
||||
|
||||
reload = SPOOL_CalcReload(hz);
|
||||
|
||||
if (!running) {
|
||||
// Premier démarrage — charger le timer et démarrer
|
||||
TMR5H = reload >> 8;
|
||||
TMR5L = reload & 0xFF;
|
||||
PIR3bits.TMR5IF = 0;
|
||||
T5CONbits.TMR5ON = 1;
|
||||
running = 1;
|
||||
}
|
||||
// Si déjà running, le nouveau reload sera pris en compte
|
||||
// au prochain débordement dans l'ISR
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user