Electronics > Microcontrollers

XC8 Soft UART on PIC16F819-Help please

<< < (3/4) > >>

DavidAlfa:
Proteus

djsb:
I have tried the first example on silicon and it works perfectly. I have a few questions and the answers would help me to understand how some of the code works

1/What is this line calculating

--- Code: ---#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)

--- End code ---

Is it the bit time or a value specifically for timer 2?

2/How does this code work?


--- Code: ---void SUART_Read_Text(char *Output, unsigned int length)


  while(length--)
        {
        *Output++ = SUART_Receive();      // Receive char
        }
}

--- End code ---

Can you give me an example of how this function is used (similar to SUART_Write_Text examples)?

3/In UART_Write_Text could a variable be passed to the function somehow?

4/ Are there any built in Printf/fprintf/sprintf functions in XC8 that can be used? Or does the programmer have to make their own printf like functions to compensate? What about format specifiers etc?

5/ How are pointers used in your first example?

Thanks for your help.

DavidAlfa:

--- Quote from: djsb on June 06, 2023, 06:56:10 pm ---1/What is this line calculating

--- Code: ---#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)

--- End code ---
Is it the bit time or a value specifically for timer 2?

--- End quote ---

Check where OneBitDelay is being used  ;).
Both! It's the Timer 2 period, matching the bit time.
Timer2 starts counting up, when the counter matches the period it resets and the cycle starts over.
So for 8MHz Xtal, Fcy is Xtal/4 =2MHz, for 9600baud the period is 2MHz/9600=208.
Real timer period is PR2+1, thus we subtract 1 from the final value.



--- Quote from: djsb on June 06, 2023, 06:56:10 pm ---2/How does this code work?

--- Code: ---void SUART_Read_Text(char *Output, unsigned int length)


  while(length--)
        {
        *Output++ = SUART_Receive();      // Receive char
        }
}

--- End code ---

--- End quote ---

You specify the buffer Output and the expected data length.
The function will keep receiving data from the serial port  until the data length is done.
SUART_Receive waits for the start bit, then processes the following bits and returns the final char.

But it's too simple, might cause stalling and weird things.
Better to modify to receive data until a null char / new line are received, and return the received count:

--- Code: ---// Output: Buffer where to write data
// length: Max receiving bytes, to prevent overflow
unsigned int SUART_Read_Text(char *Output, unsigned int length)
{
  unsigned int rx = 0;
  while(length)
  {
    char a= SUART_Receive();         // Receive char
    if(!a || a=='\r' || a=='\n')        // Break on string termination, new line or line return
      break;
    Output[rx++] = a;                   // Store in output buffer, increase count   
    length--;
  }
  if(length)                            // Clear last char to terminate the string
    Output[rx] = '\0';                  // We didn't fill the output buffer, so clear next available char
  else
    Output[rx-1] = '\0';            // length fully used, clear last char of the buffer

  return rx;                            // Return total received chars
}

--- End code ---



--- Quote from: djsb on June 06, 2023, 06:56:10 pm ---Can you give me an example of how this function is used (similar to SUART_Write_Text examples)?

--- End quote ---


--- Code: ---while(1){
  char buffer[32];                             // Output buffer, 32 chars
  unsigned int recv=0;

  SUART_Write_Text("\r\n\nPlease type something and press enter, I'll answer back :)\r\n");
  recv = SUART_Read_Text(buffer, sizeof(buffer));     // Automatically computes the buffer size

  // Now it''ll wait for the data from RX. At the computer, type something like "Hello, djsb here".
  // Enter will cause a new line, stopping the receiving
  SUART_Write_Text("Got data!\r\n\n");
  SUART_Write_Text("I received: \"");
  SUART_Write_Text(buffer);
  SUART_Write_Text("\"\r\n");
  if (recv==sizeof(buffer)){
      SUART_Write_Text("The buffer was filled up, stopping receiving early.\r\n");
      SUART_Write_Text("Please type shorter strings.\r\n");
  }
  __delay_ms(1000);


--- End code ---



--- Quote from: djsb on June 06, 2023, 06:56:10 pm ---3/In UART_Write_Text could a variable be passed to the function somehow?

--- End quote ---
No, it's an entirely different thing.



--- Quote from: djsb on June 06, 2023, 06:56:10 pm ---4/ Are there any built in Printf/fprintf/sprintf functions in XC8 that can be used? Or does the programmer have to make their own printf like functions to compensate? What about format specifiers etc?

--- End quote ---

Yes, by adding the header #include <stdlib.h> .
I recommend trying itoa() for (un)signed integers (Add #include <stdio.h>)
(s)printf might use a heck of resources, itoa will be much ligher for simple interger to string conversion.
Avoid floats when possible, they will use a lot of resources!

--- Code: ---#include <stdlib.h>

  char buffer[32];                             // Output buffer, 32 chars
  unsigned int counter=0;

while(1){ 
  SUART_Write_Text("(itoa)    Counter: ");
  SUART_Write_Text(itoa(buffer, counter, 10)); // itoa returns the same char pointer as specified in the argument
  SUART_Write_Text("\r\n");

  sprintf(buffer, "(sprintf) Counter: %u\r\n", counter); 
  SUART_Write_Text(buffer);

  printf("(printf)  Counter: %u\r\n\r\n", counter);

  counter++;
  __delay_ms(1000);
}

--- End code ---



--- Quote from: djsb on June 06, 2023, 06:56:10 pm ---5/ How are pointers used in your first example?

--- End quote ---
What "first example" exactly?

djsb:
Thanks for your reply and further help.
My comment about pointers refers to the Asterisks in this code


--- Code: ---
#include <xc.h>

#define _XTAL_FREQ      8000000UL
#define Baudrate        38400

#if (Baudrate!=9600) && (Baudrate!=19200) && (Baudrate!=38400)
 #error "Please set a valid baudrate: 9600/19200/38400"
#endif

#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)

#define UART_RX         PORTBbits.RB4
#define UART_RX_DIR     TRISBbits.TRISB4
#define UART_TX         PORTBbits.RB2
#define UART_TX_DIR     TRISBbits.TRISB2

// Bit macros to avoid slow looping (We need it to be as fast as possible)
#define BIT_DELAY()     TMR2IF=0; while(!TMR2IF);
#define TX_BIT(v,n)     if(v & n){UART_TX=1;} else{UART_TX=0;} BIT_DELAY()
#define RX_BIT(v,n)     if(UART_RX){v|=n;} BIT_DELAY()

void SUART_Init(void)
{
 UART_TX = 1;                           // TX pin is high in idle state
 UART_RX_DIR = 1;                       // Input
 UART_TX_DIR = 0;                       // Output
 PR2 = OneBitDelay;                     // Load period counter
 T2CON = 0;                             // Use timer 2 as timebase
 T2CONbits.TMR2ON = 1;                  // Start timer
}

unsigned char SUART_Receive(void)
{
 unsigned char DataValue = 0;

  while(UART_RX==1);            // wait for start bit
  TMR2=OneBitDelay*2/3;         // Preload the timer with at 2/3 of a bitDelay (1/2 might sample too late with this slow mcu)
  BIT_DELAY();                  // Wait for it to expire
  BIT_DELAY();                  // Counter resetted, wait for a full bit time now
  RX_BIT(DataValue,0x01);       // Unrolled loop for fastest processing
  RX_BIT(DataValue,0x02);
  RX_BIT(DataValue,0x04);
  RX_BIT(DataValue,0x08);
  RX_BIT(DataValue,0x10);
  RX_BIT(DataValue,0x20);
  RX_BIT(DataValue,0x40);
  RX_BIT(DataValue,0x80);
  return DataValue;
}

void SUART_Transmit(char DataValue)
{
  UART_TX = 0;                                           // Start
  TMR2=0;
  BIT_DELAY();
  TX_BIT(DataValue,0x01);       // Unrolled loop for fastest processing
  TX_BIT(DataValue,0x02);
  TX_BIT(DataValue,0x04);
  TX_BIT(DataValue,0x08);
  TX_BIT(DataValue,0x10);
  TX_BIT(DataValue,0x20);
  TX_BIT(DataValue,0x40);
  TX_BIT(DataValue,0x80);
  UART_TX = 1;                                           //Stop
  BIT_DELAY();
}

void SUART_Write_Text(char *text)
{
  while(*text)
    SUART_Transmit(*text++);
}

void SUART_Read_Text(char *Output, unsigned int length)
{   
  while(length--)
    *Output++ = SUART_Receive();      // Receive char
}

void initMain()
{
  OSCCONbits.IRCF = 0b0111; //Set the Clock to 8MHz when using internal oscillator.
  ADCON1bits.PCFG = 0b0110; //See page 82 of data sheet..   
  PORTA = 0x00;              //Clear Port A.
  PORTB = 0x00;              //Clear Port B.   
}

int main(void)
{
  char i;
  initMain();   
  SUART_Init();
  __delay_ms(1000);
 
     
  /* Simple string tests */
  /*
  SUART_Write_Text("Hello!\r\n");
  SUART_Write_Text("Software uart, ");
  #if (Baudrate==9600)
    SUART_Write_Text("9600");
  #elif (Baudrate==19200)
    SUART_Write_Text("19200");
  #elif(Baudrate==38400)
    SUART_Write_Text("38400");
  #else
    SUART_Write_Text("unknown");
  #endif
  SUART_Write_Text(" bauds.\r\n\n");
  */
   
 
  /* TX/RX loop */
  /*
  SUART_Write_Text("Please type in the console now, you should see the input below.\r\n\n");
  while(1){
    SUART_Transmit(SUART_Receive());
  }
  */
 
  while(1)
  {
    for ( i = 'A'; i <= 'Z'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
    for ( i = 'a'; i < 'z'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
    for ( i = '0'; i <= '9'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
  }
}


--- End code ---

In particular, in the SUART_Write_Text(char *text) and void SUART_Read_Text(char *Output, unsigned int length) functions. I'm unfamiliar with the use of pointers in general and not clear on how they work in a small microcontroller. Thanks again.

DavidAlfa:
Pointers in this context are pretty easy!


--- Code: ---char letter;        // This is a simple char. This variable is stored somewhere in the ram. A pointer would contain that address.
char * ptr;         // This is a char pointer. It can hold an address pointing to a char variable type

ptr = &letter;     // Store letter address into the pointer.    &(variable) takes the address of a variable
 
Write(letter);     // Those do exactly the same.
Write(*ptr);       // *(pointer) takes the value of the variable placed at the address hold by the pointer.

--- End code ---

A string is actually a pointer to an aray:

--- Code: ---char *dave_msg = "Hello, I'm Dave";         // Make a pointer to an array containing this string

--- End code ---

This would be the same as:

--- Code: ---char dave_msg_arr[] = "Hello, I'm Dave";  // Make an array containing this string
char *dave_msg = dave_msg_arr;              // Point to the first element, H

--- End code ---

Remember strings in C are null-terminated.
This means that "Hello, I'm Dave" is not 15 char, but 16, there's a final char set to 0, indicating the end of the string.
A very simple error would be to send an unterminated string using a standard C function.
It would cause an uncontrolled situation where it would send all the data it finds until reaching a 0.

So SUART_Write_Text(char *text) would take the text pointer, send the data it points to, increase the address and repeat until it finds a 0.

--- Code: ---void SUART_Write_Text(char *text)
{
  while(*text)                                      // If the memory address pointer by text is not 0
    SUART_Transmit(*text++);             // Send the data, increment the pointer address afterwards and repeat the loop
}

--- End code ---

For SUART_Write_Text("Hello"), you would have an array in memory like this:

Address   Value
 0             'H'
 1             'e'
 2             'l'
 3             'l'
 4             'o'
 5              0      <- Null termination

text pointer would initially point to 'H'. It will increment after each pass, so it'll go through 'e', 'l', 'l', 'o' and finally 0.
When 0 is found, the while(*text) (Which is the same as while(*text != 0)) breaks the loop and the function returns.

SUART_Read_Text(char *Output, unsigned int length) does a similar thing.
You specify the input buffer:

--- Code: ---char buffer[16];    // Store here the incoming characters
SUART_Read_Text(buffer, 16);  // Passing the array name automatically makes a pointer to its first element

--- End code ---

Inside SUART_Read_Text(char *Output, unsigned int length), the Output pointer will be incremented after each received char.
So initially it'll point to buffer[0], If you write "Hello" it'll do:

- Receives 'H'. Store 'H' to Output pointer address, currently pointing to buffer[0]. Increase Output address.
- Receives 'e'. Store to Output pointer address, currently pointing to buffer[1]. Increase Output address.
- Receives 'l'. Store to Output pointer address, currently pointing to buffer[2]. Increase Output address.
- Receives 'l'. Store to Output pointer address, currently pointing to buffer[3]. Increase Output address.
- Receives 'o'. Store to Output pointer address, currently pointing to buffer[4]. Increase Output address.
- (You pressed enter) Receives '\n'. The last code I posted would detect this and finish the reception. Store 0 to Output pointer address, currently pointing to buffer[5] to set the ending,return.

Now buffer would contain ['H'] ['e'] ['l'] ['l'] ['o'] [ 0].

Navigation

[0] Message Index

[#] Next page

[*] Previous page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod