Author Topic: Radio tag to send configuration data to microcontroller  (Read 3176 times)

0 Members and 1 Guest are viewing this topic.

Offline ocsetTopic starter

  • Super Contributor
  • ***
  • Posts: 1516
  • Country: 00
Radio tag to send configuration data to microcontroller
« on: August 07, 2018, 02:32:23 pm »
Hello,
We wish to configure our offline LED lamps  at the end of the production stage. We therefore wish to put a M24LR64-R radio tag on  the PCB, and connect it to our PIC16F18856 micro  that's on the Lamp PCB.
We can then transmit to this radio chip and “tell” the lamp what power its supposed to run at, etc etc....
The problem is the datasheet doesn’t give a recommended footprint for the PCB antenna that is needed. Do you know of one? Also, what do we use to actually transmit data to the antenna of the chip?
Presumably  the M24LR64-R needs to have its  clock and data lines  connected to the TX/RX pins of the micro? Also, how does the micro know when we have loaded the M24LR64 with the data?.....i mean, when we power  up the lamp,  and haven’t yet sent the configuration data to the M24LR64-R, then how does the micro know that it must wait for us to send the data?
What if we send the wrong data to the M24LR64, and then wish  to re-send the data...how will the micro  know that we want it to re-read the M24LR64? (and scrap the first lot of data)

M24LR64-R datasheet
https://www.st.com/resource/en/datasheet/m24lr64-r.pdf
« Last Edit: August 07, 2018, 02:37:19 pm by treez »
 

Offline tsman

  • Frequent Contributor
  • **
  • Posts: 599
  • Country: gb
Re: Radio tag to send configuration data to microcontroller
« Reply #1 on: August 07, 2018, 04:08:23 pm »
Uh what? Putting in a M24LR64-R just to load in the factory data is massive overkill especially since they're far more expensive than just a small connector. You're making this overly complicated for no reason. Program the boards using a connector or pogo pins onto test points before you put it into the final unit. If you don't know what boards go into what lamps before final assembly then you need to work on your procedures.

Also, what do we use to actually transmit data to the antenna of the chip?
Buy a compatible industrial RFID reader/writer that is compatible.

Presumably  the M24LR64-R needs to have its  clock and data lines  connected to the TX/RX pins of the micro?
It uses I2C. Not the UART TX/RX pins. Look up how to configure and connect the I2C peripheral in your PIC.

Also, how does the micro know when we have loaded the M24LR64 with the data?
Checksum + magic value to say the data is valid. If it isn't valid then just do nothing. Power cycle to make it reread the configuration data.

.....i mean, when we power  up the lamp,  and haven’t yet sent the configuration data to the M24LR64-R, then how does the micro know that it must wait for us to send the data?
The contents won't be valid so make your code detect that.

What if we send the wrong data to the M24LR64, and then wish  to re-send the data...how will the micro  know that we want it to re-read the M24LR64? (and scrap the first lot of data)
You can overwrite the data using your RFID reader and then powercycle your lamp. If the mistakenly written data is so wrong that it causes damage then your lamp has already blown up anyway so you can afford to only read the data at the start.
 
The following users thanked this post: ocset

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Radio tag to send configuration data to microcontroller
« Reply #2 on: August 07, 2018, 05:00:38 pm »
I’ve seen a product with a chip like this before. It’s genuis.
The product was a Caelo Gavazzi three phase voltage frequency relay that you can configure witg NFC.
 
The best thing is that you can configure it, inside the box when it’s off!
They also have a model that supports live data, but I have not torn down that unit to check it’s contents.

ST must have a reference board with an antenna, right? Else there will be tools available.
 
The following users thanked this post: ocset

Offline ocsetTopic starter

  • Super Contributor
  • ***
  • Posts: 1516
  • Country: 00
Re: Radio tag to send configuration data to microcontroller
« Reply #3 on: September 24, 2018, 10:44:23 am »
Thanks,
Thanks,  the I2C pin sof the PIC16F18856 have to be used to connect to the M24LR64-R. (as tsman kindly said)
Do you know which ones these are?

PIC16F18856 datasheet
http://ww1.microchip.com/downloads/e...-40001824D.pdf

- - - Updated - - -

..i take it is is pins 19 and 20 of the PIC16F18856 (28PIN QFN)?
« Last Edit: September 24, 2018, 10:55:02 am by treez »
 

Online JPortici

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Re: Radio tag to send configuration data to microcontroller
« Reply #4 on: September 24, 2018, 11:28:27 am »
Correct, but take note of this:
http://ww1.microchip.com/downloads/en/DeviceDoc/PIC16LF18856-76-Data-Sheet-40001824D.pdf page 9

Quote
3:This is a bidirectional signal. For normal module operation, the firmware should map this signal to the same pin in both the PPS input and PPS output registers.
4:These pins are configured for I2C logic levels.; The SCLx/SDAx signals may be assigned to any of the RB1/RB2/RC3/RC4 pins. PPS assignments to the other pins (e.g., RA5) will operate, but input logic levels will be standard TL/ST, as selected by the INLVL register, instead of the I2C specific or SMbus input buffer thresholds

3) means that you can remap the pin position, if the default one is inconvenient to you BUT you have to assign the pin to both the PPS IN and PPS OUT
4) means that while you can remap the position to ANY one of the pins, only the few in the parenthesis have the I/O circuitry necessary to ensure proper operation, and you also have to select the I/O thresholds for I2C

oh by the way, that memory chip is very interesting, too bad it's listed as obsolete at digikey. any replacement available?
 
The following users thanked this post: ocset

Offline dgtl

  • Regular Contributor
  • *
  • Posts: 183
  • Country: ee
Re: Radio tag to send configuration data to microcontroller
« Reply #5 on: September 24, 2018, 02:16:33 pm »
The problem is the datasheet doesn’t give a recommended footprint for the PCB antenna that is needed. Do you know of one?
There's an appnote for that. AN2972. Or just take the layout of the evaluation kit and copy that. As long as you're not doing anything stupid (metal layer over or around antenna) and not looking for the maximal range, it's quite forgiving. You may want to add trimmer capacitors for prototypes to tune the antenna for maximum reader range and then replace them later with fixed capacitors.

Also, what do we use to actually transmit data to the antenna of the chip?
Any ISO15693 capable RFID reader. Even Android phones these days are capable. There are plenty of USB readers. Consider the thing as ISO15693 memory tag and just program the memory.

Presumably  the M24LR64-R needs to have its  clock and data lines  connected to the TX/RX pins of the micro? Also, how does the micro know when we have loaded the M24LR64 with the data?.....i mean, when we power  up the lamp,  and haven’t yet sent the configuration data to the M24LR64-R, then how does the micro know that it must wait for us to send the data?
What if we send the wrong data to the M24LR64, and then wish  to re-send the data...how will the micro  know that we want it to re-read the M24LR64? (and scrap the first lot of data)
Use one bit in the data as busy-ready bit: write it to 1 when updating and 0 when completed (or the other way around, depends on the memory default state). The uc software shall wait until this bit is ready.
When updating the config, first set this bit to busy, then program your data and finally set it back to ready.

For detecing update, there's the WIP/BUSY pin. Wait until it indicates a write and then re-read the data. Perhaps monitor the pin during read so that you don't read while write is in progress and you get partially updated data.
 
The following users thanked this post: ocset

Offline In Vacuo Veritas

  • Frequent Contributor
  • **
  • !
  • Posts: 320
  • Country: ca
  • I like vacuum tubes. Electrons exist, holes don't.
Re: Radio tag to send configuration data to microcontroller
« Reply #6 on: September 24, 2018, 02:49:07 pm »
Why don't you put a cell phone in each lamp? Then when you inevitably make another mistake or brain-dead design decision, you can text the firmware update to each lamp as a series of emojis.
 
The following users thanked this post: JVR, ocset

Offline ocsetTopic starter

  • Super Contributor
  • ***
  • Posts: 1516
  • Country: 00
Re: Radio tag to send configuration data to microcontroller
« Reply #7 on: September 24, 2018, 07:34:28 pm »
Thanks, Yes i see its gone obselete, so we'll go for the sister chip M24LR64E-R now.

Considering the antenna that we have to put in with it, page 13 of AN2972 provides an equation whereby the inductance of a “square spiral” antenna can be calculated…however, the equation  on  pg 13 doesn’t   include  many of the necessary parameters, eg, the coil  trace thickness, the spacing between traces, the permeability of FR4, etc etc. Therefore, the “eDesignSuite antenna design tool”  from st.com has to be used….

..However, when I use this tool, I put  in 10 turns, and it draws only 5 turns. Why is this?
..ok ive tried it again, and its now ok
In fact, when I put in 10 turns without having filled in any other parameters, it does then actually draw 10 turns, but its when i add the length and width  parameters  etc that it  then halves the number of turns that I put in.
..ok ive tried it again, and its now ok

..So my question is, ..is this tool reliable?…and how can I calculate the antenna inductance if this tool is not reliable?

M24LR64E-R datasheet:
https://www.st.com/resource/en/datasheet/m24lr64e-r.pdf

AN2972:  App Note on making antenna for RF chips like M24LR64E-R
https://www.st.com/content/ccc/resource/technical/document/application_note/bc/ac/13/fe/69/fb/49/8a/CD00232630.pdf/files/CD00232630.pdf/jcr:content/translations/en.CD00232630.pdf

eDesignSuite antenna design tool:
https://my.st.com/analogsimulator/html/#/home
« Last Edit: September 24, 2018, 07:51:13 pm by treez »
 

Offline matseng

  • Frequent Contributor
  • **
  • Posts: 563
  • Country: se
    • My Github
Re: Radio tag to send configuration data to microcontroller
« Reply #8 on: September 24, 2018, 10:18:35 pm »
In NFC the exact inductance of the antenna is not really important. Usually they are designed to end up between 1 and 4uH and then you measure the exact values of L and R for the antenna to calculate the values for the impedance matcher and that is put between the antenna and the tranceiever chip.  This is done with a VNA that today can be had for just $500 for a unit that is good enough to tune a NFC design.

RFSIM99 is a good companion tool for analysing and calculating the matching network.

I went from zero knowledge in the area to having designed and successully tuned two different NFC projects in about 6 weeks. It's not that hard , and there's plenty of app notes and nice video tutorials covering this subject.
 
The following users thanked this post: ocset

Offline ocsetTopic starter

  • Super Contributor
  • ***
  • Posts: 1516
  • Country: 00
Re: Radio tag to send configuration data to microcontroller
« Reply #9 on: September 25, 2018, 07:27:22 am »
Thanks,
At the moment we wish to look into the the M24LR64, M24SR64 or ST25DV-I2C or ST25DV-PWM.
We are trying to assess what is the difference between these chips. All we want to do is throw data in with RF, also preferably have the option of doing this with the chip not actually powered. And obviously to read the data with the micro.

The M24LR64 is a “long range” chip, and the M24SR64 is a “ short range” chip, but the datasheets don’t say what this actual range is in metric units.
I wondered if anyone knows?

M24SR64 datasheet:
https://www.st.com/resource/en/datasheet/m24sr04-y.pdf

M24LR64E datasheet
https://www.st.com/resource/en/datasheet/m24lr64e-r.pdf
 

Offline dgtl

  • Regular Contributor
  • *
  • Posts: 183
  • Country: ee
Re: Radio tag to send configuration data to microcontroller
« Reply #10 on: September 25, 2018, 07:57:34 am »
The long range and short range correspond to different radio protocols: ISO15693 (==NFC type 5) and ISO14443A (NFC type 4). The range depends on the protocol, reader (+antenna) and the antenna of the ic. One standard of those has longer range, the other has higher transmission speed. The short range (proximity card) is about up to 10cm, long range (vicinity card) up to 1m (but only with a very large reader antenna). With regular credit card size reader and not perfectly tuned antennas you can expect around 5cm and 10cm usually. This depends heavily on reader used; ie usually cell phones are horrible readers due their antenna squashed there on top of everything else.

ST has their evaluation boards, that are very cheap (~10$). Get some and try writing them with Android phones, there are apps that can write data to tags.
 
The following users thanked this post: ocset

Online Howardlong

  • Super Contributor
  • ***
  • Posts: 5319
  • Country: gb
Re: Radio tag to send configuration data to microcontroller
« Reply #11 on: September 25, 2018, 08:34:33 am »
I have done RFID solutions directly on PIC16 as a reader and PIC10F322 as a transponder, with no need for any third party devices.

For the reader, I've done it on a PIC16F1718 using either the on chip opamps or the on chip zero crossing detector, and more recently on the PIC16F15324 using the zero crossing detector. All three require a half dozen or so discrete parts each, with the ZCD versions being the simplest and most reliable solution. These are all 125kHz solutions based on the EM4102 standard.

I tried a number of antennas, and the large coils with a tuning capacitor are certainly the most efficient. You can use a surface mount wire wound and cap, but the range is nowhere near as good.

I attach example code. The PIC16F1718 code examples were in single .c files so I've embedded it in the post in clear text also. The PIC10F322 transponder had some time critical stuff so I had to resort to assembly language for some of that, I've included both source files as a zip. The PIC16F15324 version I split into several source files so I've attached as a zip archive also.

The PIC10F322 transponder is powered by the RFID Reader's emitted energy.

PIC161718 OpAmp reader code:

Code: [Select]

// PIC16F1718 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = ON    // Clock Out Enable (CLKOUT function is enabled on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover Mode (Internal/External Switchover Mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PPS1WAY = OFF    // Peripheral Pin Select one-way control (The PPSLOCK bit can be set and cleared repeatedly by software)
#pragma config ZCDDIS = ON      // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR and can be enabled with ZCDSEN bit.)
#pragma config PLLEN = ON       // Phase Lock Loop enable (4x PLL is always enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

// Howard Long 12 May 2018
// Implements an EM4102 125kHz RFID reader
//
// Runs at Fosc=32MHz, Fcy=8MHz, from INTOSC+PLL
//
// Diagnostics/debugging pins
// MCLR 10k to Vdd
// RB7 ICSPDAT
// RB6 ICSPCLK
// RA6 has processor Fcy (CLKOUT))
//
// Implementation pins
// RC2 CCP1 PWM output, 125kHz: TMR2 & CCP1 implement a 125kHz 50% PWM
// RA5 OPA1IN- Opamp feedback, 1Mohm to OPA1OUT, 1k to Vss
// RA4 OPA1IN+ From AM envelope detector
// RA1 OPA1OUT This should saturate, tied to RA0 digital input pin
// RA0 Input from opamp amplifier, Schmitt input
//
// o EM4102 tags modulate using ASK by loading the coupled antenna coil in the near field.
// o The 125kHz carrier is very large in comparison to the detected loading.
// o Without complex DSP, it's very simple to demodulate using an AM envelope detector (i.e., diode and RC low pass filter).
// o The bit rate is 125kHz/64, or a period of 512us, so the RC low pass filter on the envelope detector is appropriately set.
// o The envelope detector output will a large DC component, so is AC coupled to the next stage.
//
// o The signal is then referenced to Vdd/2 and limited using back to back diodes.
//
// o Key to the EM4102 model is that the coding on the signal data has no DC residual component, achieved using Manchester encoding.
// o A zero is represented as a falling edge in the middle of the bit time, and a one as a rising edge.
// o The Manchester decoder in this reader is done using a state machine that switches between edge detection and timer inside an ISR.
// o The Manchester decoder algorithm is the wait for and edge fall or rise, then wait for 0.75 bit time (384us) and sample.
// o The decoder algorithm is based on the algorithm in Microchip AN1470, but in software using a timer and IOC.
//
// o The EM4102 protocol present a frame of 64 bits in a total of four phases.
// o - Header: 9 bits, all one;
// o - Data: 10 data fields, each including 5 bits, (four data and one parity bit);
// o - Column Parity: four bits, compared to the bit-wise accumulated four bit parities of the previous ten data fields;
// o - Stop: one bit, zero.
//
// o The EM4102 protocol is also achieved using a state machine within the ISR, but separate to the Manchester decoder,
// o If any problem occurs at any point in the protocol reception, for example no header, or a bad parity, the entire state machine is reset
//
// Two possible improvement I can think of right now:

// o Use a comparator rather than an opamp, there are more cheaper PICs out there in smaller packages that would support this;
// o In the Manchester decoder, check that transitions occur when expected in a given window;
// o Again in the Manchester decoder, check that there was only one transition in the timer ISR part.

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#define FOSC 32000000
#define UART_BAUDRATE 9600

static volatile enum // State machine
{
    SME_IDLE=0,
    SME_HEADER,
    SME_DATA,
    SME_COLUMNPARITY,
    SME_STOP,
    SME_WAIT // Wait for superloop to process
} _sme=SME_IDLE;

static uint8_t _au8Data[10]; //

void interrupt ISR(void)
{
    // There are two states for the Manchester decoder, either we are waiting for an Interrupt on Change (IOC)
    // or we are waiting for the timer TM4, neither should be simultaneously active.
   
    if (INTCONbits.IOCIE && INTCONbits.IOCIF) // IOC?
    {
        // We've had a transition, so sample the pin, start the timer 384us timer, and disable IOC
        TMR4=0;
        PIR2bits.TMR4IF=0;
        PIE2bits.TMR4IE=1;
        T4CONbits.TMR4ON=1;
       
        IOCAFbits.IOCAF0=0;
        INTCONbits.IOCIE=0;
        INTCONbits.IOCIF=0;
    }
    if (PIE2bits.TMR4IE && PIR2bits.TMR4IF) // TMR4?
    {
        // We're at 384us now, so let's disable the timer, grab the bit, and re-enable IOC
        static bit b; // This is not stateful, but you can't define a bit in XC8 unless it's static or global
       
        // The rest of the local variables must maintain state outside of the ISR for the next time
        static uint8_t u8ColumnParity=0;
        static uint8_t u8BitCount=0;
        static uint8_t *pu8Data=_au8Data;
        static uint8_t u8Accumulator=0;
        static uint8_t u8RowParity=0;
       
        b=PORTAbits.RA0; // Capture the bit for later
       
        // Diagnostic GPIO twiddle
        LATCbits.LATC1=1;
        LATCbits.LATC0=(uint8_t)b;
               
        // Disable the 384us timer
        PIR2bits.TMR4IF=0;
        PIE2bits.TMR4IE=0;
        T4CONbits.TMR4ON=0;
       
        // Re-enable the IOC
        IOCAFbits.IOCAF0=0;
        INTCONbits.IOCIF=0;
        INTCONbits.IOCIE=1;
       
        // This is the EM4102 Protocol decoder
        switch (_sme)
        {
            case SME_IDLE:
                if (b)
                {
                    u8BitCount=8;
                    _sme=SME_HEADER;
                }
                break;
            case SME_HEADER:
                if (b)
                {
                    u8BitCount--;
                    if (u8BitCount==0)
                    {
                        u8RowParity=0;
                        u8ColumnParity=0;
                        u8Accumulator=0;
                        u8BitCount=5;
                        pu8Data=_au8Data;
                        _sme=SME_DATA;
                    }
                }
                else
                {
                    _sme=SME_IDLE; // Give up, not enough 1's
                }
                break;
            case SME_DATA:
                u8BitCount--;
                if (b)
                {
                    u8RowParity^=1;
                }
                if (u8BitCount==0)
                {
                    if (u8RowParity)
                    {
                        _sme=SME_IDLE; // Give up, parity is wrong
                    }
                    else
                    {
                        u8ColumnParity^=u8Accumulator; // Update the column parity bits
                        *pu8Data++=u8Accumulator; // Store the data
                        if (pu8Data-_au8Data<10) // Are we there yet?
                        {
                            u8Accumulator=0;
                            u8RowParity=0;
                            u8BitCount=5;
                        }
                        else
                        {
                            u8Accumulator=0;
                            u8BitCount=4;
                            _sme=SME_COLUMNPARITY;
                        }
                    }
                }
                else
                {
                    u8Accumulator<<=1;
                    if (b)
                    {
                        u8Accumulator++;
                    }
                }
                break;
            case SME_COLUMNPARITY:
                u8BitCount--;
                u8Accumulator<<=1;
                if (b)
                {
                    u8Accumulator++;
                }
                if (u8BitCount==0)
                {
                    if (u8Accumulator!=u8ColumnParity)
                    {
                        _sme=SME_IDLE; // Give up, column parity is wrong
                    }
                    else
                    {
                        _sme=SME_STOP;
                    }
                }
                break;
            case SME_STOP:
                if (!b)
                {
                    // Got it!
                    _sme=SME_WAIT; // Let superloop do its thing
                }
                else
                {
                    _sme=SME_IDLE; // Give up, no stop bit
                }
                break;
            case SME_WAIT: // Main superloop has not yet processed the last data
                break;
            default: // Should never get here
                break;
        }
        LATCbits.LATC1=0;       
    }
}

void putch(char c)
{
    while (!TXSTAbits.TRMT)
    {
        NOP();
    }
    TXREG=c;
}

int main(void)
{
    OSCCONbits.IRCF=0b1110; // 0b1110 = 8MHz (32MHz w/PLL)
   
    TRISA=0;
    TRISB=0;
    TRISC=0;
    ANSELA=0;
    ANSELB=0;
    ANSELC=0;
   
    // Set up opamp 1
    TRISAbits.TRISA1=1; // OPA1OUT
    ANSELAbits.ANSA1=1;
    TRISAbits.TRISA4=1; // OPA1IN+
    ANSELAbits.ANSA4=1;
    TRISAbits.TRISA5=1; // OPA1IN-
    ANSELAbits.ANSA5=1;
   
    OPA1CONbits.OPA1UG=0; // 0 => OPA1IN- pin
    OPA1CONbits.OPA1PCH=0b00; // 0b00 => OPA1IN+ pin
    OPA1CONbits.OPA1SP=1; // 1 => fast GBWP
    OPA1CONbits.OPA1EN=1;
   
    // Setup UART
    RC5PPS=0b10100; // 0b10100 => TX/CK
    TRISCbits.TRISC5=0;
    {
        const uint16_t u16=(uint16_t)(((double)FOSC)/UART_BAUDRATE/16-0.5);
       
        SPBRGH=u16>>8;
        SPBRGL=(uint8_t)u16;
    }
    TXSTAbits.BRGH=1;
    TXSTAbits.SYNC=0;
    RCSTAbits.SPEN=1;
    TXSTAbits.TXEN=1;
   
    // Setup 125kHz coil square wave
    RC2PPSbits.RC2PPS=0b01100; // 0b01100 => CCP1
    T2CON=0;
    CCP1CON=0;
    PR2=63;
    TMR2=0;
    CCP1CONbits.CCP1M=0b1100; // 0b1100 => PWM mode
    CCPR1L=32;
    CCP1CONbits.DC1B=0;
    PIR1bits.TMR2IF=0;
    PIE1bits.TMR2IE=0;
    T2CONbits.TMR2ON=1;
   
    // Prepare a timer for timing manchester coding
    // Complete bit time is 512us
    // We sample at 3/4 bit time, 384us.
    // Use prescaler div-by-64, and time to a 48 step period (div-by-3072, 8MHz Tcy/3072 = 384us)
    T4CON=0;
    T4CONbits.T4CKPS=0b11; // 0b11 => div-by-64 prescaler
    TMR4=0;
    PR4=47;
   
    // Interrupt on change
    TRISAbits.TRISA0=1; // Externally, RA0 is tied to RA1 (OPA1OUI)
    IOCAPbits.IOCAP0=1;
    IOCANbits.IOCAN0=1;
    IOCAFbits.IOCAF0=0;
    INTCONbits.IOCIE=1;
    INTCONbits.PEIE=1;
    INTCONbits.GIE=1;
   
    while (1)
    {
        if (_sme==SME_WAIT)
        {
            static uint8_t au8Data2[10];
            const char acHex[]="0123456789ABCDEF";
            int i;
           
            NOP();
            memcpy(au8Data2,_au8Data,sizeof(au8Data2)); // Grab a copy ASAP to allow another grab
            _sme=SME_IDLE;
            putch('\r');
            putch('\n');
            for (i=0;i<10;i++)
            {
                uint8_t u8=au8Data2[(uint8_t)i];
                char c=acHex[u8];
                putch(c);
            }
        }
    }
}



PIC16F1718 ZCD reader code:

Code: [Select]

// PIC16F1718 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = ON    // Clock Out Enable (CLKOUT function is enabled on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover Mode (Internal/External Switchover Mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PPS1WAY = OFF    // Peripheral Pin Select one-way control (The PPSLOCK bit can be set and cleared repeatedly by software)
#pragma config ZCDDIS = ON      // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR and can be enabled with ZCDSEN bit.)
#pragma config PLLEN = ON       // Phase Lock Loop enable (4x PLL is always enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

// Howard Long 12 May 2018
// Implements an EM4102 125kHz RFID reader
//
// Runs at Fosc=32MHz, Fcy=8MHz, from INTOSC+PLL, but pretty sure this is overkill
//
// Diagnostics/debugging pins
// MCLR 10k to Vdd
// RB7 ICSPDAT
// RB6 ICSPCLK
// RA6 has processor Fcy (CLKOUT))
// RA7 is CLC1OUT brings out ZCDOUT
//
// Implementation pins
// RC2 CCP1 PWM output, 125kHz: TMR2 & CCP1 implement a 125kHz 50% PWM
// RB0 is ZCD input
// RC5 is UART TX out
//
// o EM4102 tags modulate using ASK by loading the coupled antenna coil in the near field.
// o The 125kHz carrier has very large amplitude in comparison to the loading modulation (i.e., a very low modulation index, 5% or less).
// o Without complex DSP, it's very simple to demodulate using an AM envelope detector (i.e., diode and RC low pass filter).
// o The bit rate is 125kHz/64, or a period of 512us, so the RC low pass filter on the envelope detector is appropriately set.
// o The envelope detector output will a large DC component, so is AC coupled to the next stage.
//
// o The signal is then referenced to Vdd/2 and limited using back to back diodes.
//
// o Key to the EM4102 model is that the coding on the signal data has no DC residual component, achieved using Manchester encoding.
// o A zero is represented as a falling edge in the middle of the bit time, and a one as a rising edge.
// o The Manchester decoder in this reader is done using a state machine that switches between edge detection and timer inside an ISR.
// o The Manchester decoder algorithm is the wait for and edge fall or rise, then wait for 0.75 bit time (384us) and sample.
// o The decoder algorithm is based on the algorithm in Microchip AN1470, but in software using a timer and zero crossing detector.
// o The zero crossing detector is quite handy: it simplifies the work between the envelope detector and the PIC.
// o The ZCD provides self DC bias to bring us back into PIC's voltage domains, and acts as a comparator.
//
// o The EM4102 protocol presents a frame of 64 bits in a total of four phases:
// o - Header: 9 bits, all one;
// o - Data: 10 data fields, each including 5 bits, (four data and one parity bit);
// o - Column Parity: four bits, compared to the bit-wise accumulated four bit parities of the previous ten data fields;
// o - Stop: one bit, zero.
// o The EM4102 protocol is also achieved using a state machine within the ISR, but separate to the Manchester decoder.
// o There are also IDLE and WAIT phases added to the decoder state machine to provide synchronisation between the ISR and the superloop.
// o If any problem occurs at any point in the protocol reception, for example no header, or a bad parity, the entire state machine is reset.
//
// Two possible improvements I can think of right now:
//
// o In the Manchester decoder, check that transitions occur when expected in a given window;
// o Again in the Manchester decoder, check that there was only one transition in the timer ISR part.

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#define FOSC 32000000
#define UART_BAUDRATE 9600

static volatile enum // State machine
{
    SME_IDLE=0,
    SME_HEADER,
    SME_DATA,
    SME_COLUMNPARITY,
    SME_STOP,
    SME_WAIT // Wait for superloop to process
} _sme=SME_IDLE;

static uint8_t _au8Data[10]; //

void interrupt ISR(void)
{
    // There are two states for the Manchester decoder, in this ISR.
    // Either we are waiting for a zero crossing (ZCD)
    // or we are waiting for the timer TM4, neither should be simultaneously active.
    if (PIE3bits.ZCDIE && PIR3bits.ZCDIF)
    {
        // We've had a transition, so sample the pin, start the timer 384us timer, and disable ZCD for now
        TMR4=0;
        PIR2bits.TMR4IF=0;
        PIE2bits.TMR4IE=1;
        T4CONbits.TMR4ON=1;

        PIE3bits.ZCDIE=0;
        PIR3bits.ZCDIF=0;
    }
   
    if (PIE2bits.TMR4IE && PIR2bits.TMR4IF) // TMR4?
    {
        // We're at 384us now, so let's disable the timer, grab the bit, and re-enable ZCD
        static bit b; // This is not stateful, but you can't define a bit in XC8 unless it's static or global
       
        // The rest of the local variables must maintain state outside of the ISR for the next time
        static uint8_t u8ColumnParity=0;
        static uint8_t u8BitCount=0;
        static uint8_t *pu8Data=_au8Data;
        static uint8_t u8Accumulator=0;
        static uint8_t u8RowParity=0;
       
//        b=PORTAbits.RA0; // Capture the bit for later
        b=ZCD1CONbits.ZCD1OUT;
       
        // Diagnostic GPIO twiddle
        LATCbits.LATC1=1;
        LATCbits.LATC0=(uint8_t)b;
               
        // Disable the 384us timer
        PIR2bits.TMR4IF=0;
        PIE2bits.TMR4IE=0;
        T4CONbits.TMR4ON=0;
       
        // Re-enable the ZCD
        PIR3bits.ZCDIF=0;
        PIE3bits.ZCDIE=1;
       
        // This is the EM4102 Protocol decoder
        switch (_sme)
        {
            case SME_IDLE:
                if (b)
                {
                    u8BitCount=8;
                    _sme=SME_HEADER;
                }
                break;
            case SME_HEADER:
                if (b)
                {
                    u8BitCount--;
                    if (u8BitCount==0)
                    {
                        u8RowParity=0;
                        u8ColumnParity=0;
                        u8Accumulator=0;
                        u8BitCount=5;
                        pu8Data=_au8Data;
                        _sme=SME_DATA;
                    }
                }
                else
                {
                    _sme=SME_IDLE; // Give up, not enough 1's
                }
                break;
            case SME_DATA:
                u8BitCount--;
                if (b)
                {
                    u8RowParity^=1;
                }
                if (u8BitCount==0)
                {
                    if (u8RowParity)
                    {
                        _sme=SME_IDLE; // Give up, parity is wrong
                    }
                    else
                    {
                        u8ColumnParity^=u8Accumulator; // Update the column parity bits
                        *pu8Data++=u8Accumulator; // Store the data
                        if (pu8Data-_au8Data<10) // Are we there yet?
                        {
                            u8Accumulator=0;
                            u8RowParity=0;
                            u8BitCount=5;
                        }
                        else
                        {
                            u8Accumulator=0;
                            u8BitCount=4;
                            _sme=SME_COLUMNPARITY;
                        }
                    }
                }
                else
                {
                    u8Accumulator<<=1;
                    if (b)
                    {
                        u8Accumulator++;
                    }
                }
                break;
            case SME_COLUMNPARITY:
                u8BitCount--;
                u8Accumulator<<=1;
                if (b)
                {
                    u8Accumulator++;
                }
                if (u8BitCount==0)
                {
                    if (u8Accumulator!=u8ColumnParity)
                    {
                        _sme=SME_IDLE; // Give up, column parity is wrong
                    }
                    else
                    {
                        _sme=SME_STOP;
                    }
                }
                break;
            case SME_STOP:
                if (!b)
                {
                    // Got it!
                    _sme=SME_WAIT; // Let superloop do its thing
                }
                else
                {
                    _sme=SME_IDLE; // Give up, no stop bit
                }
                break;
            case SME_WAIT: // Main superloop has not yet processed the last data
                break;
            default: // Should never get here
                break;
        }
        LATCbits.LATC1=0;       
    }
   
}

void putch(char c)
{
    while (!TXSTAbits.TRMT)
    {
        NOP();
    }
    TXREG=c;
}

int main(void)
{
    OSCCONbits.IRCF=0b1110; // 0b1110 = 8MHz (32MHz w/PLL)
   
    TRISA=0;
    TRISB=0;
    TRISC=0;
    ANSELA=0;
    ANSELB=0;
    ANSELC=0;
   
    // Set up Zero crossing detector
    TRISBbits.TRISB0=1;
    ANSELBbits.ANSB0=1;
    ZCD1CON=0;
    ZCD1CONbits.ZCD1INTN=1;
    ZCD1CONbits.ZCD1INTP=1;
    PIR3bits.ZCDIF=0;
    PIE3bits.ZCDIE=1;
    ZCD1CONbits.ZCD1EN=1;
   
    // Configure CLC1OUT to show ZCDOUT for diagnostics
    RA7PPSbits.RA7PPS=0b00100;
    TRISAbits.TRISA7=0;
    CLC1CON=0;
    CLC1POL=0;
    CLC1SEL0=0;
    CLC1SEL1=0;
    CLC1SEL2=0;
    CLC1SEL3=0;
    CLC1GLS0=0;
    CLC1GLS1=0;
    CLC1GLS2=0;
    CLC1GLS3=0;   
    CLC1CONbits.MODE=0b010; // 0b010 => 4-input AND
    CLC1SEL0bits.LC1D1S=0b10011; // 0b10011 => ZCD1OUT
    CLC1POLbits.LC1POL=0;
    CLC1POLbits.LC1G1POL=0;
    CLC1POLbits.LC1G2POL=1;
    CLC1POLbits.LC1G3POL=1;
    CLC1POLbits.LC1G4POL=1;
    CLC1GLS0bits.LC1G1D1T=1;
    CLC1GLS0bits.LC1G1D1N=0;
    CLC1GLS0bits.LC1G1D2T=0;
    CLC1GLS0bits.LC1G1D2N=0;
    CLC1GLS0bits.LC1G1D3T=0;
    CLC1GLS0bits.LC1G1D3N=0;
    CLC1GLS0bits.LC1G1D4T=0;
    CLC1GLS0bits.LC1G1D4N=0;
    CLC1CONbits.EN=1;
   
    // Setup UART
    RC5PPS=0b10100; // 0b10100 => TX/CK
    TRISCbits.TRISC5=0;
    {
        const uint16_t u16=(uint16_t)(((double)FOSC)/UART_BAUDRATE/16-0.5);
       
        SPBRGH=u16>>8;
        SPBRGL=(uint8_t)u16;
    }
    TXSTAbits.BRGH=1;
    TXSTAbits.SYNC=0;
    RCSTAbits.SPEN=1;
    TXSTAbits.TXEN=1;
   
    // Setup 125kHz coil square wave
    RC2PPSbits.RC2PPS=0b01100; // 0b01100 => CCP1
    T2CON=0;
    CCP1CON=0;
    PR2=63;
    TMR2=0;
    CCP1CONbits.CCP1M=0b1100; // 0b1100 => PWM mode
    CCPR1L=32;
    CCP1CONbits.DC1B=0;
    PIR1bits.TMR2IF=0;
    PIE1bits.TMR2IE=0;
    T2CONbits.TMR2ON=1;
   
    // Prepare a timer for timing Manchester coding, but don't start it until needed after a transition is detected
    // Complete bit time is 512us
    // We sample at 3/4 bit time, 384us.
    // Use prescaler div-by-64, and time to a 48 step period (div-by-3072, 8MHz Tcy/3072 = 384us)
    T4CON=0;
    T4CONbits.T4CKPS=0b11; // 0b11 => div-by-64 prescaler
    TMR4=0;
    PR4=47;
   
    while (1)
    {
        if (_sme==SME_WAIT)
        {
            static uint8_t au8Data2[10];
            const char acHex[]="0123456789ABCDEF";
            int i;
           
            NOP();
            memcpy(au8Data2,_au8Data,sizeof(au8Data2)); // Grab a copy ASAP to allow another grab
            _sme=SME_IDLE;
            putch('\r');
            putch('\n');
            for (i=0;i<10;i++)
            {
                uint8_t u8=au8Data2[(uint8_t)i];
                char c=acHex[u8];
                putch(c);
            }
        }
    }
}



Edit: Some people may be wondering why I have apparently pointless NOPs in my code: functionally, they usually have no purpose (except for timing reasons where it's explicitly stated), they are usually there so I can place a convenient breakpoint. Occasionally I might place three or so adjacent NOPs, this is to deal with breakpoint skidding on hardware breakpoints.
« Last Edit: September 25, 2018, 09:27:20 am by Howardlong »
 
The following users thanked this post: ocset

Offline JVR

  • Regular Contributor
  • *
  • Posts: 201
  • Country: be
Re: Radio tag to send configuration data to microcontroller
« Reply #12 on: September 25, 2018, 10:15:40 am »
Having recently done something similar, be very sure that this is what you want to do. A connector or even PLC might be a better option in this case.

Loads of factors decide the range of communication, PCB area, copper pours, wiring etc. And you might end up with a RF range that does not exit your housing. Also, are you sure you need the XX64 version? the XX04 is much cheaper, and 4K is a ton of space to configure a LED lamp.
 
The following users thanked this post: ocset

Offline Simon

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Radio tag to send configuration data to microcontroller
« Reply #13 on: September 25, 2018, 11:50:31 am »
Why don't you put a cell phone in each lamp? Then when you inevitably make another mistake or brain-dead design decision, you can text the firmware update to each lamp as a series of emojis.

why don't you stop being an arrogant arse before it get's you banned. I have already had to clean up a mess you made of a thread!
 
The following users thanked this post: JPortici, ocset

Offline ocsetTopic starter

  • Super Contributor
  • ***
  • Posts: 1516
  • Country: 00
Re: Radio tag to send configuration data to microcontroller
« Reply #14 on: October 06, 2018, 09:08:32 am »
Thanks,
We dont have much room for an antenna, so we need to stick to the NFC chips i think.
Like the idea of the trimmer caps...but we also wonder about making the antenna variable as follows....
(ceramic caps are  currently very pricey so prefer to not have another ceramic on the board)

To summarise, we are trying to use the  ST25DV04K-IER6S3  (13MHz Near field Radio receiver chip)  to receive data to our PCB….this data is then retrieved by the microcontroller on this PCB.
The st.com software app which calculates the antenna for this chip, has made the antenna 2cm by 2cm , and we don’t have room for that on any of our PCBs. Therefore, we have decided to use a 1cm by 1cm antenna (as shown) and we are trying to compensate for the small size by having the antenna spiral down through the 4 layers of the PCB …thus giving us more turns, but without having the wide diameter.

Also,  we have made  make/break links between the coils of each PCB layer, so that we can experiment and see which number of turns receives the near field radio wave the best.

Do you think that our ploy is bogus?

ST25DV04K datasheet
https://www.st.com/resource/en/datasheet/st25dv04k.pdf
« Last Edit: October 06, 2018, 03:32:38 pm by treez »
 

Offline Dubbie

  • Supporter
  • ****
  • Posts: 1115
  • Country: nz
Re: Radio tag to send configuration data to microcontroller
« Reply #15 on: October 06, 2018, 09:42:52 am »
Is...... that your pcb layout?!
 
The following users thanked this post: ocset

Offline ocsetTopic starter

  • Super Contributor
  • ***
  • Posts: 1516
  • Country: 00
Re: Radio tag to send configuration data to microcontroller
« Reply #16 on: October 06, 2018, 03:31:27 pm »
Thanks, the board was smashed together extremely quickly, since the chances of us making sales in this high volume market is extremely low..........in fact, the only likely purpose of this driver is for an exhibition where all it has to do is light up some LEDs for a few hours.....but we also have other products which we also want the antenna for.
 

Offline jmelson

  • Super Contributor
  • ***
  • Posts: 2765
  • Country: us
Re: Radio tag to send configuration data to microcontroller
« Reply #17 on: October 08, 2018, 09:15:49 pm »
Hello,
We wish to configure our offline LED lamps  at the end of the production stage. We therefore wish to put a M24LR64-R radio tag on  the PCB, and connect it to our PIC16F18856 micro  that's on the Lamp PCB.

Has it ever occurred to you that electrical interference out in the field could trigger this receiver to load trash into the firmware?  Without careful checking for CRC and signature, I'd almost GUARANTEE this will happen.

Jon
 
The following users thanked this post: ocset

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14472
  • Country: fr
Re: Radio tag to send configuration data to microcontroller
« Reply #18 on: October 10, 2018, 06:59:03 pm »
Is...... that your pcb layout?!

Ouch.
Is that "topological" routing? :popcorn:
 
The following users thanked this post: ocset


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf