CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Annoying Hyperterminal XModem/EEPROM problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
andyd



Joined: 08 Mar 2007
Posts: 30

View user's profile Send private message

Annoying Hyperterminal XModem/EEPROM problem
PostPosted: Thu Mar 08, 2007 5:13 am     Reply with quote

I've written a program to send a file to my PIC (16F88) using Hyperterminal (using XModem). Up until now, all I'd tried sending my PIC were text files full of nice normal characters. Since my PIC actually needs to receive ADPCM files, I thought I'd better test it with one, and that's where it all falls down.

Basically, the PIC seems to "skip over" certain characters in the file - if I program the EEPROM (24AA1025) with another file, then program it with the ADPCM file and get the PIC to dump the EEPROM contents into Hyperterminal, I see some random characters (that I'd expect from the file) and then every so often, it will just have segments of whatever was previously in the EEPROM in the middle of what should be the ADPCM file, i.e. it's just refusing to write certain characters in the ADPCM file to memory. It's definitely not a problem with how Hyperterminal is displaying it as if I put a plain text file in first, then try and overwrite it, I can actually read whole segments of the text still! Displaying the EEPROM contents as ints gives me lots of negative numbers with a few positive ones.

I wrote a small C program on my PC to just read in the file as unsigned chars and then display it, which worked fine. However, changing all my functions in my PICC code to use unsigned chars still didn't help. The only conclusion I could come to was that the fgetc() function was reading them into the PIC as signed (I've searched though all the compiler libraries to find it but can't!) and after that no amount of casting could get them written right. Does this sound like it could be the culprit? Or something else in the PIC? Any ideas on how to get round it? A friend suggested using the hardware USART of the PIC and reading directly out of the register, but I haven't got a clue where to start with doing that in C. Any ideas on what could be causing the problem, or any possible solutions (that hopefully don't mean throwing away loads of what I've written so far!) would be very much appreciated!

Code:
#include <16f88.h>
#include <stdio.h>
#include <stdlib.h>

#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock = 8000000)                              // Delay setup
#use rs232(baud = 9600, xmit = PIN_B0, rcv = PIN_A0, stream = PC) // RS232 setup
#use I2C(Master, sda = PIN_B1, scl = PIN_B4, FAST)               // I2C setup


// EEPROM write

void eeprom_write(unsigned char address_upper, unsigned char address_lower, unsigned char data)
{
        i2c_start();               // Start communication
        i2c_write(0xA0);                // Send control code & address of EEPROM then set to write mode
        i2c_write(address_upper);       // Write upper address byte
        i2c_write(address_lower);       // Write lower address byte
        i2c_write(data);                // Write data
        i2c_stop();                  // Stop communication
        delay_ms(5);               // Wait 5ms
}


// EEPROM read

unsigned char eeprom_read(unsigned char address_upper, unsigned char address_lower)
{
        unsigned char temp_byte;

        i2c_start();               // Start communication
        i2c_write(0xA0);                // Send control code & address of EEPROM then set to write mode
        i2c_write(address_upper);       // Write upper address byte
        i2c_write(address_lower);       // Write lower address byte

        i2c_start();               // Start communication
        i2c_write(0xA1);                // Send control code & address of EEPROM then set to read mode
        temp_byte = i2c_read(0);        // Read data without acknowledge bit

        i2c_stop();                     // Stop communication

      return temp_byte;
}


// EEPROM write (string)

void eeprom_write_string(unsigned char address_upper, unsigned char address_lower, unsigned char* data)
{
        while(*data)                              // Loop until end of string to be written is reached
        {
                i2c_start();                        // Start communication
                i2c_write(0xA0);                         // Send control code & address of EEPROM then set to write mode
                i2c_write(address_upper);                // Write upper address byte
                i2c_write(address_lower);                // Write lower address byte
                i2c_write(*data++);                      // Write data
                i2c_stop();                           // Stop communication

                address_lower++;                     // Add 1 to lower address byte
                if(address_lower == 0x00) address_upper++;   // If lower address = 0, add 1 to upper address byte

                delay_ms(5);                        // Wait 5ms
        }
}


// EEPROM contiguous read start (reads first byte)

int eeprom_read_contig_start(unsigned char address_upper, unsigned char address_lower)
{
        int data;

        i2c_start();               // Start communication
        i2c_write(0xA0);         // Send control code & address of EEPROM then set to write mode
        i2c_write(address_upper);   // Write upper address byte
        i2c_write(address_lower);     // Write lower address byte
        i2c_start();                // Resend start signal to indicate read portion
        i2c_write(0xA1);            // Send control code & address of EEPROM then set to read mode
        data=i2c_read();            // Read data

        return(data);
}


// EEPROM contiguous read

int eeprom_read_contig(void)
{
        int data;

        data = i2c_read();           // Read data

        return(data);
}


// EEPROM contiguous read stop (reads last byte)

int eeprom_read_contig_stop(void)
{
        int data;
        data = i2c_read(0);         // Read data without acknowledge bit
        i2c_stop();                 // Stop communication

        return(data);
}

void main(void)
{
      int16 i;                      // Counter variable
      int packetnum;                   // XModem packet number
      int packetnum_inverse;             // Inverse XModem packet number
      int packetcount = 0;            // Packet count in program to keep synchronisation
      int checksum_rec, checksum_calc;   // XModem received checksum & calculated checksum
      int32 checksum_temp;             // Used in checksum calculation
      int error = 0;                   // Transmission error
        int out;                     // Used to send PC XModem control signals           
      char c;                        // Character read in from PC during file transmission   
      int data1[64];                  // First half of XModem packet
      int data2[64];                  // Second half of XModem packet
      
      char address_upper;               // Upper EEPROM address byte
      char address_lower;               // Lower EEPROM address byte
      

      setup_adc_ports(NO_ANALOGS);         // Turn off analogue inputs
      setup_oscillator(OSC_8MHZ|OSC_INTRC);   // Use internal 8MHz oscillator
      
      
         fputs("Initiate file transfer using PC then press button to accept.", PC);      
         
         while(!input(PIN_A1)) // Wait for button press
         {
      
         }      
   
         address_upper = 0b00000000;   // Variables set to write data to address 0000000000000001
         address_lower = 0b00000001;
   
         out = 0x15;
         fputc(out, PC); // Send NAK character to PC to indicate PIC is ready to recieve data (1 byte checksum)
         
         while(TRUE) // Loop until file transfer complete (loop will break)
         {
            c = 0;
               
            while((c != 0x01)&&(c != 0x04)) // Wait for SOH or EOT character
            {
               c = fgetc(PC); // Keep checking data from PC until SOH or EOT character received
            }
               
            if (c == 0x04) break; // If EOT character received, end file transfer
               
            
            packetnum = fgetc(PC);          // Get packet number from PC                  
            packetnum_inverse = fgetc(PC);   // Get inverse packet number from PC
   
            if (packetnum_inverse != (255 - packetnum)) error = 1;   // If packet number inverse incorrect, set error flag
   
            if(packetnum == packetcount) // If received packet number matches expected, increment expected for next transmission
            {
               if(packetcount == 256)
               {
                  packetcount = 0;
               }
      
               else
               {
                  packetcount++;
               }
            }
   
   
            checksum_temp= 0;   
            
            for (i = 0; i < 64; i++) // Loop until first half of packet received, store in string & add to checksum
            {
               data1[i] = (unsigned char)fgetc(PC);       
               checksum_temp += (unsigned char)data1[i];
            }
   
            for (i = 0; i < 64; i++) // Loop until second half of packet received, store in string & add to checksum
            {
               data2[i] = (unsigned char)fgetc(PC);
               checksum_temp += (unsigned char)data2[i];
            }
            
            
            checksum_calc = checksum_temp%256; // Calculate checksum
   
            checksum_rec = fgetc(PC); // Get checksum byte from PC
   
            if(checksum_calc != checksum_rec) error = 1; // If checksum incorrect, set error flag
         
            
            if(error != 1) // If no error, write to EEPROM
            {
               eeprom_write_string(address_upper, address_lower, data1);
   
               if(address_lower >= 192) // If next 64 bytes of data will cause an overflow in lower address byte
               {
                  address_upper++;                  // Add 1 to upper address byte    
                  address_lower += 64;               // Add 64 to lower address byte
                  address_lower = address_lower - 256;   // Take 256 from lower address byte
               }
               
               else
               {
                  address_lower += 64;               // If no overflow will be caused, add 64 to lower address byte
               }
   
               eeprom_write_string(address_upper, address_lower, data2);
   
               if(address_lower >= 192) // If next 64 bytes of data will cause an overflow in lower address byte   
               {
                  address_upper ++;                   // Add 1 to upper address byte
                  address_lower += 64;               // Add 64 to lower address byte
                  address_lower = address_lower - 256;   // Take 256 from lower addres byte
               }
               else
               {
                  address_lower += 64;               // If no overflow will be caused, add 64 to lower address byte
               }
            }
         
      
            if(error == 1) // If error, send NAK
            {
               out = 0x15;
            }
   
            else   // If no error, send AK
            {
               out = 0x06;
            }
               
            error = 0; // Reset error flag
   
            fputc(out,PC); // Send AK or NAK character to PC
               
         }
   
         out = 0x06;
         fputc(out,PC); // Send AK character to PC to acknowledge EOT character received
   
         address_upper = 0b00000000;   
         address_lower = 0b00000001;

         fprintf(PC, "%d,", (unsigned int)eeprom_read_contig_start(address_upper, address_lower));
         for(i = 0; i<500; i++)
         {
            fprintf(PC, "%d,", eeprom_read_contig());
         }
         fprintf(PC, "%d", eeprom_read_contig_stop());


}
andyd



Joined: 08 Mar 2007
Posts: 30

View user's profile Send private message

PostPosted: Thu Mar 08, 2007 6:11 am     Reply with quote

This is getting really annoying now... I've just written a small function to use in place of fgetc which uses the hardware USART & reads from the RCREG directly (I think I've done it right) so that I can make sure that it's returning an unsigned char, but the same thing still happens.

Could it be Hyperterminal causing the problems?

Code:
unsigned char serial_rx()
{
   unsigned char c;
   while(!kbhit()) // Wait until byte received
   continue;
   c = RCREG;
   return c;
}
Ttelmah
Guest







PostPosted: Thu Mar 08, 2007 9:39 am     Reply with quote

I think you are on a 'wild goose chase', in thinking the problem relates to signed/unsigned. As far as the PIC is concerned, it doesn't matter whether a value is signed or unsigned. It receives a binary value, which you can _treat_ as signed or unsigned, if it suits you. The most likely thing to cause 'skipping', is to do with a timing problem. Remember just how long writing data takes. 5mSec, is potentially 5 character times...

Best Wishes
Guest








PostPosted: Thu Mar 08, 2007 10:16 am     Reply with quote

If it's truly ADPCM then your data is likely to contain any value 0-255 in any byte. Your serial receive code is looking for particular values & trating them as part of the protocol:
Code:
if (c == 0x04) break; // If EOT character received, end file transfer

So if your ADPCM data contains a byte value 4 you will see this as a termination of the file.
newguy



Joined: 24 Jun 2004
Posts: 1903

View user's profile Send private message

PostPosted: Thu Mar 08, 2007 1:46 pm     Reply with quote

As someone else has pointed out, a data byte of 0x04 will cause your program to abort because it thinks it has received the end of file character. You have to implement a bit more robust checking scheme to look for a combination of characters - e.g. "End$". This method is more robust because as the number of characters in the termination message increases, the likelihood of random data triggering it goes down.

Check this post: http://www.ccsinfo.com/forum/viewtopic.php?t=23953

It pertains to wireless data transfer, but the interrupt-driven serial receive routine and associated validity check is directly applicable to your situation.
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Mar 08, 2007 6:11 pm     Reply with quote

Xmodem is not a very strong protocol but you are seeing way too much errors. Look again at how you are handling an error situation, it has some bugs. For example, after detecting an error in the packet header you are incrementing packetcount, this is wrong as you want to receive the same packet again. Now the packet is skipped and there you have your gap in the received data.

Another weak point is that after detecting an error in the packet header you are always reading 128 bytes. There is a good chance that because of the detected error there will be a different number of bytes to be received.


I can see you are struggling with variable sizes; 8, 16 or 32-bits are all mixed up so let me give you some tips.
Code:
               if(packetcount == 256)
               {
                  packetcount = 0;
               }
     
               else
               {
                  packetcount++;
               }

1) packetcount is an 8 bit integer, the maximum value it can contain is 255. It will never reach 256! After getting at 255 it will wrap to zero again. The above code can be rewritten to the functional same equivalent:
Code:
                  packetcount++;   // Will wrap at reaching 255


2) A similar thinking error goes for
Code:
                  address_lower = address_lower - 256;   // Take 256 from lower address byte
An 8 bit value minus 256 will give you the same value again. This line is doing nothing.

3) Instead of two seperate address_lower and address_upper variables you can make your code much shorter and easier to understand by replacing this by a single int16.
Code:
               if(address_lower >= 192) // If next 64 bytes of data will cause an overflow in lower address byte
               {
                  address_upper++;                  // Add 1 to upper address byte   
                  address_lower += 64;               // Add 64 to lower address byte
                  address_lower = address_lower - 256;   // Take 256 from lower address byte
               }
               
               else
               {
                  address_lower += 64;               // If no overflow will be caused, add 64 to lower address byte
               }
is then reduced to
Code:
int16 Address;
Address += 64;
Study the make8() function in the manual for how to split an int16 into two seperate int8 variables again.


4)
Code:
            checksum_calc = checksum_temp%256; // Calculate checksum
Here you are throwing away the 24 most significant bits of the checksum. So why did you choose to make this variable 32 bits when you are only interested in the 8 least significant bits? You can save some code space by making checksum_temp an int8.
Guest








PostPosted: Fri Mar 09, 2007 3:08 am     Reply with quote

Anonymous wrote:
If it's truly ADPCM then your data is likely to contain any value 0-255 in any byte. Your serial receive code is looking for particular values & trating them as part of the protocol:
Code:
if (c == 0x04) break; // If EOT character received, end file transfer

So if your ADPCM data contains a byte value 4 you will see this as a termination of the file.


I had thought that this could potentially be a problem and will look at implementing it better, however surely if this was causing the problem the PIC would receiving data completely as it thinks the transfer is over, and there would be nothing to make it resume later in the transfer? As it seems to be writing chunks of data interspersed with not writing any, that would mean it has somehow resumed file transfer on its own unless I'm missing an obvious point here? Also it only checks for the 0x04 character at the start of every packet (as that's the only time it should legitimately occur)?

Also, thanks for the tips ckielstra, will definitely implement those Smile
andyd



Joined: 08 Mar 2007
Posts: 30

View user's profile Send private message

PostPosted: Fri Mar 09, 2007 3:09 am     Reply with quote

Sorry, that was me, forgot to login!
andyd



Joined: 08 Mar 2007
Posts: 30

View user's profile Send private message

PostPosted: Fri Mar 09, 2007 11:15 am     Reply with quote

After looking into this a bit further, it seems it's the NULL character that's causing the PIC to screw up somehow. I compared what was coming out of the EEPROM with a display of the file as ints on a PC, and whenever the PIC stops writing to the EEPROM, there's an 0 in the display of ints on the PC (i.e. NULL). Just to confirm this, I made a program to create a text file containing chars 255-0 in reverse order just so I knew when a NULL should be occuring, and, sure enough, as soon as a NULL occurs, it all goes wrong.

The 128 byte packet it receives from the PC is written into 2 64 byte strings purely for memory reasons (the compiler claims to have run out of memory when I declare a single 128 byte one).

It would appear that the PIC gets a null, freaks out, then doesn't write anything to the string for the remainder of the 64 bytes, then gets to the next string, writes fine until it gets another NULL, doesn't write anything for the rest of that string, then because both strings are half empty, when it comes to write them to the EEPROM, only the data in the EEPROM that "lines up" with the data in the strings gets overwritten, the rest stays as it is, explaining why old data gets displayed.

The question now is, why does the PIC freak out when it gets a NULL, and how do I get round it?
Sherpa Doug
Guest







PostPosted: Fri Mar 09, 2007 11:48 am     Reply with quote

In the standard C language the null character terminates a string.
Ttelmah
Guest







PostPosted: Sat Mar 10, 2007 3:17 am     Reply with quote

The problem is caused by his own 'eeprom_write_string' function. The 'while' loop in this, will only loop so long as the character is non zero. This is 'correct' behaviour to deal with a 'string' (sinceas you correctly say, zero is the string terminator), but what he has, is not actually a 'string', but a set of binary data. Realistically, the length of the received data 'block', needs to be passed to this function, and it needs to loop for the entire block, and not just till the zero character. Also though, when retrieving the block from the EEPROM, it will be necessary to again have access to the length of the data (since the same problem will appear on retrieval).

Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group