initial commit
This commit is contained in:
commit
41cf57e7d3
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
build/
|
||||||
|
MPLABXLog.xml
|
||||||
57
Makefile
Normal file
57
Makefile
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Makefile — fdr1-modbus-slave
|
||||||
|
# Cible : PIC18F4431
|
||||||
|
# Compilateur : XC8
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Projet
|
||||||
|
PROJECT = fdr1-modbus-slave
|
||||||
|
MCU = 18F4431
|
||||||
|
|
||||||
|
# Outils
|
||||||
|
CC = xc8-cc
|
||||||
|
IPECMD = java -jar /opt/microchip/mplabx/v6.20/mplab_platform/mplab_ipe/ipecmd.jar
|
||||||
|
# -OK
|
||||||
|
|
||||||
|
# Répertoires
|
||||||
|
SRC_DIR = src
|
||||||
|
INC_DIR = includes
|
||||||
|
BUILD_DIR = build
|
||||||
|
|
||||||
|
# Flags
|
||||||
|
DFP = /opt/microchip/mplabx/v6.20/packs/Microchip/PIC18Fxxxx_DFP/1.6.159/xc8
|
||||||
|
|
||||||
|
CFLAGS = -mcpu=$(MCU)
|
||||||
|
CFLAGS += -mdfp=$(DFP)
|
||||||
|
CFLAGS += -I$(INC_DIR)
|
||||||
|
CFLAGS += -O2
|
||||||
|
CFLAGS += -maddrqual=ignore
|
||||||
|
CFLAGS += -Wl,-Map=$(BUILD_DIR)/$(PROJECT).map
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
SRCS = $(wildcard $(SRC_DIR)/*.c)
|
||||||
|
|
||||||
|
# Cible principale
|
||||||
|
HEX = $(BUILD_DIR)/$(PROJECT).hex
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
.PHONY: all clean flash
|
||||||
|
|
||||||
|
all: $(BUILD_DIR) $(HEX)
|
||||||
|
|
||||||
|
# Création du répertoire build si absent
|
||||||
|
$(BUILD_DIR):
|
||||||
|
mkdir -p $(BUILD_DIR)
|
||||||
|
|
||||||
|
# Compilation + link en une seule passe XC8
|
||||||
|
$(HEX): $(SRCS)
|
||||||
|
$(CC) $(CFLAGS) $(SRCS) -o $(HEX)
|
||||||
|
|
||||||
|
# Flash via PICkit3
|
||||||
|
flash: $(HEX)
|
||||||
|
$(IPECMD) -TPPK3 -P$(MCU) -F$(HEX) -M -OL
|
||||||
|
|
||||||
|
# Nettoyage
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
76
README.md
Normal file
76
README.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# fdr1-modbus-slave
|
||||||
|
|
||||||
|
Firmware Modbus RTU esclave pour encodeur incrémental, 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Matériel requis
|
||||||
|
|
||||||
|
- Carte **FDR1 v1.00**
|
||||||
|
- Microcontrôleur **PIC18F4431**
|
||||||
|
- Quartz **8 MHz** (HSPLL x4 → Fosc = 32 MHz)
|
||||||
|
- Encodeur incrémental raccordé au périphérique QEI
|
||||||
|
- Liaison série RS232 (RS485 à venir)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Modbus RTU
|
||||||
|
|
||||||
|
| Paramètre | Valeur |
|
||||||
|
|------------------|------------|
|
||||||
|
| Adresse esclave | 183 |
|
||||||
|
| Vitesse | 115200 bps |
|
||||||
|
| Format | 8N1 |
|
||||||
|
| Function code | FC03 |
|
||||||
|
|
||||||
|
### Registres
|
||||||
|
|
||||||
|
| Adresse | Nom | Type | Accès | Description |
|
||||||
|
|----------|-----------|--------|-------|-----------------|
|
||||||
|
| `0x1700` | HR0 | UINT16 | R | Position QEI |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build & Flash
|
||||||
|
|
||||||
|
### Prérequis
|
||||||
|
|
||||||
|
- XC8 (dans le PATH)
|
||||||
|
- MPLABX v6.20 avec DFP PIC18Fxxxx installé
|
||||||
|
- PICkit3
|
||||||
|
|
||||||
|
### Compilation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flashage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make flash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nettoyage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Copyright (c) 2026, FDR1 Project
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. All advertising materials mentioning features or use of this software must display the following acknowledgement: *This product includes software developed by the FDR1 Project.*
|
||||||
|
4. Neither the name of the FDR1 Project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
|
||||||
70
includes/config.h
Normal file
70
includes/config.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* File: config.h
|
||||||
|
* Author: JR
|
||||||
|
*
|
||||||
|
* Created on May 6, 2024, 2:50 PM
|
||||||
|
*
|
||||||
|
* All configurable parameters are defined here
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
// MCU frequency
|
||||||
|
#define FOSC 32000000 // 8MHz crystal *4 (PLL) = 32MHz
|
||||||
|
#define _XTAL_FREQ FOSC
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "sysconfig.h"
|
||||||
|
|
||||||
|
/* Pin Mapping:
|
||||||
|
*
|
||||||
|
* Port #4 #2 Func Wire to
|
||||||
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
* RA0 2 2 RLD Red LED (limits out of range)
|
||||||
|
* RA1 3 3 BLD Blue LED (activity)
|
||||||
|
* RA2 3 4 GLD Green LED (speed)
|
||||||
|
* RA3 5 5 QEA QEA
|
||||||
|
* RA4 6 6 QEB QEB
|
||||||
|
* RA5 7 XX Qenable (reserved/unused)
|
||||||
|
* RA6 14 10 O Rotation out
|
||||||
|
* RA7 13 XX
|
||||||
|
* RB0 33 21 Carriage Step
|
||||||
|
* RB1 34 22 O Motor Step
|
||||||
|
* RB2 35 23 Carriage Dir
|
||||||
|
* RB3 36 24 O Motor Dir
|
||||||
|
* RB4 37 25 O Motor Enable
|
||||||
|
* RB5 38 26 P PGM
|
||||||
|
* RB6 39 27 P PGC
|
||||||
|
* RB7 40 28 P PGD
|
||||||
|
* RC0 15 11 Carriage Enable
|
||||||
|
* RC1 16 12 O DialReq
|
||||||
|
* RC2 17 13 I DialData
|
||||||
|
* RC3 18 14 INT0 DialClk
|
||||||
|
* RC4 23 15 INT1 Btn_Reset_Dist
|
||||||
|
* RC5 24 16 INT2 Btn_Reset_Limits
|
||||||
|
* RC6 25 17 TX RS232 TX
|
||||||
|
* RC7 26 18 RX RS232 RX
|
||||||
|
* RD0 19 XX
|
||||||
|
* RD1 20 XX
|
||||||
|
* RD2 21 XX
|
||||||
|
* RD3 22 XX
|
||||||
|
* RD4 27 XX
|
||||||
|
* RD5 28 XX
|
||||||
|
* RD6 29 XX
|
||||||
|
* RD7 30 XX
|
||||||
|
* RE0 8 XX
|
||||||
|
* RE1 9 XX
|
||||||
|
* RE2 10 XX
|
||||||
|
* RE3 1 1 P VPP
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Peripherals:
|
||||||
|
* QEI: 16 bit Motion Feedback (IC+QEI)
|
||||||
|
* Timer0: 16 bit
|
||||||
|
* Timer1: 16 bit
|
||||||
|
* Timer2: 8 bit RTC ?
|
||||||
|
* Timer5: 16 bit Motor rotation control / Next: Reserved: Velocity measure
|
||||||
|
*/
|
||||||
13
includes/macros.h
Normal file
13
includes/macros.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
|
||||||
|
#ifndef __MACROS_H__
|
||||||
|
#define __MACROS_H__
|
||||||
|
|
||||||
|
#define RED(STATE) {LATDbits.LD0 = STATE;}
|
||||||
|
#define GREEN(STATE) {LATDbits.LD2 = STATE;}
|
||||||
|
#define BLUE(STATE) {LATDbits.LD1 = STATE;}
|
||||||
|
#define RED_TGL() {LATDbits.LD0 = !LATDbits.LD0;}
|
||||||
|
#define GREEN_TGL() {LATDbits.LD2 = !LATDbits.LD2;}
|
||||||
|
#define BLUE_TGL() {LATDbits.LD1 = !LATDbits.LD1;}
|
||||||
|
|
||||||
|
#endif //__MACROS_H__
|
||||||
9
includes/qei.h
Normal file
9
includes/qei.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
|
||||||
|
#ifndef __FDR1_QEI__
|
||||||
|
#define __FDR1_QEI__
|
||||||
|
|
||||||
|
void QEI_Init(void);
|
||||||
|
uint16_t QEI_ReadPosition(void);
|
||||||
|
|
||||||
|
#endif //__FDR1_QEI__
|
||||||
40
includes/rtu.h
Normal file
40
includes/rtu.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef RTU_H
|
||||||
|
#define RTU_H
|
||||||
|
|
||||||
|
#include <xc.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Configuration Modbus RTU
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
#define RTU_SLAVE_ADDR 0xB7
|
||||||
|
#define RTU_FC03 0x03
|
||||||
|
|
||||||
|
#define RTU_HR0_ADDR 0x1700 // Position QEI
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Timer2 — timeout inter-caractère 3.5×
|
||||||
|
// ≈ 304 µs @ 115200 bps
|
||||||
|
// prescaler 1:16, PR2 = 151
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
#define RTU_TIMER_PRESCALER 16
|
||||||
|
#define RTU_TIMER_PR2 151
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Buffer
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
#define RTU_BUF_SIZE 32
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// API publique
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
void RTU_Init(void);
|
||||||
|
void RTU_RxISR(void); // ISR UART RX haute priorité
|
||||||
|
void RTU_TimerISR(void); // ISR Timer2 basse priorité
|
||||||
|
void RTU_Task(void); // boucle principale
|
||||||
|
|
||||||
|
#endif // RTU_H
|
||||||
9
includes/setup.h
Normal file
9
includes/setup.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
|
||||||
|
#ifndef __FDR1_SETUP__
|
||||||
|
#define __FDR1_SETUP__
|
||||||
|
|
||||||
|
void setup(void);
|
||||||
|
void setup_gpio(void);
|
||||||
|
|
||||||
|
#endif //__FDR1_SETUP__
|
||||||
68
includes/sysconfig.h
Normal file
68
includes/sysconfig.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* File: sysconfig.h
|
||||||
|
* Author: JR
|
||||||
|
*
|
||||||
|
* Created on May 6, 2024, 2:50 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
// CONFIG1H
|
||||||
|
#pragma config OSC = HSPLL
|
||||||
|
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
|
||||||
|
#pragma config IESO = ON // Internal External Oscillator Switchover bit (Internal External Switchover mode enabled)
|
||||||
|
|
||||||
|
// CONFIG2L
|
||||||
|
#pragma config PWRTEN = OFF // Power-up Timer Enable bit (PWRT disabled)
|
||||||
|
#pragma config BOREN = OFF // Brown-out Reset Enable bits (Brown-out Reset enabled)
|
||||||
|
#pragma config BORV = 42 // Brown Out Reset Voltage bits (VBOR set to 4.2V)
|
||||||
|
|
||||||
|
// CONFIG2H
|
||||||
|
#pragma config WDTEN = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
|
||||||
|
#pragma config WDPS = 32768 // Watchdog Timer Postscale Select bits (1:32768)
|
||||||
|
#pragma config WINEN = OFF // Watchdog Timer Window Enable bit (WDT window disabled)
|
||||||
|
|
||||||
|
// CONFIG3L
|
||||||
|
#pragma config PWMPIN = OFF // PWM output pins Reset state control (PWM outputs disabled upon Reset (default))
|
||||||
|
#pragma config LPOL = HIGH // Low-Side Transistors Polarity (PWM0, 2, 4 and 6 are active-high)
|
||||||
|
#pragma config HPOL = HIGH // High-Side Transistors Polarity (PWM1, 3, 5 and 7 are active-high)
|
||||||
|
#pragma config T1OSCMX = ON // Timer1 Oscillator MUX (Low-power Timer1 operation when microcontroller is in Sleep mode)
|
||||||
|
|
||||||
|
// CONFIG3H
|
||||||
|
#pragma config FLTAMX = RC1 // FLTA MUX bit (FLTA input is multiplexed with RC1)
|
||||||
|
#pragma config SSPMX = RC7 // SSP I/O MUX bit (SCK/SCL clocks and SDA/SDI data are multiplexed with RC5 and RC4, respectively. SDO output is multiplexed with RC7.)
|
||||||
|
#pragma config PWM4MX = RB5 // PWM4 MUX bit (PWM4 output is multiplexed with RB5)
|
||||||
|
#pragma config EXCLKMX = RC3 // TMR0/T5CKI External clock MUX bit (TMR0/T5CKI external clock input is multiplexed with RC3)
|
||||||
|
#pragma config MCLRE = ON // MCLR Pin Enable bit (Enabled)
|
||||||
|
|
||||||
|
// CONFIG4L
|
||||||
|
#pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
|
||||||
|
#pragma config LVP = ON // Low-Voltage ICSP Enable bit (Low-voltage ICSP enabled)
|
||||||
|
|
||||||
|
// CONFIG5L
|
||||||
|
#pragma config CP0 = OFF // Code Protection bit (Block 0 (000200-000FFFh) not code-protected)
|
||||||
|
#pragma config CP1 = OFF // Code Protection bit (Block 1 (001000-001FFF) not code-protected)
|
||||||
|
#pragma config CP2 = OFF // Code Protection bit (Block 2 (002000-002FFFh) not code-protected)
|
||||||
|
#pragma config CP3 = OFF // Code Protection bit (Block 3 (003000-003FFFh) not code-protected)
|
||||||
|
|
||||||
|
// CONFIG5H
|
||||||
|
#pragma config CPB = OFF // Boot Block Code Protection bit (Boot Block (000000-0001FFh) not code-protected)
|
||||||
|
#pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected)
|
||||||
|
|
||||||
|
// CONFIG6L
|
||||||
|
#pragma config WRT0 = OFF // Write Protection bit (Block 0 (000200-000FFFh) not write-protected)
|
||||||
|
#pragma config WRT1 = OFF // Write Protection bit (Block 1 (001000-001FFF) not write-protected)
|
||||||
|
#pragma config WRT2 = OFF // Write Protection bit (Block 2 (002000-002FFFh) not write-protected)
|
||||||
|
#pragma config WRT3 = OFF // Write Protection bit (Block 3 (003000-003FFFh) not write-protected)
|
||||||
|
|
||||||
|
// CONFIG6H
|
||||||
|
#pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
|
||||||
|
#pragma config WRTB = OFF // Boot Block Write Protection bit (Boot Block (000000-0001FFh) not write-protected)
|
||||||
|
#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected)
|
||||||
|
|
||||||
|
// CONFIG7L
|
||||||
|
#pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000200-000FFFh) not protected from table reads executed in other blocks)
|
||||||
|
#pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (001000-001FFF) not protected from table reads executed in other blocks)
|
||||||
|
#pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (002000-002FFFh) not protected from table reads executed in other blocks)
|
||||||
|
#pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (003000-003FFFh) not protected from table reads executed in other blocks)
|
||||||
|
|
||||||
|
// CONFIG7H
|
||||||
|
#pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot Block (000000-0001FFh) not protected from table reads executed in other blocks)
|
||||||
8
includes/uart.h
Normal file
8
includes/uart.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
|
||||||
|
#ifndef __FDR1_UART__
|
||||||
|
#define __FDR1_UART__
|
||||||
|
|
||||||
|
void UART_Init(void);
|
||||||
|
|
||||||
|
#endif //__FDR1_UART__
|
||||||
30
src/main.c
Normal file
30
src/main.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <xc.h>
|
||||||
|
#include <setup.h>
|
||||||
|
#include <qei.h>
|
||||||
|
#include <uart.h>
|
||||||
|
#include <rtu.h>
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
RTU_Task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __interrupt(low_priority) LOWprio_interrupt(void) {
|
||||||
|
if( PIR1bits.TMR2IF )
|
||||||
|
RTU_TimerISR();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __interrupt(high_priority) HIGHprio_interrupt(void) {
|
||||||
|
if( PIR1bits.RCIF )
|
||||||
|
RTU_RxISR();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
35
src/qei.c
Normal file
35
src/qei.c
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
|
||||||
|
void QEI_Init(void) {
|
||||||
|
QEICONbits.QEIM = 0; // Disable QEI Module
|
||||||
|
QEICONbits.QERR = 0; // Clear any count errors
|
||||||
|
QEICONbits.PDEC = 0; // Continue operation during sleep
|
||||||
|
QEICONbits.nVELM = 1; // Disable velocity mode
|
||||||
|
|
||||||
|
DFLTCONbits.FLTCK = 1; // filter 1:2, on QEA, QEB, and IDX
|
||||||
|
DFLTCONbits.FLT1EN = 1;
|
||||||
|
DFLTCONbits.FLT2EN = 1;
|
||||||
|
DFLTCONbits.FLT3EN = 1;
|
||||||
|
DFLTCONbits.FLT4EN = 0;
|
||||||
|
|
||||||
|
MAXCNTH = 0xFF; // MAXCNT = 65535, to maximize range
|
||||||
|
MAXCNTL = 0xFF;
|
||||||
|
|
||||||
|
POSCNTH = 0; // Reset position counter
|
||||||
|
POSCNTL = 0;
|
||||||
|
|
||||||
|
QEICONbits.QEIM = 5; // X4 mode with position counter reset by MAXCNT
|
||||||
|
// QEICONbits.QEIM = 2; // X2 mode with position counter reset by MAXCNT
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t QEI_ReadPosition(void) {
|
||||||
|
uint8_t hi1, hi2, lo;
|
||||||
|
|
||||||
|
do {
|
||||||
|
hi1 = POSCNTH;
|
||||||
|
lo = POSCNTL;
|
||||||
|
hi2 = POSCNTH;
|
||||||
|
} while (hi1 != hi2);
|
||||||
|
|
||||||
|
return ((uint16_t)hi1 << 8) | lo;
|
||||||
|
}
|
||||||
180
src/rtu.c
Normal file
180
src/rtu.c
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#include "rtu.h"
|
||||||
|
#include "qei.h"
|
||||||
|
#include <macros.h>
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Buffers & état interne
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
static uint8_t rx_buf[RTU_BUF_SIZE];
|
||||||
|
static uint8_t rx_len;
|
||||||
|
static uint8_t frame_ready;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// CRC16 Modbus
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
static uint16_t RTU_CRC16(uint8_t *buf, uint8_t len)
|
||||||
|
{
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
uint8_t i, bit;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
crc ^= buf[i];
|
||||||
|
for (bit = 0; bit < 8; bit++) {
|
||||||
|
if (crc & 0x0001)
|
||||||
|
crc = (crc >> 1) ^ 0xA001;
|
||||||
|
else
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Envoi d'un octet (attente TXIF)
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
static void RTU_SendByte(uint8_t byte)
|
||||||
|
{
|
||||||
|
while (!PIR1bits.TXIF);
|
||||||
|
TXREG = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Réponse FC03 — 1 registre
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
static void RTU_SendFC03(uint16_t value)
|
||||||
|
{
|
||||||
|
uint8_t resp[5];
|
||||||
|
uint16_t crc;
|
||||||
|
|
||||||
|
resp[0] = RTU_SLAVE_ADDR;
|
||||||
|
resp[1] = RTU_FC03;
|
||||||
|
resp[2] = 0x02; // byte count = 2
|
||||||
|
resp[3] = (value >> 8); // high byte
|
||||||
|
resp[4] = (value & 0xFF); // low byte
|
||||||
|
|
||||||
|
crc = RTU_CRC16(resp, 5);
|
||||||
|
|
||||||
|
RTU_SendByte(resp[0]);
|
||||||
|
RTU_SendByte(resp[1]);
|
||||||
|
RTU_SendByte(resp[2]);
|
||||||
|
RTU_SendByte(resp[3]);
|
||||||
|
RTU_SendByte(resp[4]);
|
||||||
|
RTU_SendByte(crc & 0xFF); // CRC low
|
||||||
|
RTU_SendByte(crc >> 8); // CRC high
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Init
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
void RTU_Init(void)
|
||||||
|
{
|
||||||
|
rx_len = 0;
|
||||||
|
frame_ready = 0;
|
||||||
|
|
||||||
|
// Timer2 — timeout inter-caractère 3.5×
|
||||||
|
PR2 = RTU_TIMER_PR2;
|
||||||
|
T2CON = 0b00000110; // prescaler 1:16, Timer2 OFF
|
||||||
|
PIR1bits.TMR2IF = 0;
|
||||||
|
IPR1bits.TMR2IP = 0; // basse priorité
|
||||||
|
PIE1bits.TMR2IE = 1;
|
||||||
|
|
||||||
|
// UART RX interrupt
|
||||||
|
IPR1bits.RCIP = 1; // haute priorité
|
||||||
|
PIE1bits.RCIE = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// ISR UART RX — haute priorité
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
void RTU_RxISR(void)
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
|
||||||
|
BLUE(1);
|
||||||
|
|
||||||
|
// Gestion erreur overrun
|
||||||
|
if (RCSTAbits.OERR) {
|
||||||
|
RCSTAbits.CREN = 0;
|
||||||
|
RCSTAbits.CREN = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte = RCREG;
|
||||||
|
|
||||||
|
if (rx_len < RTU_BUF_SIZE)
|
||||||
|
rx_buf[rx_len++] = byte;
|
||||||
|
|
||||||
|
// Redémarre le timer à chaque octet reçu
|
||||||
|
TMR2 = 0;
|
||||||
|
PIR1bits.TMR2IF = 0;
|
||||||
|
T2CONbits.TMR2ON = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// ISR Timer2 — basse priorité
|
||||||
|
// Silence de 3.5× détecté → trame complète
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
void RTU_TimerISR(void)
|
||||||
|
{
|
||||||
|
T2CONbits.TMR2ON = 0;
|
||||||
|
PIR1bits.TMR2IF = 0;
|
||||||
|
BLUE(0);
|
||||||
|
|
||||||
|
if (rx_len > 0)
|
||||||
|
frame_ready = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// Tâche principale — traitement de la trame
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
void RTU_Task(void)
|
||||||
|
{
|
||||||
|
uint16_t crc_rx, crc_calc;
|
||||||
|
uint16_t reg_addr, reg_count;
|
||||||
|
|
||||||
|
if (!frame_ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
frame_ready = 0;
|
||||||
|
|
||||||
|
// Trame FC03 minimum : 8 octets
|
||||||
|
if (rx_len < 8)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
// Adresse esclave
|
||||||
|
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);
|
||||||
|
|
||||||
|
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
|
||||||
|
if (reg_addr != RTU_HR0_ADDR || reg_count != 1)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
GREEN(1);
|
||||||
|
RTU_SendFC03(QEI_ReadPosition());
|
||||||
|
GREEN(0);
|
||||||
|
|
||||||
|
done:
|
||||||
|
rx_len = 0;
|
||||||
|
}
|
||||||
53
src/setup.c
Normal file
53
src/setup.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <xc.h>
|
||||||
|
#include <setup.h>
|
||||||
|
#include <qei.h>
|
||||||
|
#include <uart.h>
|
||||||
|
#include <rtu.h>
|
||||||
|
#include <macros.h>
|
||||||
|
|
||||||
|
void setup(void) {
|
||||||
|
setup_gpio();
|
||||||
|
|
||||||
|
RED(1);
|
||||||
|
|
||||||
|
QEI_Init();
|
||||||
|
UART_Init();
|
||||||
|
RTU_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
|
||||||
|
INTCON3 = 0b11011000; // INT1 interrupt high prio, enabled , wired on DialCLK // INT2 interrupt high prio, enabled , wired on DialCLK
|
||||||
|
|
||||||
|
RCONbits.IPEN = 1;
|
||||||
|
INTCONbits.GIEH = 1; // haute priorité
|
||||||
|
INTCONbits.GIEL = 1; // basse priorité
|
||||||
|
|
||||||
|
__delay_ms(3000);
|
||||||
|
|
||||||
|
RED(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_gpio() {
|
||||||
|
|
||||||
|
ANSEL0 = 0; // All analog select disable (=digital I/O)
|
||||||
|
ANSEL0bits.ANS2 = 1; // set RA2/QEI_IDX as analog to disable acting as QEI input
|
||||||
|
ANSEL1 = 0;
|
||||||
|
|
||||||
|
LATA = 0;
|
||||||
|
TRISA = 0b10111011; // XO / QEx / RA0+RA1+RA2
|
||||||
|
|
||||||
|
LATB = 0;
|
||||||
|
TRISB = 0b00000000; // PGx / Motor driver outputs / SDA+SCL
|
||||||
|
|
||||||
|
LATC = 0;
|
||||||
|
TRISC = 0b10111111; // all inputs: TX/RX/ buttons / RC0+RC1
|
||||||
|
|
||||||
|
LATD = 0;
|
||||||
|
TRISD = 0b11111000; // Red, Blue, Green LEDs as output
|
||||||
|
|
||||||
|
LATE = 0;
|
||||||
|
TRISE = 0b11111000; // (xxxxIOOO) MCLR / Arm
|
||||||
|
|
||||||
|
}
|
||||||
17
src/uart.c
Normal file
17
src/uart.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
|
||||||
|
void UART_Init() {
|
||||||
|
TRISC |= 0b11000000;
|
||||||
|
RCSTA = 0xB0; // SPEN | SREN | CREN
|
||||||
|
TXSTA = 0x24; // TXEN | BRGH
|
||||||
|
// SPBRG = 1; // 250000bps at 8MHz
|
||||||
|
// SPBRG = 7; // 250000bps at 32MHz
|
||||||
|
// SPBRG = 3; // 500000bps at 32MHz
|
||||||
|
// SPBRG = 1; // 1000000bps at 32MHz
|
||||||
|
// SPBRG = 17; // 115200 bps à 32MHz, erreur -1.36%
|
||||||
|
SPBRG = 16; // 115200 bps à 32MHz, erreur +2.12%
|
||||||
|
|
||||||
|
// EUSART interrupts as low prio, enable
|
||||||
|
IPR1bits.RCIP = 0;
|
||||||
|
PIE1bits.RCIE = 1;
|
||||||
|
}
|
||||||
80
unused/echo.c
Normal file
80
unused/echo.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include <xc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <echo.h>
|
||||||
|
|
||||||
|
void echo(char* str) {
|
||||||
|
for(int i=0; str[i]!=0;i++)
|
||||||
|
echo_c(str[i]);
|
||||||
|
}
|
||||||
|
void echo_c(char c) {
|
||||||
|
while(!TXIF);
|
||||||
|
TXREG = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// void echo_float(float v) {
|
||||||
|
// char buf[CHAR_BUFF_SIZE];
|
||||||
|
// sprintf(buf, "%0.3f", v);
|
||||||
|
// echo(buf);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void echo_float1(float v) {
|
||||||
|
// char buf[CHAR_BUFF_SIZE];
|
||||||
|
// sprintf(buf, "%0.1f", v);
|
||||||
|
// echo(buf);
|
||||||
|
// }
|
||||||
|
// void echo_float2(float v) {
|
||||||
|
// char buf[CHAR_BUFF_SIZE];
|
||||||
|
// sprintf(buf, "%0.2f", v);
|
||||||
|
// echo(buf);
|
||||||
|
// }
|
||||||
|
// void echo_float4(float v) {
|
||||||
|
// char buf[CHAR_BUFF_SIZE];
|
||||||
|
// sprintf(buf, "%0.4f", v);
|
||||||
|
// echo(buf);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void echo_float6(float v) {
|
||||||
|
// char buf[CHAR_BUFF_SIZE];
|
||||||
|
// sprintf(buf, "%0.6f", v);
|
||||||
|
// echo(buf);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void echo_float8(float v) {
|
||||||
|
// char buf[CHAR_BUFF_SIZE];
|
||||||
|
// sprintf(buf, "%0.8f", v);
|
||||||
|
// echo(buf);
|
||||||
|
// }
|
||||||
|
|
||||||
|
const char h[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||||
|
|
||||||
|
void echo_hex(uint8_t v) {
|
||||||
|
echo_c(h[(v&0xf0)>>4]);
|
||||||
|
echo_c(h[(v&0x0f)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void echo_hex16(uint16_t v) {
|
||||||
|
echo_hex((v&0xff00)>>8);
|
||||||
|
echo_hex((v&0x00ff));
|
||||||
|
}
|
||||||
|
|
||||||
|
void echo_uint16(uint16_t v) {
|
||||||
|
char buf[CHAR_BUFF_SIZE];
|
||||||
|
sprintf(buf, "%hu", v);
|
||||||
|
echo(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void echo_int32(int32_t v) {
|
||||||
|
char buf[CHAR_BUFF_SIZE];
|
||||||
|
sprintf(buf, "%ld", v);
|
||||||
|
echo(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// void echo_bool_label(char* label, uint8_t b) {
|
||||||
|
// echo(label);
|
||||||
|
// if(b)
|
||||||
|
// echo("ON");
|
||||||
|
// else
|
||||||
|
// echo("off");
|
||||||
|
// }
|
||||||
28
unused/echo.h
Normal file
28
unused/echo.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef __ECHO_H__
|
||||||
|
#define __ECHO_H__
|
||||||
|
|
||||||
|
#define NL "\r\n"
|
||||||
|
|
||||||
|
#define CHAR_BUFF_SIZE 16
|
||||||
|
|
||||||
|
void echo(char*);
|
||||||
|
void echo_c(char);
|
||||||
|
|
||||||
|
// void echo_int(int);
|
||||||
|
void echo_hex(uint8_t);
|
||||||
|
void echo_hex16(uint16_t);
|
||||||
|
|
||||||
|
void echo_float(float); // def 3 digits
|
||||||
|
void echo_float1(float); // 6 digits
|
||||||
|
void echo_float2(float); // 6 digits
|
||||||
|
void echo_float4(float); // 6 digits
|
||||||
|
void echo_float6(float); // 6 digits
|
||||||
|
void echo_float8(float); // 8 digits
|
||||||
|
|
||||||
|
void echo_uint16(uint16_t);
|
||||||
|
void echo_int32(int32_t);
|
||||||
|
|
||||||
|
void echo_bool_label(char*, uint8_t);
|
||||||
|
|
||||||
|
|
||||||
|
#endif //__ECHO_H__
|
||||||
Loading…
Reference in New Issue
Block a user