home | area personale         schemi | tutorial | robotica | pic micro | recensioni         forum | chat irc         faq | contatti         store | Ordina PCB
username
password
cerca

 
FORUM: Pic Micro
Tutto quanto riguarda questi microprocessori... progetti, suggerimenti, aiuti, discussioni...ecc


RS232 in bit banging con interrupt, non va.
     
Autore Messaggio opzioni
picmicro675




una ogni 10 livelli


postato il:
10.06.2018, alle ore 08:42
RS232 in bit banging con interrupt, non va. 

Ciao forum.
Avrei bisogno di questo tipo di seriale, scritta in assembly per evitare che si creino malfunzionamenti dovuti ad altre fonti di interrupt. Almeno c`e` la speranza che sia molto meno influenzata rispetto ad una che usa dei delay in software per ogni bit in RTX, che non accetta altre operazioni durante il periodo di pausa. E se ci fossero interrupts, e` facile che sballa la lettura della seriale.
;====================================================================
; Processor: PIC12F675
; Compiler:  MPASM (MPLAB)
;====================================================================

            list p=12F675, b=8, c= 120, n=71, t=on, st=off, f=inhx32
            #include    <p12f675.inc>
            errorlevel -302
            radix dec
            #define     BUFLEN 6
            CBLOCK      0x22            ; first 2 bytes assigned for macro.inc
            buffer:BUFLEN
            RXCHAR, FSRTMP, PTRL, PTRH, ticks, x, i, flags
            F_ISR, P_ISR, W_TEMP, STATUS_TEMP, GPIO_shadow, CHARCNT, GPIOcopy
            RD_PTR, WR_PTR, Reg4W
            ENDC

            __CONFIG _WDT_OFF & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF

            #define     TXFLAG flags,2  
            #define     PORT0 flags,3
            #define     PORT1 flags,4
            #define     zeroX flags,5
            #define     TICKSVAL 2
            #define     dec_no Reg4W
; interrupt on timer1 will occur every half second
; corrections may be required
            #define     settmr1H  11    ; preset for timer1 MSB register
            #define     settmr1L  220   ; preset for timer1 LSB register
            #define     TXPIN 0
            #define     OUT1PIN 1
            #define     RXPIN 2
            #define     IN1PIN 3
            #define     OUT0PIN 4
            #define     IN0PIN 5
            #define     IN0 GPIO,IN0PIN
            #define     IN1 GPIO,IN1PIN
            #define     TXD GPIO,TXPIN
            #define     RXD GPIO,RXPIN

_Print      macro       str
            local       String, Print
            movlw       low String
            movwf       PTRL
            movlw       high String
            movwf       PTRH
            goto        Print
String      dt          str,0
Print       call        PutString       ; print string
            endm

            org         0x0000
            goto        Main
            org         0x0004
;==============================================================================
;                    Interrupt Service Routine
;==============================================================================

            movwf       W_TEMP          ; save W-reg
            swapf       STATUS,W        ; doesn`t change STATUS bits
            movwf       STATUS_TEMP     ; save STATUS reg
            clrf        STATUS          ; bank 0
            movf        PCLATH,W        ; get PCLATH
            movwf       P_ISR           ; save PCLATH
            movf        FSR,W           ; get FSR
            movwf       F_ISR           ; save FSR

            btfss       INTCON,T0IE     ; timer0 interrupt enabled?
            goto        ckExInt         ; no, check something else
            btfsc       INTCON,T0IF     ; timer0 interrupt?
            goto        ISR_SET         ; yes, check what`s to move
ckExInt     btfss       INTCON,INTF     ; RXD \"start bit\" interrupt?
            goto        ISR_NXT         ; no, branch
            bcf         INTCON,INTE     ; start reception and stop interrupts
            bcf         INTCON,INTF     ; clear external interrupt flag
            bcf         TXFLAG          ; indicate RX in progress
            movlw       10              ; (1 start + 8 data + 1 stop)
            movwf       CHARCNT         ; initialize RX bit counter
            bcf         INTCON,T0IF     ; clear TMR0 interrupt flag
            movlw       -51             ; 1/2 bit time
            movwf       TMR0
            bsf         INTCON,T0IE     ; start the timer0
            goto        ISR_NXT         ; wait next timer0 interrupt
ISR_SET     bcf         INTCON,T0IF     ; clear TMR0 interrupt flag
            movlw       -103            ; setup for next int
            movwf       TMR0
            btfsc       TXFLAG          ; TX in progress?
            goto        ISR_TX          ; no, branch
ISR_RX      movf        WR_PTR,W        ; get WR_PTR (RXBUFF + 00..07)
            movwf       FSR             ; setup indirect address
            clrc                        ; clear Carry, assume data = 0
            btfsc       RXD             ; TRPIN = 0 ?
            setc                        ; no, set Carry, data = 1
            rrf         INDF,F          ; shift bit into our character
            decfsz      CHARCNT,F       ; all 10 bits?
            goto        ISR_NXT         ; no, branch
            rlf         INDF,F          ; yes, get rid of the stop bit
            incf        WR_PTR,W        ; W = WR_PTR + 1
            sublw       buffer+BUFLEN   ; this works for 20..27
            xorwf       RD_PTR,W        ; WR_PTR+1 = RD_PTR (full)?
            skpz                        ; yes, ignore new character
            incf        WR_PTR,F
            bcf         WR_PTR,3
            goto        END_TX
ISR_TX      movf        CHARCNT,W       ; get TX bit count
            bnz         ISR_TX0         ; no, branch, send next bit
            movlw       10              ; 10 bits (start + 8 data + st
            movwf       CHARCNT         ; initialize TX bit counter
            goto        ISR_TX1         ; send the start bit
ISR_TX0     rrf         RXCHAR,F        ; shift lsb into C
            bsf         RXCHAR,7        ; set bit 7 for stop bits
            skpnc                       ; skip if C=0
            bsf         TXD             ; set TX pin
            skpc                        ; skip if C=1
ISR_TX1     bcf         TXD             ; clr TX pin
            decfsz      CHARCNT,F       ; last bit shifted out?
            goto        ISR_NXT
END_TX      bcf         TXFLAG          ; yes, indicate end of TX
            bcf         INTCON,T0IE     ; stop timer0, transmission ended
            bsf         INTCON,INTE     ; permit reception
ISR_NXT
            banksel     PIR1            ; select bank 0
            btfss       PIR1,TMR1IF     ; If TMR1IF is set Then
            goto        ISR_XIT         ; no, then return
            call        reloadTMR1      ; reload the Timer1
            bcf         PIR1,TMR1IF     ; PIR1.TMR1IF = 0
            decfsz      ticks,F         ; if ticks zero
            goto        ISR_XIT         ; no, see for the next
            bsf         zeroX           ; signalize reached zero count
            movlw       TICKSVAL        ; reload ticks
            movwf       ticks
ISR_XIT     movf        F_ISR,W
            movwf       FSR             ; restore FSR
            movf        P_ISR,W
            movwf       PCLATH          ; restore PCLATH
            swapf       STATUS_TEMP,W
            movwf       STATUS          ; restore STATUS
            swapf       W_TEMP,F        ; don`t screw up STATUS
            swapf       W_TEMP,W        ; restore W-reg
            retfie                      ; return from interrupt

Put232      btfsc       TXFLAG          ; busy?
            goto        Put232          ; yes, branch and wait
            bsf         INTCON,T0IE     ; start timer0, new transmission
            bcf         INTCON,INTE     ; stop external interrupts
            movwf       RXCHAR          ; stuff character
            bsf         TXFLAG          ; initiate TX
            return

Get232      movf        RD_PTR,W
            xorwf       WR_PTR,W        ; RD_PTR = WR_PTR (buff empty)?
            skpnz
            return
            movf        FSR,W           ; get and save FSR
            movwf       FSRTMP
            movf        RD_PTR,W
            movwf       FSR             ; setup indirect address
            movf        INDF,W          ; get RXBUFF[RD_PTR] character
            movwf       RXCHAR          ; save it for later
            movf        FSRTMP,W
            movwf       FSR             ; restore FSR
            incf        RD_PTR,W        ; increment RD_PTR
            andlw       b`00100111`     ; keep it in range 20..27
            movwf       RD_PTR
            movf        RXCHAR,W        ; get receive character
            return

;==============================================================================
;                       initialization
;==============================================================================

init        call        0x3FF           ; load calibration bits
            banksel OSCCAL              ; select the correct bank
            movwf       OSCCAL          ; save them
            clrf        flags           ; clear all flags
            clrf        CHARCNT         ; init CHARCNT variable
            movlw       1<<RXPIN | 1<<IN0PIN | 1<<IN1PIN
            movwf       TRISIO
            movwf       WPU
            movlw       b`00000010`     ; prescaler 1:8, 1200 baud
            movwf       OPTION_REG
            ; setting timer0, general and port interrupts
            movlw       1<<GIE | 1<<GPIE | 1<<PEIE | 1<<INTE
            movwf       INTCON
            clrf        ANSEL
            clrf        VRCON           ; VRCON = 0
            banksel GPIO
            bsf         TXD             ; put TXPIN in idle state
            movlw       b`00110001`     ; Timer1 enabled prescaler 1:8
            movwf       T1CON
            movlw       7               ; comparators off
            movwf       CMCON
            movlw       TICKSVAL        ; reload ticks
            movwf       ticks
reloadTMR1  
            banksel TMR1H               ; reset to right bank if recalled
            movlw       settmr1H        ; set for timer1H MSB register
            movwf       TMR1H
            movlw       settmr1L        ; set for timer1L LSB register
            movwf       TMR1L
            return                      ; return

PutString
            call        GetTable        ; get a table character
            andlw       b`11111111`
            skpnz                       ; 00 byte, last character?
            return                      ; yes, return
            call        Put232          ; else,output character
            incfsz      PTRL,F          ; increment pointer
            goto        PutString
            incf        PTRH,F
            goto        PutString
GetTable
            movf        PTRH,W
            movwf       PCLATH
            movf        PTRL,W
            movwf       PCL

Main        call        init
            banksel     PIE1
            bsf         PIE1,TMR1IE
resume      movlw       buffer          ; RX circular buffer address
            movwf       RD_PTR          ; init circular buffer RD ptr
            movwf       WR_PTR          ; init circular buffer WR ptr
main_loop   _Print  \"System ready\\r\\n\"
Loop        btfss       zeroX
            goto        next
            banksel     GPIO
            bcf         zeroX
            btfss       flags,6
            goto        flip
            bcf         flags,6
            bcf         GPIO,OUT0PIN
            goto        next
flip        bsf         flags,6
            bsf         GPIO,OUT0PIN
next        call        Get232          ; receive character
            bz          Loop
            clrf        CHARCNT
put_new     call        Put232          ; echo character back
            goto        Loop            ; loop forever

            end

ATTENZIONE il codice e` formattato per compatibilita` all ` impaginazione di grix !!! (Gli apostrofi sono stati cambiati)
Al naturale lo si puo` scaricare da qui
http://www.grix.it/UserFiles/picmicro675/File/TransitFiles/s…
Penso che questo esempio possa funzionare anche su altri micro. Di sicuro su un 12F629 senza modifiche. Comunque la simulazione con Proteus funziona egregiamente, mentre se la provo in hardware, non lampeggia nemmeno il LED al GP4.

C`e` qualcuno capace di notare dove sta l \' errore ?
Per quello che ho potuto riscontrare, nelle mie esperienze, a volte ci sono problemi se i piedini sono lasciati liberi, che il simulatore non denota. Forse ci vuole almeno un collegamento a positivo o a massa con una resistenza.



Anno nuovo, forum nuovo.
Mi sa che lascio.
marsram




una ogni 100 livelli
una ogni 10 livelli


postato il:
10.06.2018, alle ore 15:09
Senza entrare nei dettagli del listato:

1. - cosa vuol dire "se la provo in hardware, non lampeggia nemmeno il LED al GP4" ? Come fai a "provarlo in hardware" se la lista NON si compila, dato che MPASM rende immediatamente un errore formale?

2.- lascia perplessi anche questo pezzo
GetTable
movf PTRH,W
movwf PCLATH
movf PTRL,W
movwf PCL

Main call init

Dopo aver modificato il PC, chiami la routine di inizializzazione?

Non entro nei dettagli del sorgente.
- Primo, perchè non ho idea del motivo di avere ben due timer in interrupt per trasmettere un segnale asincrono, dove di timer ne basta uno.
- Secondo, e fondamentale, perchè senza un flow chart, recuperare cosa si vuole che faccia il programma non è una cosa da poco.
Se hai un flow chart sensato, se ne può discutere.
- Terzo, perchè ci sono punti altamente critici, come accendere o spegnere flag IE di interrupt non sincroni nella routine di interruzione, cosa che può facilmente portare a crash.
picmicro675




una ogni 10 livelli


postato il:
10.06.2018, alle ore 18:19
1) mpasm compila senza errore. Altrimenti come posso dire che lo carico in hardware.
:020000040000FA
:02000000962840
:08000800B200030EB3008301F6
:100010000A08B1000408B0008B1E10280B191C2818
:100020008B1C42280B128B102F110A30B5000B11BC
:10003000CD3081008B1642280B11993081002F1989
:10004000312838088400031005190314800CB50BFF
:100050004228800D380A283C3706031DB80AB8111B
:100060003F283508031D37280A30B5003C28A80C66
:10007000A81703180514031C0510B50B42282F11EF
:100080008B120B1683120C1C4C2883200C10AC0B0B
:100090004C28AF160230AC003008840031088A00CA
:1000A000330E8300B20E320E09002F1955288B161D
:1000B0000B12A8002F15080037083806031908008E
:1000C0000408A900370884000008A8002908840053
:1000D000370A2739B70028080800FF238316900045
:1000E000AF01B5012C308500950002308100D83079
:1000F0008B009F0199018312051431309000073065
:1001000099000230AC0083120B308F00DC308E007F
:1001100008009220FF39031908005520AA0F8928EA
:10012000AB0A89282B088A002A0882006D208316D2
:100130000C142230B700B800A130AA000030AB0088
:10014000B028533479347334743465346D342034C6
:10015000723465346134643479340D340A340034D3
:100160008920AF1EBC288312AF122F1FBA282F136D
:100170000512BC282F1705165C200319B128B501FC
:040180005520B1282D
:02400E00D43F9D
:00000001FF

2) la routine GetTable non ha un seguito dato che va a caricare una tabella di RETW "carattere ASCII".
Se serve potrei includere il listing del programma.
Comunque nel sorgente intero ho usato un metodo simile per ricavare le tabelle dei messaggi da inviare alla seriale. Migliore che il modo di inserire questa macro.
Quella è usata dalla routine Put232. In pratica il Program Counter viene indirizzato ad una locazione che deve ritornare con un valore ASCII in W, al punto che è stato salvato sullo stack.

In fondo ho ripreso un sorgente pubblicato su internet con eventuali modifiche per lo scopo che va ben oltre il semplice RTX della seriale.
Filename: 12F675 Serial Demo 3.asm ;* Author: Mike McLaren, K8LH (k8lh@arrl.net)

Avrei bisogno una critica, per capire se ci sono gravi sintomi di sbadataggine.

a) il timer0 serve per dare il periodo di pausa tra due bit trasmessi o ricevuti. Di fatto si usa il prescaler a 1:8 poi 104 cicli per un totale di 833 uSec, che si avvicina ai 1200 bps.

b) il timer1 è usato per tenere una cadenza di un secondo che in altre parti del programma sono usate per contare periodi più lunghi. Ora comunque si può avere solo il massimo di mezzo secondo e compensare con un secondo conteggio. Scelto al periodo più lungo proprio al caso di non disturbare tanto.
Nel qui caso viene fatto uso solo per segnalare se il micro sta facendo il suo lavoro.

Poi ci sono le preferenze di non tenere attivi gli interrupt che non devono disturbare il percorso voluto. Infatti se sto ricevendo un byte, non deve esistere una interruzione da esterno, visto che posso solo gestire in half duplex. E quando non c'è trasmissione il timer0 dovrebbe stare fermo fino a che non è richiesto di fare le cadenze di trasmissione.
Perchè nella sua completezza, il programma prevede di controllare almeno 4 interrupts. Dei quali i rimanenti 2 dovrei anche esaminare se posso filtrare i fronti per diminuire il carico dei periodi in ISR.

Ultimo, magari fosse semplice usare i flow-chart. Dopo anni, comincio ora a capire come si usa il C, figuriamoci se mi metto a capire anche il flow-chart.



Anno nuovo, forum nuovo.
Mi sa che lascio.
marsram




una ogni 100 livelli
una ogni 10 livelli


postato il:
10.06.2018, alle ore 18:54
Se non usi un flow chart, che non richiede alcuna comprensione particolare, essendo solo la rappresentazione grafica del percorso logico del programma, come puoi pensare di risolvere problemi appena poco più che semplicistici? Come pensi di organizzare senza? Se hai una capacità di astrazione elevata da QI 160, ci può anche stare, ma in generale non esiste un buon programmatore che non usi un flowchart.

Nei Midrange NON esiste la possibilità di un interrupt di interromperne un altro. Fino a che l'interruzione attuale NON è stata chiusa dal retfie, qualsiasi altra chiamata è in attesa.
Quindi NON può esserci alcun problema di interferenze.

Peraltro, se disabiliti l'interrupt di Timer0 NON fermi per niente il conteggio del timer e neppure eviti che il flag IF vada a 1 all'overflow. Timer0 NON si può spegnere. Timer1 e Timer2 si, ma Timer0 proprio NO.

Se accendi e spegni flag durante la gestione dell'interrupt e per caso la periferica ha attivato una interruzione, all'uscita, , se non hai cancellato anche il flag IF, ti ritrovi immediatamente dentro di nuovo, con qualche possibile problemino di gestione.

Per venirne a capo, usa MPLAB-SIM o ancora meglio un micro con ICD e in una oretta hai risolto il problema.
picmicro675




una ogni 10 livelli


postato il:
10.06.2018, alle ore 21:19
marsram:
Se non usi un flow chart, che non richiede alcuna comprensione particolare

Su questo convengo che devo prenderne atto. Il mio metodo si basa su try&fail, fino che trovo il difetto.
marsram:
Nei Midrange NON esiste la possibilità di un interrupt di interromperne un altro.

Infatti, solo che se devo guidare una sequenza di operazioni, quindi gli altri interrupt non devono riempirmi l' ISR di richieste inutili. Come infatti dici, finito un retfie mi ritrovo magari un altro interrupt dall' esterno che non deve essere preso in considerazione.

marsram:
Quindi NON può esserci alcun problema di interferenze.

Questo è ovvio che durante un ciclo di ISR non ci sono altri interrupt. Ma appare che appena si completa il ciclo rischio di ritornarci subito.

marsram:
neppure eviti che il flag IF vada a 1 all'overflow. Timer0 NON si può spegnere.

Comprendo che non si fermi, comunque posso escluderlo dal causare un interrupt.

marsram:
Se accendi e spegni flag durante la gestione dell'interrupt e per caso la periferica ha attivato una interruzione, all'uscita, , se non hai cancellato anche il flag IF, ti ritrovi immediatamente dentro di nuovo, con qualche possibile problemino di gestione.

Come sto cercando di gestire, per non essere sovraccaricato da interruts. Specialmente se non servono.

marsram:
Per venirne a capo, usa MPLAB-SIM


Come forse citato in altro argomento, ho un certo numero di questi micro e quelli con il debug non ho ancora preso una decisione.
Trovo tal programma scomodo da usare. In particolar modo quando si deve lavorare con stimoli di natura sequenziale, che molto probabilmente richiede un certo apprendimento nella programmazione del simulatore.

Non penso che un simulatore come Proteus faccia di meno. Come pure si possa anche provare con un Real Pic simulator, con qualche difficoltà anche lì nel capire come si possa usare la comunicazione seriale e poter leggere il disassemblato, che è privo ormai di tutto quello che potrebbe dare un coff.

Comunque farò degli esperimenti anche con MPLAB-SIM, che pare che con MPLABX ci sia anche una possibilità di interfacciare un terminale con il porgramma per dare gli stimoli che avrei bisogno di provare.



Anno nuovo, forum nuovo.
Mi sa che lascio.
picmicro675




una ogni 10 livelli


postato il:
10.06.2018, alle ore 21:25
Ad ogni modo, se qualcuno volesse provarlo, con il simulatore che sa usare meglio, io ho messo a disposizione il necessario. Con l' eventuale possibilità di variare anche le cose che si ritengono sbagliate.



Anno nuovo, forum nuovo.
Mi sa che lascio.
picmicro675




una ogni 10 livelli


postato il:
12.06.2018, alle ore 13:51
Nelle ultime prove, togliendo tutti gli interrpts, meno quello del Timer1, ancora non si vede il segnale sul piedino dedicato. Direi che sebbene GIE e TMR1IE abilitati non avviene un interrupt da arrivare in ISR.


Anno nuovo, forum nuovo.
Mi sa che lascio.
marsram




una ogni 100 livelli
una ogni 10 livelli


postato il:
12.06.2018, alle ore 18:04
Insisto che simulatori come Proteus e simili NON SERVONO A NIENTE. Non sono mai serviti a niente e mai a niente serviranno. Forse sul'analogica o piccole logiche, più per didattica che altro.
Ti dovrebbe essere chiaro, ormai: se nel proteus va e nell'hardware no, vuol semplicemente dire che la risposta di proteus è inaffidabile e inutile.

Infatti, provato, l'int di Timer1 funge.

MPLAB. Va bene anche l'IDE 8.92 senza passare a MPLABX che è meno immediato.

- Crea un project con il wizard di MPLAB
- seleziona MPSIM come simulatore
- modifica il sorgente in modo da avere solo l'interrupt di Timer1
io ho provato con queste poche modifiche
goto ISR_NXT
btfss INTCON,T0IE ; timer0 interrupt enabled?

Put232 btfsc TXFLAG ; busy?
goto Put232 ; yes, branch and wait
; bsf INTCON,T0IE ; start timer0, new transmission

- fallo compilare con Make in assoluto
- metti un breakpoint all'inizio dell routine di interrupt

Se vai Step Into vai passo passo e vedi cosa fanno le singole istruzioni.
Però puoi lanciare l'esecuzione con Run.
L'esecuzione si ferma al break point, ovvero Timer1 genera una richiesta di interruzione.

Ugualmente funge se attivi anche il Timer0.

Ora occorre andare Step Into per vedere passo passo cosa fa il programma per la trasmissione e se è corretto il procedimento logico.

Come aiuto accendi con View la finestra Special Function Register e quella Program Memory e controlli che le cose giuste vadano nei giusti registri al giusto momento.
Fatto questo, potrai provare la ricezione.

Non dovrebbe richiedere neppure un eccesso di tempo.
picmicro675




una ogni 10 livelli


postato il:
12.06.2018, alle ore 19:57
Vediamo le cose in sincrono. Ho provato con Proteus e con Real Pic Sim, sicuramente sto facendo la cosa sbagliata.
Avviato MPLAB IDE 8.60
1) Ricopiato il sorgente di cui sopra con le correzioni date dal caso che grix storpia.
2) Commentato dalla riga 68 alla 120 inclusa e ricompilato
3) Messo un breakpoint alla linea 129, dato il RUN e funziona arrivando alla detta linea.
4) Fatto una prova a caricare il sorgente come sopra descritto ma non c'è segnale sul GP4, come sarebbe da aspettarsi. Infatti non ho valutato che il _Print è bloccante. Commentato la linea 225 e ricompilato. I breakpoint arrivano alla linea 233 e alla 236. Ora lo carico nel micro

5) caricato, ma non appare nessun segnale sul piedino 3.






6) ho provato anche a caricare l' hex su due micro. Comincio ad avere dei sospetti su questi due micro (forse troppo bistrattati
7) Ho caricato l' hex su un terzo micro vergine e il lampeggio avviene

Credo che farò ancora qualche altra prova domani. Forse ho scoperto la causa principale.



Anno nuovo, forum nuovo.
Mi sa che lascio.
agric





postato il:
13.06.2018, alle ore 11:31

marsram:
Timer0 NON si può spegnere.

Non lo si può spegnere Ma lo si può fermare.
Utilizzatndo il flag per selezionare il clk di sistema o la modalità count da esterno.
In count su pin esterno il conteggio del timer0 si ferma
Tra l'altro se il pin esterno è usato in analogico non ci sono problemi io ho utilizzato questo sistema per tenere fermo o e far continuare il conteggio del timer nel cronometro utilizzato per spiegare liinterrupt in un mio vecchio tutorial



meglio essere un granello di pepe che una cacca d'asino
segui questo thread con grixFC, per questa funzione devi aver installato il software grixFC

torna su
     

Come utente anonimo puoi leggere il contenuto di questo forum ma per aprire una discussione
o per partecipare ad una discussione esistente devi essere registrato ed accedere al sito




 







 
 
indietro | homepage | torna su copyright © 2004/2024 GRIX.IT - La community dell'elettronica Amatoriale