Shop OBEX P1 Docs P2 Docs Learn Events
Difference between fopen and spin2cpp for full duplex serial — Parallax Forums

Difference between fopen and spin2cpp for full duplex serial

robyouyourobyouyou Posts: 3
edited 2012-10-05 07:21 in Propeller 1
Hi,

I have a problem that I can't wrap my head around. I'm trying to read serial data from an RFID reader using full duplex serial. When do a simple convert of the .spin file that works everything in propgcc works great. However when I try and use fopen with the _FullDuplexSerialDriver directly nothing works. Here's the code that does not work:
#include <propeller.h>
#include <stdlib.h>
#include <stdio.h>
extern _Driver _FullDuplexSerialDriver;
/**
 * Main program function.
 */
int main(void)
{
    uint8_t tagID[12];
    uint8_t Enable;
    uint8_t RFRead;
    int i;
    
    printf("Start of program\n");
    waitcnt(CLKFREQ * 3 + CNT);
    
    Enable = 0xF8;
    RFRead = 0xFA;
            
                                        /* driver to use            Baud  rx tx  mode   */
    FILE *NFCin = __fopen_driver(NFCin, &_FullDuplexSerialDriver, "19200, 4, 3", "rb+");
     
    printf("Opened Serial Port 4/3 setting raw IO\n"); 
        
    setvbuf(NFCin, NULL, _IONBF, 0);
    NFCin->_flag &= ~_IOCOOKED;
        
    printf("Turning on NFC Reader\n");
    
    fwrite(&Enable, 1, 1, NFCin);
    waitcnt(CLKFREQ * 3 + CNT);
    fwrite(&RFRead, 1, 1, NFCin);
    waitcnt(CLKFREQ + CNT);
      
    printf("Reading Tag.\n"); 
          
    while(1)
    {
        fwrite(&RFRead, 1, 1, NFCin);
        waitcnt(CLKFREQ/2 + CNT); 
        fread(tagID, 1, 12, NFCin);
        for (i=0; i<= 11; i++) { 
            printf("Byte %d = %x\n", i, tagID[i]);
        }
        waitcnt(CLKFREQ + CNT);
    }
    fclose(NFCin);
    return 0;
}

I'm I doing something monumentally stupid/missing something obvious? The same basic code structure (wait times, etc) works great via spin2cpp so I know it must be doing something related to the driver incorrectly (but I have no clue what).

Any help/blinding glimpses of the obvious would be greatly appreciated.

Comments

  • ersmithersmith Posts: 6,054
    edited 2012-10-03 14:47
    I think you're running afoul of the C rule that between any read and write on the same FILE struct there must be an intervening fseek or fflush.

    It would probably be easier to set up two FILE structs, one for reading and one for writing. As long as they use the same pins the FullDuplexSerial driver will re-use the same COG.

    Eric
  • robyouyourobyouyou Posts: 3
    edited 2012-10-03 18:33
    Hi Eric,

    Thanks for the tip. I tried the following:
    #include <propeller.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    extern _Driver _FullDuplexSerialDriver;
    
    /**
     * Main program function.
     */
    int main(void)
    {
        uint8_t tagID[12];
        uint8_t Enable;
        uint8_t RFRead;
        uint8_t RedLED;
        int i;
        
        printf("Start of program\n");
        waitcnt(CLKFREQ * 3 + CNT);
        
        Enable = 0xF8;
        RFRead = 0xFA;
        RFRead = 0xFD;
                
                                            /* driver to use            Baud  rx tx  mode   */
        FILE *NFCin = __fopen_driver(NFCin, &_FullDuplexSerialDriver, "19200, 4, 3", "rb");
        FILE *NFCout = __fopen_driver(NFCin, &_FullDuplexSerialDriver, "19200, 4, 3", "rb+");
         
        printf("Opened Serial Port 4/3 setting raw IO\n"); 
            
        setvbuf(NFCin, NULL, _IONBF, 0);
        setvbuf(NFCout, NULL, _IONBF, 0);
        NFCin->_flag &= ~_IOCOOKED;
            
        printf("Turning on NFC Reader\n");
        
        fwrite(&Enable, 1, 1, NFCout);
        waitcnt(CLKFREQ * 3 + CNT);
        fwrite(&RFRead, 1, 1, NFCout);
        fwrite(&RedLED, 1, 1, NFCout);
        waitcnt(CLKFREQ + CNT);
          
        printf("Reading Tag.\n"); 
              
        while(1)
        {
            printf("Sending Read Command.\n");
            fwrite(&RFRead, 1, 1, NFCout);
            waitcnt(CLKFREQ/2 + CNT); 
            printf("Reading Tag ID.\n");
            fread(tagID, 1, 12, NFCin);
            printf("Printing Tag ID.\n");
            for (i=0; i<= 11; i++) { 
                printf("Byte %d = %x\n", i, tagID[i]);
            }
            waitcnt(CLKFREQ + CNT);
        }
        fclose(NFCin);
        fclose(NFCout);
        return 0;
    }
    

    and now the program hangs (blocking waiting for input I'm guessing) at the fread section in the while loop (before it would just spit out 0 as the byte read). I played around with the mode for the fopen and noticed that if I changed rb+ to w on the NFCout pointer, the program would hang at the first fwrite. So it appears maybe there's something odd going on with the mode string. Is there some special string to use for serial TTL communication (i.e. given this is a serial buffer and not a "file" should I always be starting at the head of the stream - rewinding the file pointer before each read/write)?

    I have a suspicion that I'm not writing anything out at all as I included an fwrite to turn on a LED on the RFID reader board and the LED never comes on. Short of a scope, is there anyway to tell what's going on?

    The odd thing about this is that the spin2cpp version works great - maybe I'll just have to go that route but it only supports 1 reader and I have 3 that I need to talk to (all TTL serial). I guess I could just start and stop the spin2cpp FDS driver for each reader but this feels like a cop out.
  • ersmithersmith Posts: 6,054
    edited 2012-10-04 06:46
    The lines:
        FILE *NFCin = __fopen_driver(NFCin, &_FullDuplexSerialDriver, "19200, 4, 3", "rb");
        FILE *NFCout = __fopen_driver(NFCin, &_FullDuplexSerialDriver, "19200, 4, 3", "rb+");
    
    are a problem for 2 reasons: (1) you've got NFCin there twice, and (2) more importantly, __fopen_driver expects the first pointer to actually point to valid storage, which isn't the case here (because NFCin and NFCout are not initialized prior to the call). You could try something like:
        FILE tmp0, tmp1; /* real storage */
        FILE *NFCin = __fopen_driver(&tmp0, &_FullDuplexSerialDriver, "19200, 4, 3", "rb");
        FILE *NFCout = __fopen_driver(&tmp1, &_FullDuplexSerialDriver, "19200, 4, 3", "rb+");
    
    instead, or you could set up the driver list and call plain fopen (probably better):
    extern _Driver _FullDuplexSerialDriver, _SimpleSerialDriver;
    
    _Driver *_driverlist[] = {
      &_SimpleSerialDriver,
      &_FullDuplexSerialDriver,
      NULL
    };
    
    /**
     * Main program function.
     */
    ...
      FILE *NFCin = fopen("FDS:19200, 4, 3", "rb");
      FILE *NFCout = fopen("FDS:19200, 4, 3", "wb");
    ...
    
  • TorTor Posts: 2,010
    edited 2012-10-04 06:54
    Speaking from a general C point of view, the fopen/fread/fwrite functions (the stdio functions) are not a good choice to use for forth-and-back (read-and-write) protocols, because these functions are buffered. When you 'fread', say, 20 bytes from a file descriptor, the fread implementation in the C runtime library is supposed to use buffered I/O and (on a typical *nix system) read from the device in chunks of 4096 bytes. So the next fread call will just read from that buffer, not from the device. This of course messes up any kind of query/answer protocol.

    So to talk to a device you would use 'open/read/write' instead, which works directly on the device/driver level. How fread & co is implemented for the Propeller I don't know, but if it isn't buffering then it's strictly speaking not a stdio implementation.

    -Tor
  • ersmithersmith Posts: 6,054
    edited 2012-10-04 07:00
    Tor wrote: »
    So to talk to a device you would use 'open/read/write' instead, which works directly on the device/driver level. How fread & co is implemented for the Propeller I don't know, but if it isn't buffering then it's strictly speaking not a stdio implementation.
    In the current PropGCC libraries read and write are just implemented by calling fread and fwrite (this was to save having to write more code :-)). In the next release they will go directly to the driver and hence be a bit faster.
  • jazzedjazzed Posts: 11,803
    edited 2012-10-04 10:24
    Tor wrote: »
    ... if it isn't buffering then it's strictly speaking not a stdio implementation.

    It's buffered.
  • TorTor Posts: 2,010
    edited 2012-10-05 05:58
    ersmith wrote: »
    In the current PropGCC libraries read and write are just implemented by calling fread and fwrite (this was to save having to write more code :-)). In the next release they will go directly to the driver and hence be a bit faster.
    Ok :-) -- Usually fread/fwrite (library functions) call read/write (system / OS functions), not the other way around! Sounds good in any case though! :)
    jazzed wrote:
    It's buffered.
    Thanks jazzed. Ok so that means my general-case description holds up - the (upcoming) read/write functions would be a better choice for where you want to read X bytes from a communication channel if X bytes was specified in the call (so as to not let fread() having its own idea about how many bytes to read). With fwrite it's possible to work around the buffering problem with an fflush(), but fread() isn't that easy.

    -Tor
  • robyouyourobyouyou Posts: 3
    edited 2012-10-05 07:21
    Many thanks Eric, your fix (and keen eyes) did the trick. For those wanting to hook-up to an APSX RW-210 here's the C code I (well Eric) got to work:
    #include <propeller.h>
    #include <stdlib.h>
    #include <stdio.h>
    extern _Driver _FullDuplexSerialDriver;
    extern _Driver _SimpleSerialDriver;
    _Driver *_driverlist[] = {
      &_SimpleSerialDriver,
      &_FullDuplexSerialDriver,
      NULL
    };
    /**
     * Main program function.
     */
     int main(void)
    {
        uint8_t tagID[12];
        uint8_t Enable;
        uint8_t RFRead;
        uint8_t RedLED;
        int i;
        
        printf("Start of program\n");
        waitcnt(CLKFREQ * 3 + CNT);
        
        Enable = 0xF8;
        RFRead = 0xFA;
        RedLED = 0xFD;
         
         
        FILE *NFCin = fopen("FDS:19200, 4, 3", "rb");
        FILE *NFCout = fopen("FDS:19200, 4, 3", "wb");        
             
        printf("Opened Serial Port 4/3 setting raw IO\n"); 
            
        setvbuf(NFCin, NULL, _IONBF, 0);
        setvbuf(NFCout, NULL, _IONBF, 0);
        NFCin->_flag &= ~_IOCOOKED;
            
        printf("Turning on NFC Reader\n");
        
        fwrite(&Enable, 1, 1, NFCout);
        waitcnt(CLKFREQ * 3 + CNT);
        fwrite(&RFRead, 1, 1, NFCout);
        printf("Turning on Red LED\n");
        waitcnt(CLKFREQ + CNT);
        fwrite(&RedLED, 1, 1, NFCout);
        waitcnt(CLKFREQ + CNT);
          
        printf("Reading Tag.\n"); 
              
        while(1)
        {
            printf("Sending Read Command.\n");
            fwrite(&RFRead, 1, 1, NFCout);
            printf("Reading Tag ID.\n");
            fread(tagID, 1, 12, NFCin);
            printf("Printing Tag ID.\n");
            for (i=0; i<= 11; i++) { 
                printf("Byte %d = %x\n", i, tagID[i]);
            }
            waitcnt(CLKFREQ + CNT);
        }
        fclose(NFCin);
        fclose(NFCout);
        return 0;
    }
    

    Thanks again,

    Rob.
Sign In or Register to comment.