ImmortalAres
11-07-2005, 12:24
i'm trying to use kevins serial driver code version 0.3
i've changed all the registers to work on a 18f452 (which i have on a demo board with a serial port hooked up) the serial port is live as we used a multimeter to test and its data ready line is open at 10.8 volts if i remember correctly. so i don't think its the connection from board to PC
when i step thorugh on my icd 2 debugger the code loops and gets stuck here:
// if the queue is full, wait here until space is available
while(Tx_2_Queue_Full);//<----------the queue never empties
// put the byte on the circular queue
Tx_2_Queue[Tx_2_Queue_Write_Index] = byte;
// increment the queue byte count
Tx_2_Queue_Byte_Count++;
// increment the write pointer
Tx_2_Queue_Write_Index++;
any ideas? i've been trying to get this serial driver working for a week now. i'm seriously stuck and at a loss for ways to fix it
heres the whole files
serial_ports.c
/************************************************** *****************************
*
* TITLE: serial_ports.c
*
* VERSION: 0.3 (Beta)
*
* DATE: 05-Feb-2005
*
* AUTHOR: R. Kevin Watson
* kevinw@jpl.nasa.gov
*
* COMMENTS: The source code in this file implements a fully buffered,
* interrupt-driven serial port driver that can be used with
* either or both on-board serial ports.
*
* This source code will work with the Robovation (A/K/A EDU-RC)
* robot controller and the FIRST Robotics robot controller.
*
* By default, serial port one will operate at 115200 baud, which
* is compatible with InnovationFIRST's terminal program, and
* serial port two will operate at 9600 baud. These values can be
* easily changed by modifying the serial port initialization
* functions mentioned below.
*
* This file is best viewed with tabs set to four characters.
*
* Nine things must be done before this software will work
* correctly:
*
* 1a) FRC-RC: As this software is intended to replace IFI's
* serial port driver, the call to Serial_Driver_Initialize()
* in user_routines.c / User_Initialization() should be
* removed or commented out. In addition, all references to
* "user_Serialdrv.c" and "user_Serialdrv.h" must be removed
* from the project and all project source files.
*
* 1b) EDU-RC: As this software is intended to replace IFI's
* serial port driver, the call to Initialize_Serial_Comms()
* in user_routines.c / User_Initialization() should be
* removed or commented out. In addition, all references to
* "printf_lib.c" and "printf_lib.h" must be removed from
* the project and all project source files.
*
* 2) You must add the serial_ports.c/.h source files to
* your MPLAB project.
*
* 3) A #include statement for the serial_ports.h header
* file must be included at the beginning of each source
* file that uses the serial ports. The statement should
* look like this: #include "serial_ports.h".
*
* 4) If you intend to use the C18 output stream functions,
* A #include statement for the stdio.h header file must be
* included at the beginning of each source file that calls
* any of these functions. The statement should look like
* this: #include <serial_ports.h>.
*
* 5) Init_Serial_Port_One() and/or Init_Serial_Port_Two()
* must be called from the User_Initialization() function
* located in the user_routines.c source file.
*
* 6) The interrupt handler(s) must be installed in the
* InterruptHandlerLow() function located in the
* user_routines_fast.c source file. See the accompanying
* copy of user_routines_fast.c to see how this is done.
*
* 7) Decide what functionality you need and comment out the
* #define ENABLE_SERIAL_PORT_xxx_yy entries in serial_ports.h
* as necessary. As an example, if you only need to send data
* using serial port one and would like to reclaim the resources
* used by serial port two and serial port one's receiver
* source code, the top of the serial_ports.h file would look
* like this:
*
* // comment out the next line to disable all serial port one
* // receive functionality
* // #define ENABLE_SERIAL_PORT_ONE_RX
*
* // comment out the next line to disable all serial port one
* // transmit functionality
* #define ENABLE_SERIAL_PORT_ONE_TX
*
* // comment out the next line to disable all serial port two
* // receive functionality
* // #define ENABLE_SERIAL_PORT_TWO_RX
*
* // comment out the next line to disable all serial port two
* // transmit functionality
* // #define ENABLE_SERIAL_PORT_TWO_TX
*
* By default, both serial ports and their respective receive
* and transmit sections are enabled.
*
* 8) As the default output device for C18's output stream
* functions is the null device, you'll presumably want to
* change the value of stdout_serial_port to "SERIAL_PORT_ONE"
* or "SERIAL_PORT_TWO" if you want to see printf()'s output.
* User_Initialization() is a good place to do this.
*
* 9) To support terminal emulation software, \r\n should
* be used instead of just \n in the printf() format string.
*
*
* This serial port driver can send output stream data to either
* of the serial ports by setting the value of the global variable
* stdout_serial_port before calling output stream functions like
* printf(). Setting the value to "SERIAL_PORT_ONE" will send the
* output to serial port one. Likewise, setting the value to
* "SERIAL_PORT_TWO" will send the output to serial port two.
* Setting the value to "NUL" will send the output to the null
* device, meaning that the output is sent nowhere. These values
* are #define'd in serial_ports.h. As an example,
*
* stdout_serial_port = SERIAL_PORT_ONE;
* printf("Kernighan");
* stdout_serial_port = NUL;
* printf("and");
* stdout_serial_port = SERIAL_PORT_TWO;
* printf("Ritchie");
*
* will send the text "Kernighan" to the peripheral device attached
* to serial port one, the text "Ritchie" to the device attached to
* serial port two and the text "and" won't be sent anywhere.
* By default, output is sent to the null device, which is the only
* output device guaranteed to be present.
*
* You are free to use this source code for any non-commercial
* use. Please do not make copies of this source code, modified
* or un-modified, publicly available on the internet or
* elsewhere without permission. Thanks.
*
* Copyright ©2004-2005 R. Kevin Watson. All rights are reserved.
*
************************************************** ******************************
*
* CHANGE LOG:
*
* DATE REV DESCRIPTION
* ----------- --- ----------------------------------------------------------
* 22-Dec-2004 0.1 RKW - Original code.
* 28-Dec-2004 0.2 RKW - Using preprocessor directives, added the ability
* to enable/disable individual serial port receive and
* transmit code. Updated documentation.
* 05-Feb-2005 0.3 RKW - Added _user_putc() interface to C18's new output
* stream capabilities. Updated documentation.
*
************************************************** *****************************/
#include <stdio.h>
#include "p18f452.h"
#include "serial_ports.h"
// by default stdout stream output is sent to the null device,
// which is the only device guaranteed to be present.
unsigned char stdout_serial_port = NUL;
//
// Serial Port 2 Receive Variables:
//
#ifdef ENABLE_SERIAL_PORT_TWO_RX
volatile unsigned char Rx_2_Queue[RX_2_QUEUE_SIZE]; // serial port 2's receive circular queue
volatile unsigned char Rx_2_Queue_Full = FALSE; // flag that indicates that serial port 2's
// receive circular queue is full and cannot
// accept any more data
unsigned char Rx_2_Queue_Empty = TRUE; // flag that indicates that there is no more
// data present in serial port 2's receive
// circular queue
unsigned char Rx_2_Queue_Read_Index = 0; // read index into serial port 2's receive
// circular queue
volatile unsigned char Rx_2_Queue_Write_Index = 0; // write index into serial port 2's receive
// circular queue
volatile unsigned char Rx_2_Queue_Byte_Count = 0; // number of bytes in serial port 2's receive
// circular queue
volatile unsigned char RX_2_Overrun_Errors = 0; // number of overrun errors that have occurred
// in serial port 2's receive circuitry since
// the last reset
volatile unsigned char RX_2_Framing_Errors = 0; // number of framing errors that have occurred
// in serial port 2's receive circuitry since
// the last reset
#endif
//
// Serial Port 2 Transmit Variables:
//
#ifdef ENABLE_SERIAL_PORT_TWO_TX
volatile unsigned char Tx_2_Queue[TX_2_QUEUE_SIZE]; // serial port 2's transmit circular queue
volatile unsigned char Tx_2_Queue_Full = FALSE; // flag that indicates that serial port 2's
// transmit circular queue is full and cannot
// accept any more data
volatile unsigned char Tx_2_Queue_Empty = TRUE; // flag that indicates that there is no more
// data to send in serial port 2's transmit
// circular queue
volatile unsigned char Tx_2_Queue_Read_Index = 0; // read index into serial port 2's transmit
// circular queue
unsigned char Tx_2_Queue_Write_Index = 0; // write index into serial port 2's transmit
// circular queue
volatile unsigned char Tx_2_Queue_Byte_Count = 0; // number of bytes in serial port 2's transmit
// circular queue
#endif
/************************************************** *****************************
*
* FUNCTION: Init_Serial_Port_Two()
*
* PURPOSE: Initializes serial port two for asynchronous operation
*
* CALLED FROM: user_routines.c/User_Initialization()
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS: This function must be called before you try to use serial
* port two.
*
* By default, this serial port is set to 9600 baud with
* the transmitter and receiver enabled.
*
* The serial port's baud rate is programmed by entering
* a value into the SPBRG2 register and possibly changing
* the value of the BRGH bit. Several example values are
* included in the serial_ports.h file.
*
* Numbers within brackets refer to the PIC18F8520 data
* sheet page number where more information can be found.
* This document can be found at microchip's website at
* http://www.microchip.com or at the author's website at
* http://www.kevin.org/frc
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX or ENABLE_SERIAL_PORT_TWO_TX
* is #define'd in serial_ports.h
*
************************************************** *****************************/
void Init_Serial_Port_Two(void)
{
// Start by initializing the serial port with code
// common to receive and transmit functions
SPBRG = BAUD_19200; // baud rate generator register [200]
//
TXSTAbits.BRGH = 1; // high baud rate select bit (asynchronous mode only) [198]
// 0: low speed
// 1: high speed
//
PIE1bits.RCIE = 0; // receive interrupt enable bit [95]
// 0: disables received data interrupt
// 1: enables received data interrupt
//
PIE1bits.TXIE = 0; // transmit interrupt enable bit [95]
// 0: disables transmit register empty interrupt
// 1: enables transmit register empty interrupt
//
TXSTAbits.SYNC = 0; // USART mode select bit [198]
// 0: asynchronous mode
// 1: synchronous mode
//
TXSTAbits.CSRC = 0; // clock source select bit (synchronous mode only) [198]
// 0: Slave mode (clock generated by external source)
// 1: Master mode (clock generated internally from BRG)
// if receive functionality is to be included in the
// software build, include code that is specific to
// initializing the receiver
#ifdef ENABLE_SERIAL_PORT_TWO_RX
//
TRISCbits.TRISC2 = 1; // make sure the RG2/RX2/DT2 pin is configured as an input [120]
//
RCSTAbits.RX9 = 0; // 9-bit receive enable bit [199]
// 0: 8-bit reception mode
// 1: 9-bit reception mode
//
RCSTAbits.ADDEN = 0; // address detect enable bit (9-bit asynchronous mode only) [199]
// 0: disables address detection
// 1: enables address detection
//
RCSTAbits.SREN = 1; // single receive enable bit (master synchronous mode only) [199]
// 0: disables single receive mode
// 1: enables single receive mode
//
RCSTAbits.CREN = 1; // continuous receive mode enable bit [199]
// asynchronous mode:
// 0: disables receiver
// 1: enable receiver
// synchronous mode:
// 0: disables continuous receive
// 1: enables continuous receive until CREN is cleared [199]
//
IPR1bits.RCIP = 0; // receive interrupt priority bit (must be 0 for IFI controllers) [98]
// 0: low-priority
// 1: high-priority
//
PIE1bits.RCIE = 1; // receive interrupt enable bit [95]
// 0: disables received data interrupt
// 1: enables received data interrupt
#endif //
// if transmit functionality is to be included in the
// software build, include code that is specific to
// initializing the serial port transmitter
#ifdef ENABLE_SERIAL_PORT_TWO_TX
//
stdout = _H_USER; // use this driver for output stream functions
//
//
TRISCbits.TRISC1 = 0; // make sure the RG1/TX2/CK2 pin is configured as an output [120] changed to trisC
//
TXSTAbits.TX9 = 0; // 9-bit transmit enable bit [198]
// 0: 8-bit transmission mode
// 1: 9-bit transmission mode
//
IPR1bits.TXIP = 0; // transmit interrupt priority bit (must be 0 for IFI controllers) [98]
// 0: low-priority
// 1: high-priority
//
PIE1bits.TXIE = 1; // transmit interrupt enable bit [95]
// 0: disables transmit register empty interrupt
// 1: enables transmit register empty interrupt
//
TXSTAbits.TXEN = 1; // Enable transmitter [198]
// 0: serial transmitter is disabled
// 1: serial transmitter
#endif //
// finally, turn on the serial port
RCSTAbits.SPEN = 1; // Serial Port Enable [199]
// 0: serial port is disabled
// 1: serial port is enabled
}
/************************************************** *****************************
*
* FUNCTION: Serial_Port_Two_Byte_Count()
*
* PURPOSE: Returns the number of bytes in serial port
* two's received data queue.
*
* CALLED FROM:
*
* PARAMETERS: none
*
* RETURNS: unsigned char
*
* COMMENTS: This function must be called to determine how much data,
* if any, is present in serial port two's received data
* queue. If the returned number is greater than zero, then
* a call to Read_Serial_Port_Two() can be made to retrieve
* the next byte.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_RX
unsigned char Serial_Port_Two_Byte_Count(void)
{
unsigned char temp;
// since we're about to use the Rx_1_Queue_Byte_Count variable,
// which can also be modified in the interrupt service routine,
// let's briefly disable the serial port interrupt to make sure
// that Rx_1_Queue_Byte_Count doesn't get altered while we're
// using it.
PIE1bits.RCIE = 0;
// now we can get a local copy of the byte count without fear
// that we'll get corrupted data
temp = Rx_2_Queue_Byte_Count;
// okay, we have a local copy of the byte count, so turn the
// serial port interrupt back on.
PIE1bits.RCIE = 1;
// return the byte count
return(temp);
}
#endif
/************************************************** *****************************
*
* FUNCTION: Read_Serial_Port_Two()
*
* PURPOSE:
*
* CALLED FROM:
*
* PARAMETERS: none
*
* RETURNS: unsigned char
*
* COMMENTS: This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_RX
unsigned char Read_Serial_Port_Two(void)
{
unsigned char byte;
if(Rx_2_Queue_Empty)
{
// error: no data to read
return(0);
}
else
{
// get a byte from the circular queue and store it temporarily
byte = Rx_2_Queue[Rx_2_Queue_Read_Index];
// decrement the queue byte count
Rx_2_Queue_Byte_Count--;
// increment the read pointer
Rx_2_Queue_Read_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Rx_2_Queue_Read_Index &= RX_2_QUEUE_INDEX_MASK;
// since we're about to use the Rx_2_Queue_Write_Index variable, which can
// also be modified in the interrupt service routine, let's briefly disable
// the serial port interrupt to make sure that Rx_2_Queue_Write_Index doesn't
// get altered while we're using it.
PIE1bits.RCIE = 0;
// is the circular queue now empty?
if(Rx_2_Queue_Read_Index == Rx_2_Queue_Write_Index)
{
Rx_2_Queue_Empty = TRUE;
}
// okay, we're done using Rx_2_Queue_Write_Index, so turn the serial port
// interrupt back on.
PIE1bits.RCIE = 1;
// Since we've just removed a byte to the queue, it can't possibly be full.
// Again, this is quicker than using an if() statement every time
Rx_2_Queue_Full = FALSE;
// return the data
return(byte);
}
}
#endif
/************************************************** *****************************
*
* FUNCTION: Write_Serial_Port_Two()
*
* PURPOSE: Sends a byte of data using serial port two.
*
* CALLED FROM:
*
* PARAMETERS: unsigned char
*
* RETURNS: nothing
*
* COMMENTS: If you don't initialize the serial port before calling
* this function, the robot controller will stop functioning
* and you'll get the much dreaded red-light-of-death. This
* is because the while() statement below is waiting for the
* transmit circuitry to send another byte, but if the serial
* port hasn't been configured, nothing will be transmitted
* and we'll be stuck in the while() loop.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_TX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_TX
void Write_Serial_Port_Two(unsigned char byte)
{
// if the queue is full, wait here until space is available
while(Tx_2_Queue_Full);
// put the byte on the circular queue
Tx_2_Queue[Tx_2_Queue_Write_Index] = byte;
// increment the queue byte count
Tx_2_Queue_Byte_Count++;
// increment the write pointer
Tx_2_Queue_Write_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Tx_2_Queue_Write_Index &= TX_2_QUEUE_INDEX_MASK;
// since we're about to use the Tx_2_Queue_Write_Index variable, which can
// also be modified in the interrupt service routine, let's briefly disable
// the serial port interrupt to make sure that Tx_2_Queue_Write_Index doesn't
// get altered while we're using it.
PIE1bits.TXIE = 0;
// is the circular queue now full?
if(Tx_2_Queue_Read_Index == Tx_2_Queue_Write_Index)
{
Tx_2_Queue_Full = TRUE;
}
// okay, we're done using Tx_2_Queue_Write_Index, so turn the serial port
// interrupt back on.
PIE1bits.TXIE = 1;
// Since we've just added a byte to the queue, it can't possibly be empty.
// Again, this is quicker than using an if() statement every time
Tx_2_Queue_Empty = FALSE;
}
#endif
/************************************************** *****************************
*
* FUNCTION: Rx_2_Int_Handler()
*
* PURPOSE: Serial port two new data interrupt handler.
*
* CALLED FROM: user_routines_fast()
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS: If the interrupt handler was installed correctly, this
* function will be called every time a new byte of data
* is received by serial port two.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_RX
void Rx_2_Int_Handler(void)
{
if(Rx_2_Queue_Full)
{
// just turn off the serial port interrupt if we can't store any more data.
// the interrupt will be re-enabled within the Receive_Byte() function when
// more data is read.
PIE1bits.RCIE = 0;
}
else
{
// put the byte on the circular queue
Rx_2_Queue[Rx_2_Queue_Write_Index] = RCREG;
// if the interrupt handler was disabled while data was being received,
// data may have backed-up in the receiver circuitry, causing an overrun
// condition. So let's check the OERR bit to see if this has happened
// and if it has, we'll need to reset the serial port receiver circuitry
// to get data flowing again.
if(RCSTAbits.OERR)
{
// reset by turning off the receiver circuitry, then...
RCSTAbits.CREN = 0;
// ...turn it back on
RCSTAbits.CREN = 1;
// indicate that we've had an error
RX_2_Overrun_Errors++;
}
// if incoming data gets misaligned and the receiver doesn't receive a
// stop bit where it expects to detect it, the receiver circuitry will
// set the FERR bit to indicate that it's received corrupted data. The
// likely reason for this is an incorrectly set baud rate on either the
// receiver or transmitter end.
if(RCSTAbits.FERR)
{
RX_2_Framing_Errors++;
}
// increment the queue byte count
Rx_2_Queue_Byte_Count++;
// increment the write pointer
Rx_2_Queue_Write_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Rx_2_Queue_Write_Index &= RX_2_QUEUE_INDEX_MASK;
// is the circular queue now full?
if(Rx_2_Queue_Read_Index == Rx_2_Queue_Write_Index)
{
Rx_2_Queue_Full = TRUE;
}
// Since we've just added a byte to the queue, it can't possibly be empty.
// Again, this is quicker than using an if() statement every time
Rx_2_Queue_Empty = FALSE;
}
}
#endif
/************************************************** *****************************
*
* FUNCTION: Tx_2_Int_Handler()
*
* PURPOSE: Serial port two empty transmit buffer interrupt handler.
*
* CALLED FROM: user_routines_fast()
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS: If the interrupt handler was installed correctly, this
* function will be called every time serial port two is
* ready to start sending a byte of data.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_TX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_TX
void Tx_2_Int_Handler(void)
{
if(Tx_2_Queue_Empty)
{
// just turn off the serial port interrupt if we don't have data to send.
// the interrupt will be re-enabled within the Send_Byte() function when
// more data is sent.
PIE1bits.TXIE = 0;
}
else
{
// get a byte from the circular queue and send it to the USART
TXREG = Tx_2_Queue[Tx_2_Queue_Read_Index];
// decrement the queue byte count
Tx_2_Queue_Byte_Count--;
// increment the read pointer
Tx_2_Queue_Read_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Tx_2_Queue_Read_Index &= TX_2_QUEUE_INDEX_MASK;
// is the circular queue now empty?
if(Tx_2_Queue_Read_Index == Tx_2_Queue_Write_Index)
{
Tx_2_Queue_Empty = TRUE;
}
// Since we've just removed a byte from the queue, it can't possibly be full.
// Again, this is quicker than using an if() statement every time
Tx_2_Queue_Full = FALSE;
}
}
#endif
/************************************************** *****************************
*
* FUNCTION: _user_putc()
*
* PURPOSE: putc() interface to C18 2.4 output stream functions
*
* CALLED FROM:
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS:
*
************************************************** *****************************/
void _user_putc(unsigned char byte)
{
if(stdout_serial_port == NUL)
{
// send the data to the bit bucket
}
#ifdef ENABLE_SERIAL_PORT_ONE_TX
else if(stdout_serial_port == SERIAL_PORT_ONE)
{
// send the data to serial port one
Write_Serial_Port_One(byte);
}
#endif
#ifdef ENABLE_SERIAL_PORT_TWO_TX
else if(stdout_serial_port == SERIAL_PORT_TWO)
{
// send the data to serial port two
Write_Serial_Port_Two(byte);
}
#endif
}
serial_ports.h
/************************************************** *****************************
*
* TITLE: serial_ports.h
*
* VERSION: 0.3 (Beta)
*
* DATE: 05-Feb-2005
*
* AUTHOR: R. Kevin Watson
* kevinw@jpl.nasa.gov
*
* COMMENTS: You are free to use this source code for any non-commercial
* use. Please do not make copies of this source code, modified
* or un-modified, publicly available on the internet or elsewhere
* without permission. Thanks.
*
* Copyright ©2004-2005 R. Kevin Watson. All rights are reserved.
*
************************************************** ******************************
*
* CHANGE LOG:
*
* DATE REV DESCRIPTION
* ----------- --- ----------------------------------------------------------
* 22-Dec-2004 0.1 RKW - Original code.
* 28-Dec-2004 0.2 RKW - Using preprocessor directives, added the ability
* to enable/disable individual serial port receive and
* transmit code. Updated documentation.
* 05-Feb-2005 0.3 RKW - Added _user_putc() interface to C18's new output
* stream capabilities. Updated documentation.
*
************************************************** *****************************/
#ifndef _SERIAL_PORTS_H
#define _SERIAL_PORTS_H
// comment out the next line to disable serial port one
// receive functionality
//#define ENABLE_SERIAL_PORT_ONE_RX
// comment out the next line to disable serial port one
// transmit functionality
//#define ENABLE_SERIAL_PORT_ONE_TX
// comment out the next line to disable serial port two
// receive functionality
#define ENABLE_SERIAL_PORT_TWO_RX
// comment out the next line to disable serial port two
// transmit functionality
#define ENABLE_SERIAL_PORT_TWO_TX
// Sample values that can be plugged into the SPBRGx register to program the
// baud rate generator for a specific baud rate. Make sure to also set the BRGH
// bit accordingly. See the Init_Serial_Port_One() and Init_Serial_Port_Two()
// functions in the serial_ports.c source file for more information.
#define BAUD_4800 129 // set BRGH = 0
#define BAUD_9600 64 // set BRGH = 0
#define BAUD_14400 42 // set BRGH = 0
#define BAUD_19200 129 // set BRGH = 1
#define BAUD_38400 64 // set BRGH = 1
#define BAUD_57600 42 // set BRGH = 1
#define BAUD_115200 21 // set BRGH = 1
#define BAUD_230400 10 // set BRGH = 1
// These values define the size, in bytes, of the four circular queues used to
// buffer incoming and outgoing serial port data. The size of the receive queue
// is critical to prevent data loss. As an example, if you're continuously
// receiving data at 38,400 baud and only call Read_Serial_Port_xxx() in the
// Process_Data_From_Master_uP() loop, which is called every 26.2ms, you'll
// need to size the queue to accept all of the data you expect to receive in
// those 26.2ms. To do this, multiply the rate at which bytes of data are being
// received times the amount of time between calls to Read_Serial_Port_xxx().
// In the above example, we're receiving data at a rate of 3840 bytes per second
// (8 bits of data plus 2 bits of overhead per byte transferred) and we need to
// store at least 26.2ms-worth of that data for a calculated queue size of 101
// bytes. Because the queue size must be a power of two, we'll need to round-up
// to a queue size of 128. Another solution is to check for received serial data
// at a higher rate by putting the call to Read_Serial_Port_xxx() in the much
// faster Process_Data_From_Local_IO() loop. As mentioned above, these values
// must be a power of two (i.e.,8,16,32,64,128) for the circular queue algorithm
// to function correctly.
#define RX_1_QUEUE_SIZE 32
#define TX_1_QUEUE_SIZE 16
#define RX_2_QUEUE_SIZE 32
#define TX_2_QUEUE_SIZE 16
// The circular queue algorithm will break if these values are altered.
#define RX_1_QUEUE_INDEX_MASK RX_1_QUEUE_SIZE-1
#define TX_1_QUEUE_INDEX_MASK TX_1_QUEUE_SIZE-1
#define RX_2_QUEUE_INDEX_MASK RX_2_QUEUE_SIZE-1
#define TX_2_QUEUE_INDEX_MASK TX_2_QUEUE_SIZE-1
#ifndef FALSE
#define TRUE 1
#define FALSE 0
#endif
// #defines used with the stdout_serial_port global variable
#define NUL 0
#define SERIAL_PORT_ONE 1
#define SERIAL_PORT_TWO 2
// if needed, declare functions and global variables that
// are specific to serial port one receiver functionality
#ifdef ENABLE_SERIAL_PORT_ONE_RX
void Init_Serial_Port_One(void);
unsigned char Serial_Port_One_Byte_Count(void);
unsigned char Read_Serial_Port_One(void);
void Rx_1_Int_Handler(void);
extern volatile unsigned char RX_1_Framing_Errors;
extern volatile unsigned char RX_1_Overrun_Errors;
#endif
// if needed, declare functions that are specific to serial
// port one transmit functionality
#ifdef ENABLE_SERIAL_PORT_ONE_TX
extern unsigned char stdout_serial_port;
void _user_putc(unsigned char);
void Init_Serial_Port_One(void);
void Write_Serial_Port_One(unsigned char);
void Tx_1_Int_Handler(void);
#endif
// if needed, declare functions and global variables that
// are specific to serial port two receiver functionality
#ifdef ENABLE_SERIAL_PORT_TWO_RX
void Init_Serial_Port_Two(void);
unsigned char Serial_Port_Two_Byte_Count(void);
unsigned char Read_Serial_Port_Two(void);
void Rx_2_Int_Handler(void);
extern volatile unsigned char RX_2_Framing_Errors;
extern volatile unsigned char RX_2_Overrun_Errors;
#endif
// if needed, declare functions that are specific to serial
// port two transmit functionality
#ifdef ENABLE_SERIAL_PORT_TWO_TX
extern unsigned char stdout_serial_port;
void _user_putc(unsigned char);
void Init_Serial_Port_Two(void);
void Write_Serial_Port_Two(unsigned char);
void Tx_2_Int_Handler(void);
#endif
#endif
user_routines.c
************************************************** *****************************/
#include <stdio.h>
#include "user_routines.h"
#include "p18f452.h"
#include "accel.h"
#include "default.h"
#include "interrupts.h"
#include "encoders.h"
#include "ifi_utilities.h"
#include "serial_ports.h"
/************************************************** *****************************
* FUNCTION NAME: User_Initialization
* PURPOSE: This routine is called first (and only once) in the Main function.
* You may modify and add to this function.
* CALLED FROM: main.c
* ARGUMENTS: none
* RETURNS: void
************************************************** *****************************/
void User_Initialization(void)
{
/* FIRST: Set up the I/O pins you want to use as digital INPUTS. */
/* SECOND: Set up the I/O pins you want to use as digital OUTPUTS. */
/* THIRD: Initialize the values on the digital outputs. */
/* Add any other initialization code here. */
Initialize_Accel();
Initialize_Interrupts();
Initialize_Timer_0();
Initialize_Timer_1();
Initialize_Timer_2();
Initialize_Timer_3();
Initialize_Encoder();
Init_Serial_Port_Two();
stdout_serial_port = SERIAL_PORT_TWO;
}
/************************************************** *****************************
* FUNCTION NAME: Process_Data
* PURPOSE: duh, processes all the data
* CALLED FROM: main.c
* ARGUMENTS: none
* RETURNS: void
************************************************** *****************************/
void Process_Data(void)
{
static unsigned char i;
static unsigned char delay;
int leftencode;
int rightencode;
int x_accel;
int y_accel;
leftencode=Get_Left_Encoder_Count();
rightencode=Get_Right_Encoder_Count();
x_accel=get_x_accel();
y_accel=get_y_accel();
//printf("lefte = %d\n",leftencode);
//printf("righte = %d\n",rightencode);
//printf("yaccel = %d\n",x_accel);
//printf("xaccel = %d\n",y_accel);
printf("matt's a noob\r\n");
}
/************************************************** ****************************/
/************************************************** ****************************/
/************************************************** ****************************/
//the following used to be in a seperate file but it was unecessary for my purposes
/************************************************** *****************************
* FUNCTION NAME: InterruptVectorLow
* PURPOSE: Low priority interrupt vector
* CALLED FROM: nowhere by default
* ARGUMENTS: none
* RETURNS: void
* DO NOT MODIFY OR DELETE THIS FUNCTION
************************************************** *****************************/
#pragma code InterruptVectorLow = LOW_INT_VECTOR
void InterruptVectorLow (void)
{
_asm
goto InterruptHandlerLow /*jump to interrupt routine*/
_endasm
}
/************************************************** *****************************
* FUNCTION NAME: InterruptHandlerLow
* PURPOSE: Low priority interrupt handler
* If you want to use these external low priority interrupts or any of the
* peripheral interrupts then you must enable them in your initialization
* routine.
* CALLED FROM: this file, InterruptVectorLow routine
* ARGUMENTS: none
* RETURNS: void
************************************************** *****************************/
void InterruptHandlerLow()
{
if(PIR1bits.CCP1IF && PIE1bits.CCP1IE) // CCP1 Interrupt?
{
PIR1bits.CCP1IF = 0;
CCP_1_ISR(); // call the CCP1 ISR (in accel.c)
}
else if (PIR2bits.CCP2IF && PIE2bits.CCP2IE) // CCP2 Interrupt?
{
PIR2bits.CCP2IF = 0;
CCP_2_ISR(); // call the CCP2 ISR (in accel.c)
}
else if (INTCONbits.RBIF && INTCONbits.RBIE) // external interrupts 3 through 6?
{
//Port_B = PORTB; // remove the "mismatch condition" by reading port b
INTCONbits.RBIF = 0; // clear the interrupt flag [89]
EncoderTask(PORTB);
}
///////////////////////////////////////////////////////////////////////////
else if (PIR1bits.RCIF && PIE1bits.RCIE) // rx2 interrupt?
{
#ifdef ENABLE_SERIAL_PORT_TWO_RX
Rx_2_Int_Handler(); // call the rx2 interrupt handler (in serial_ports.c)
#endif
}
else if (PIR1bits.TXIF && PIE1bits.TXIE) // tx2 interrupt?
{
#ifdef ENABLE_SERIAL_PORT_TWO_TX
Tx_2_Int_Handler(); // call the tx2 interrupt handler (in serial_ports.c)
#endif
}
///////////////////////////////////////////////////////////////////////
}
i've changed all the registers to work on a 18f452 (which i have on a demo board with a serial port hooked up) the serial port is live as we used a multimeter to test and its data ready line is open at 10.8 volts if i remember correctly. so i don't think its the connection from board to PC
when i step thorugh on my icd 2 debugger the code loops and gets stuck here:
// if the queue is full, wait here until space is available
while(Tx_2_Queue_Full);//<----------the queue never empties
// put the byte on the circular queue
Tx_2_Queue[Tx_2_Queue_Write_Index] = byte;
// increment the queue byte count
Tx_2_Queue_Byte_Count++;
// increment the write pointer
Tx_2_Queue_Write_Index++;
any ideas? i've been trying to get this serial driver working for a week now. i'm seriously stuck and at a loss for ways to fix it
heres the whole files
serial_ports.c
/************************************************** *****************************
*
* TITLE: serial_ports.c
*
* VERSION: 0.3 (Beta)
*
* DATE: 05-Feb-2005
*
* AUTHOR: R. Kevin Watson
* kevinw@jpl.nasa.gov
*
* COMMENTS: The source code in this file implements a fully buffered,
* interrupt-driven serial port driver that can be used with
* either or both on-board serial ports.
*
* This source code will work with the Robovation (A/K/A EDU-RC)
* robot controller and the FIRST Robotics robot controller.
*
* By default, serial port one will operate at 115200 baud, which
* is compatible with InnovationFIRST's terminal program, and
* serial port two will operate at 9600 baud. These values can be
* easily changed by modifying the serial port initialization
* functions mentioned below.
*
* This file is best viewed with tabs set to four characters.
*
* Nine things must be done before this software will work
* correctly:
*
* 1a) FRC-RC: As this software is intended to replace IFI's
* serial port driver, the call to Serial_Driver_Initialize()
* in user_routines.c / User_Initialization() should be
* removed or commented out. In addition, all references to
* "user_Serialdrv.c" and "user_Serialdrv.h" must be removed
* from the project and all project source files.
*
* 1b) EDU-RC: As this software is intended to replace IFI's
* serial port driver, the call to Initialize_Serial_Comms()
* in user_routines.c / User_Initialization() should be
* removed or commented out. In addition, all references to
* "printf_lib.c" and "printf_lib.h" must be removed from
* the project and all project source files.
*
* 2) You must add the serial_ports.c/.h source files to
* your MPLAB project.
*
* 3) A #include statement for the serial_ports.h header
* file must be included at the beginning of each source
* file that uses the serial ports. The statement should
* look like this: #include "serial_ports.h".
*
* 4) If you intend to use the C18 output stream functions,
* A #include statement for the stdio.h header file must be
* included at the beginning of each source file that calls
* any of these functions. The statement should look like
* this: #include <serial_ports.h>.
*
* 5) Init_Serial_Port_One() and/or Init_Serial_Port_Two()
* must be called from the User_Initialization() function
* located in the user_routines.c source file.
*
* 6) The interrupt handler(s) must be installed in the
* InterruptHandlerLow() function located in the
* user_routines_fast.c source file. See the accompanying
* copy of user_routines_fast.c to see how this is done.
*
* 7) Decide what functionality you need and comment out the
* #define ENABLE_SERIAL_PORT_xxx_yy entries in serial_ports.h
* as necessary. As an example, if you only need to send data
* using serial port one and would like to reclaim the resources
* used by serial port two and serial port one's receiver
* source code, the top of the serial_ports.h file would look
* like this:
*
* // comment out the next line to disable all serial port one
* // receive functionality
* // #define ENABLE_SERIAL_PORT_ONE_RX
*
* // comment out the next line to disable all serial port one
* // transmit functionality
* #define ENABLE_SERIAL_PORT_ONE_TX
*
* // comment out the next line to disable all serial port two
* // receive functionality
* // #define ENABLE_SERIAL_PORT_TWO_RX
*
* // comment out the next line to disable all serial port two
* // transmit functionality
* // #define ENABLE_SERIAL_PORT_TWO_TX
*
* By default, both serial ports and their respective receive
* and transmit sections are enabled.
*
* 8) As the default output device for C18's output stream
* functions is the null device, you'll presumably want to
* change the value of stdout_serial_port to "SERIAL_PORT_ONE"
* or "SERIAL_PORT_TWO" if you want to see printf()'s output.
* User_Initialization() is a good place to do this.
*
* 9) To support terminal emulation software, \r\n should
* be used instead of just \n in the printf() format string.
*
*
* This serial port driver can send output stream data to either
* of the serial ports by setting the value of the global variable
* stdout_serial_port before calling output stream functions like
* printf(). Setting the value to "SERIAL_PORT_ONE" will send the
* output to serial port one. Likewise, setting the value to
* "SERIAL_PORT_TWO" will send the output to serial port two.
* Setting the value to "NUL" will send the output to the null
* device, meaning that the output is sent nowhere. These values
* are #define'd in serial_ports.h. As an example,
*
* stdout_serial_port = SERIAL_PORT_ONE;
* printf("Kernighan");
* stdout_serial_port = NUL;
* printf("and");
* stdout_serial_port = SERIAL_PORT_TWO;
* printf("Ritchie");
*
* will send the text "Kernighan" to the peripheral device attached
* to serial port one, the text "Ritchie" to the device attached to
* serial port two and the text "and" won't be sent anywhere.
* By default, output is sent to the null device, which is the only
* output device guaranteed to be present.
*
* You are free to use this source code for any non-commercial
* use. Please do not make copies of this source code, modified
* or un-modified, publicly available on the internet or
* elsewhere without permission. Thanks.
*
* Copyright ©2004-2005 R. Kevin Watson. All rights are reserved.
*
************************************************** ******************************
*
* CHANGE LOG:
*
* DATE REV DESCRIPTION
* ----------- --- ----------------------------------------------------------
* 22-Dec-2004 0.1 RKW - Original code.
* 28-Dec-2004 0.2 RKW - Using preprocessor directives, added the ability
* to enable/disable individual serial port receive and
* transmit code. Updated documentation.
* 05-Feb-2005 0.3 RKW - Added _user_putc() interface to C18's new output
* stream capabilities. Updated documentation.
*
************************************************** *****************************/
#include <stdio.h>
#include "p18f452.h"
#include "serial_ports.h"
// by default stdout stream output is sent to the null device,
// which is the only device guaranteed to be present.
unsigned char stdout_serial_port = NUL;
//
// Serial Port 2 Receive Variables:
//
#ifdef ENABLE_SERIAL_PORT_TWO_RX
volatile unsigned char Rx_2_Queue[RX_2_QUEUE_SIZE]; // serial port 2's receive circular queue
volatile unsigned char Rx_2_Queue_Full = FALSE; // flag that indicates that serial port 2's
// receive circular queue is full and cannot
// accept any more data
unsigned char Rx_2_Queue_Empty = TRUE; // flag that indicates that there is no more
// data present in serial port 2's receive
// circular queue
unsigned char Rx_2_Queue_Read_Index = 0; // read index into serial port 2's receive
// circular queue
volatile unsigned char Rx_2_Queue_Write_Index = 0; // write index into serial port 2's receive
// circular queue
volatile unsigned char Rx_2_Queue_Byte_Count = 0; // number of bytes in serial port 2's receive
// circular queue
volatile unsigned char RX_2_Overrun_Errors = 0; // number of overrun errors that have occurred
// in serial port 2's receive circuitry since
// the last reset
volatile unsigned char RX_2_Framing_Errors = 0; // number of framing errors that have occurred
// in serial port 2's receive circuitry since
// the last reset
#endif
//
// Serial Port 2 Transmit Variables:
//
#ifdef ENABLE_SERIAL_PORT_TWO_TX
volatile unsigned char Tx_2_Queue[TX_2_QUEUE_SIZE]; // serial port 2's transmit circular queue
volatile unsigned char Tx_2_Queue_Full = FALSE; // flag that indicates that serial port 2's
// transmit circular queue is full and cannot
// accept any more data
volatile unsigned char Tx_2_Queue_Empty = TRUE; // flag that indicates that there is no more
// data to send in serial port 2's transmit
// circular queue
volatile unsigned char Tx_2_Queue_Read_Index = 0; // read index into serial port 2's transmit
// circular queue
unsigned char Tx_2_Queue_Write_Index = 0; // write index into serial port 2's transmit
// circular queue
volatile unsigned char Tx_2_Queue_Byte_Count = 0; // number of bytes in serial port 2's transmit
// circular queue
#endif
/************************************************** *****************************
*
* FUNCTION: Init_Serial_Port_Two()
*
* PURPOSE: Initializes serial port two for asynchronous operation
*
* CALLED FROM: user_routines.c/User_Initialization()
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS: This function must be called before you try to use serial
* port two.
*
* By default, this serial port is set to 9600 baud with
* the transmitter and receiver enabled.
*
* The serial port's baud rate is programmed by entering
* a value into the SPBRG2 register and possibly changing
* the value of the BRGH bit. Several example values are
* included in the serial_ports.h file.
*
* Numbers within brackets refer to the PIC18F8520 data
* sheet page number where more information can be found.
* This document can be found at microchip's website at
* http://www.microchip.com or at the author's website at
* http://www.kevin.org/frc
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX or ENABLE_SERIAL_PORT_TWO_TX
* is #define'd in serial_ports.h
*
************************************************** *****************************/
void Init_Serial_Port_Two(void)
{
// Start by initializing the serial port with code
// common to receive and transmit functions
SPBRG = BAUD_19200; // baud rate generator register [200]
//
TXSTAbits.BRGH = 1; // high baud rate select bit (asynchronous mode only) [198]
// 0: low speed
// 1: high speed
//
PIE1bits.RCIE = 0; // receive interrupt enable bit [95]
// 0: disables received data interrupt
// 1: enables received data interrupt
//
PIE1bits.TXIE = 0; // transmit interrupt enable bit [95]
// 0: disables transmit register empty interrupt
// 1: enables transmit register empty interrupt
//
TXSTAbits.SYNC = 0; // USART mode select bit [198]
// 0: asynchronous mode
// 1: synchronous mode
//
TXSTAbits.CSRC = 0; // clock source select bit (synchronous mode only) [198]
// 0: Slave mode (clock generated by external source)
// 1: Master mode (clock generated internally from BRG)
// if receive functionality is to be included in the
// software build, include code that is specific to
// initializing the receiver
#ifdef ENABLE_SERIAL_PORT_TWO_RX
//
TRISCbits.TRISC2 = 1; // make sure the RG2/RX2/DT2 pin is configured as an input [120]
//
RCSTAbits.RX9 = 0; // 9-bit receive enable bit [199]
// 0: 8-bit reception mode
// 1: 9-bit reception mode
//
RCSTAbits.ADDEN = 0; // address detect enable bit (9-bit asynchronous mode only) [199]
// 0: disables address detection
// 1: enables address detection
//
RCSTAbits.SREN = 1; // single receive enable bit (master synchronous mode only) [199]
// 0: disables single receive mode
// 1: enables single receive mode
//
RCSTAbits.CREN = 1; // continuous receive mode enable bit [199]
// asynchronous mode:
// 0: disables receiver
// 1: enable receiver
// synchronous mode:
// 0: disables continuous receive
// 1: enables continuous receive until CREN is cleared [199]
//
IPR1bits.RCIP = 0; // receive interrupt priority bit (must be 0 for IFI controllers) [98]
// 0: low-priority
// 1: high-priority
//
PIE1bits.RCIE = 1; // receive interrupt enable bit [95]
// 0: disables received data interrupt
// 1: enables received data interrupt
#endif //
// if transmit functionality is to be included in the
// software build, include code that is specific to
// initializing the serial port transmitter
#ifdef ENABLE_SERIAL_PORT_TWO_TX
//
stdout = _H_USER; // use this driver for output stream functions
//
//
TRISCbits.TRISC1 = 0; // make sure the RG1/TX2/CK2 pin is configured as an output [120] changed to trisC
//
TXSTAbits.TX9 = 0; // 9-bit transmit enable bit [198]
// 0: 8-bit transmission mode
// 1: 9-bit transmission mode
//
IPR1bits.TXIP = 0; // transmit interrupt priority bit (must be 0 for IFI controllers) [98]
// 0: low-priority
// 1: high-priority
//
PIE1bits.TXIE = 1; // transmit interrupt enable bit [95]
// 0: disables transmit register empty interrupt
// 1: enables transmit register empty interrupt
//
TXSTAbits.TXEN = 1; // Enable transmitter [198]
// 0: serial transmitter is disabled
// 1: serial transmitter
#endif //
// finally, turn on the serial port
RCSTAbits.SPEN = 1; // Serial Port Enable [199]
// 0: serial port is disabled
// 1: serial port is enabled
}
/************************************************** *****************************
*
* FUNCTION: Serial_Port_Two_Byte_Count()
*
* PURPOSE: Returns the number of bytes in serial port
* two's received data queue.
*
* CALLED FROM:
*
* PARAMETERS: none
*
* RETURNS: unsigned char
*
* COMMENTS: This function must be called to determine how much data,
* if any, is present in serial port two's received data
* queue. If the returned number is greater than zero, then
* a call to Read_Serial_Port_Two() can be made to retrieve
* the next byte.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_RX
unsigned char Serial_Port_Two_Byte_Count(void)
{
unsigned char temp;
// since we're about to use the Rx_1_Queue_Byte_Count variable,
// which can also be modified in the interrupt service routine,
// let's briefly disable the serial port interrupt to make sure
// that Rx_1_Queue_Byte_Count doesn't get altered while we're
// using it.
PIE1bits.RCIE = 0;
// now we can get a local copy of the byte count without fear
// that we'll get corrupted data
temp = Rx_2_Queue_Byte_Count;
// okay, we have a local copy of the byte count, so turn the
// serial port interrupt back on.
PIE1bits.RCIE = 1;
// return the byte count
return(temp);
}
#endif
/************************************************** *****************************
*
* FUNCTION: Read_Serial_Port_Two()
*
* PURPOSE:
*
* CALLED FROM:
*
* PARAMETERS: none
*
* RETURNS: unsigned char
*
* COMMENTS: This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_RX
unsigned char Read_Serial_Port_Two(void)
{
unsigned char byte;
if(Rx_2_Queue_Empty)
{
// error: no data to read
return(0);
}
else
{
// get a byte from the circular queue and store it temporarily
byte = Rx_2_Queue[Rx_2_Queue_Read_Index];
// decrement the queue byte count
Rx_2_Queue_Byte_Count--;
// increment the read pointer
Rx_2_Queue_Read_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Rx_2_Queue_Read_Index &= RX_2_QUEUE_INDEX_MASK;
// since we're about to use the Rx_2_Queue_Write_Index variable, which can
// also be modified in the interrupt service routine, let's briefly disable
// the serial port interrupt to make sure that Rx_2_Queue_Write_Index doesn't
// get altered while we're using it.
PIE1bits.RCIE = 0;
// is the circular queue now empty?
if(Rx_2_Queue_Read_Index == Rx_2_Queue_Write_Index)
{
Rx_2_Queue_Empty = TRUE;
}
// okay, we're done using Rx_2_Queue_Write_Index, so turn the serial port
// interrupt back on.
PIE1bits.RCIE = 1;
// Since we've just removed a byte to the queue, it can't possibly be full.
// Again, this is quicker than using an if() statement every time
Rx_2_Queue_Full = FALSE;
// return the data
return(byte);
}
}
#endif
/************************************************** *****************************
*
* FUNCTION: Write_Serial_Port_Two()
*
* PURPOSE: Sends a byte of data using serial port two.
*
* CALLED FROM:
*
* PARAMETERS: unsigned char
*
* RETURNS: nothing
*
* COMMENTS: If you don't initialize the serial port before calling
* this function, the robot controller will stop functioning
* and you'll get the much dreaded red-light-of-death. This
* is because the while() statement below is waiting for the
* transmit circuitry to send another byte, but if the serial
* port hasn't been configured, nothing will be transmitted
* and we'll be stuck in the while() loop.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_TX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_TX
void Write_Serial_Port_Two(unsigned char byte)
{
// if the queue is full, wait here until space is available
while(Tx_2_Queue_Full);
// put the byte on the circular queue
Tx_2_Queue[Tx_2_Queue_Write_Index] = byte;
// increment the queue byte count
Tx_2_Queue_Byte_Count++;
// increment the write pointer
Tx_2_Queue_Write_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Tx_2_Queue_Write_Index &= TX_2_QUEUE_INDEX_MASK;
// since we're about to use the Tx_2_Queue_Write_Index variable, which can
// also be modified in the interrupt service routine, let's briefly disable
// the serial port interrupt to make sure that Tx_2_Queue_Write_Index doesn't
// get altered while we're using it.
PIE1bits.TXIE = 0;
// is the circular queue now full?
if(Tx_2_Queue_Read_Index == Tx_2_Queue_Write_Index)
{
Tx_2_Queue_Full = TRUE;
}
// okay, we're done using Tx_2_Queue_Write_Index, so turn the serial port
// interrupt back on.
PIE1bits.TXIE = 1;
// Since we've just added a byte to the queue, it can't possibly be empty.
// Again, this is quicker than using an if() statement every time
Tx_2_Queue_Empty = FALSE;
}
#endif
/************************************************** *****************************
*
* FUNCTION: Rx_2_Int_Handler()
*
* PURPOSE: Serial port two new data interrupt handler.
*
* CALLED FROM: user_routines_fast()
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS: If the interrupt handler was installed correctly, this
* function will be called every time a new byte of data
* is received by serial port two.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_RX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_RX
void Rx_2_Int_Handler(void)
{
if(Rx_2_Queue_Full)
{
// just turn off the serial port interrupt if we can't store any more data.
// the interrupt will be re-enabled within the Receive_Byte() function when
// more data is read.
PIE1bits.RCIE = 0;
}
else
{
// put the byte on the circular queue
Rx_2_Queue[Rx_2_Queue_Write_Index] = RCREG;
// if the interrupt handler was disabled while data was being received,
// data may have backed-up in the receiver circuitry, causing an overrun
// condition. So let's check the OERR bit to see if this has happened
// and if it has, we'll need to reset the serial port receiver circuitry
// to get data flowing again.
if(RCSTAbits.OERR)
{
// reset by turning off the receiver circuitry, then...
RCSTAbits.CREN = 0;
// ...turn it back on
RCSTAbits.CREN = 1;
// indicate that we've had an error
RX_2_Overrun_Errors++;
}
// if incoming data gets misaligned and the receiver doesn't receive a
// stop bit where it expects to detect it, the receiver circuitry will
// set the FERR bit to indicate that it's received corrupted data. The
// likely reason for this is an incorrectly set baud rate on either the
// receiver or transmitter end.
if(RCSTAbits.FERR)
{
RX_2_Framing_Errors++;
}
// increment the queue byte count
Rx_2_Queue_Byte_Count++;
// increment the write pointer
Rx_2_Queue_Write_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Rx_2_Queue_Write_Index &= RX_2_QUEUE_INDEX_MASK;
// is the circular queue now full?
if(Rx_2_Queue_Read_Index == Rx_2_Queue_Write_Index)
{
Rx_2_Queue_Full = TRUE;
}
// Since we've just added a byte to the queue, it can't possibly be empty.
// Again, this is quicker than using an if() statement every time
Rx_2_Queue_Empty = FALSE;
}
}
#endif
/************************************************** *****************************
*
* FUNCTION: Tx_2_Int_Handler()
*
* PURPOSE: Serial port two empty transmit buffer interrupt handler.
*
* CALLED FROM: user_routines_fast()
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS: If the interrupt handler was installed correctly, this
* function will be called every time serial port two is
* ready to start sending a byte of data.
*
* This function will not be included in the build unless
* ENABLE_SERIAL_PORT_TWO_TX is #define'd in serial_ports.h
*
************************************************** *****************************/
#ifdef ENABLE_SERIAL_PORT_TWO_TX
void Tx_2_Int_Handler(void)
{
if(Tx_2_Queue_Empty)
{
// just turn off the serial port interrupt if we don't have data to send.
// the interrupt will be re-enabled within the Send_Byte() function when
// more data is sent.
PIE1bits.TXIE = 0;
}
else
{
// get a byte from the circular queue and send it to the USART
TXREG = Tx_2_Queue[Tx_2_Queue_Read_Index];
// decrement the queue byte count
Tx_2_Queue_Byte_Count--;
// increment the read pointer
Tx_2_Queue_Read_Index++;
// If the index pointer overflowed, cut-off the high-order bit. Doing this
// every time is quicker than checking for overflow every time with an if()
// statement and only then occasionally setting it back to zero. For this
// to work, the queue size must be a power of 2 (e.g., 16,32,64,128...).
Tx_2_Queue_Read_Index &= TX_2_QUEUE_INDEX_MASK;
// is the circular queue now empty?
if(Tx_2_Queue_Read_Index == Tx_2_Queue_Write_Index)
{
Tx_2_Queue_Empty = TRUE;
}
// Since we've just removed a byte from the queue, it can't possibly be full.
// Again, this is quicker than using an if() statement every time
Tx_2_Queue_Full = FALSE;
}
}
#endif
/************************************************** *****************************
*
* FUNCTION: _user_putc()
*
* PURPOSE: putc() interface to C18 2.4 output stream functions
*
* CALLED FROM:
*
* PARAMETERS: None
*
* RETURNS: Nothing
*
* COMMENTS:
*
************************************************** *****************************/
void _user_putc(unsigned char byte)
{
if(stdout_serial_port == NUL)
{
// send the data to the bit bucket
}
#ifdef ENABLE_SERIAL_PORT_ONE_TX
else if(stdout_serial_port == SERIAL_PORT_ONE)
{
// send the data to serial port one
Write_Serial_Port_One(byte);
}
#endif
#ifdef ENABLE_SERIAL_PORT_TWO_TX
else if(stdout_serial_port == SERIAL_PORT_TWO)
{
// send the data to serial port two
Write_Serial_Port_Two(byte);
}
#endif
}
serial_ports.h
/************************************************** *****************************
*
* TITLE: serial_ports.h
*
* VERSION: 0.3 (Beta)
*
* DATE: 05-Feb-2005
*
* AUTHOR: R. Kevin Watson
* kevinw@jpl.nasa.gov
*
* COMMENTS: You are free to use this source code for any non-commercial
* use. Please do not make copies of this source code, modified
* or un-modified, publicly available on the internet or elsewhere
* without permission. Thanks.
*
* Copyright ©2004-2005 R. Kevin Watson. All rights are reserved.
*
************************************************** ******************************
*
* CHANGE LOG:
*
* DATE REV DESCRIPTION
* ----------- --- ----------------------------------------------------------
* 22-Dec-2004 0.1 RKW - Original code.
* 28-Dec-2004 0.2 RKW - Using preprocessor directives, added the ability
* to enable/disable individual serial port receive and
* transmit code. Updated documentation.
* 05-Feb-2005 0.3 RKW - Added _user_putc() interface to C18's new output
* stream capabilities. Updated documentation.
*
************************************************** *****************************/
#ifndef _SERIAL_PORTS_H
#define _SERIAL_PORTS_H
// comment out the next line to disable serial port one
// receive functionality
//#define ENABLE_SERIAL_PORT_ONE_RX
// comment out the next line to disable serial port one
// transmit functionality
//#define ENABLE_SERIAL_PORT_ONE_TX
// comment out the next line to disable serial port two
// receive functionality
#define ENABLE_SERIAL_PORT_TWO_RX
// comment out the next line to disable serial port two
// transmit functionality
#define ENABLE_SERIAL_PORT_TWO_TX
// Sample values that can be plugged into the SPBRGx register to program the
// baud rate generator for a specific baud rate. Make sure to also set the BRGH
// bit accordingly. See the Init_Serial_Port_One() and Init_Serial_Port_Two()
// functions in the serial_ports.c source file for more information.
#define BAUD_4800 129 // set BRGH = 0
#define BAUD_9600 64 // set BRGH = 0
#define BAUD_14400 42 // set BRGH = 0
#define BAUD_19200 129 // set BRGH = 1
#define BAUD_38400 64 // set BRGH = 1
#define BAUD_57600 42 // set BRGH = 1
#define BAUD_115200 21 // set BRGH = 1
#define BAUD_230400 10 // set BRGH = 1
// These values define the size, in bytes, of the four circular queues used to
// buffer incoming and outgoing serial port data. The size of the receive queue
// is critical to prevent data loss. As an example, if you're continuously
// receiving data at 38,400 baud and only call Read_Serial_Port_xxx() in the
// Process_Data_From_Master_uP() loop, which is called every 26.2ms, you'll
// need to size the queue to accept all of the data you expect to receive in
// those 26.2ms. To do this, multiply the rate at which bytes of data are being
// received times the amount of time between calls to Read_Serial_Port_xxx().
// In the above example, we're receiving data at a rate of 3840 bytes per second
// (8 bits of data plus 2 bits of overhead per byte transferred) and we need to
// store at least 26.2ms-worth of that data for a calculated queue size of 101
// bytes. Because the queue size must be a power of two, we'll need to round-up
// to a queue size of 128. Another solution is to check for received serial data
// at a higher rate by putting the call to Read_Serial_Port_xxx() in the much
// faster Process_Data_From_Local_IO() loop. As mentioned above, these values
// must be a power of two (i.e.,8,16,32,64,128) for the circular queue algorithm
// to function correctly.
#define RX_1_QUEUE_SIZE 32
#define TX_1_QUEUE_SIZE 16
#define RX_2_QUEUE_SIZE 32
#define TX_2_QUEUE_SIZE 16
// The circular queue algorithm will break if these values are altered.
#define RX_1_QUEUE_INDEX_MASK RX_1_QUEUE_SIZE-1
#define TX_1_QUEUE_INDEX_MASK TX_1_QUEUE_SIZE-1
#define RX_2_QUEUE_INDEX_MASK RX_2_QUEUE_SIZE-1
#define TX_2_QUEUE_INDEX_MASK TX_2_QUEUE_SIZE-1
#ifndef FALSE
#define TRUE 1
#define FALSE 0
#endif
// #defines used with the stdout_serial_port global variable
#define NUL 0
#define SERIAL_PORT_ONE 1
#define SERIAL_PORT_TWO 2
// if needed, declare functions and global variables that
// are specific to serial port one receiver functionality
#ifdef ENABLE_SERIAL_PORT_ONE_RX
void Init_Serial_Port_One(void);
unsigned char Serial_Port_One_Byte_Count(void);
unsigned char Read_Serial_Port_One(void);
void Rx_1_Int_Handler(void);
extern volatile unsigned char RX_1_Framing_Errors;
extern volatile unsigned char RX_1_Overrun_Errors;
#endif
// if needed, declare functions that are specific to serial
// port one transmit functionality
#ifdef ENABLE_SERIAL_PORT_ONE_TX
extern unsigned char stdout_serial_port;
void _user_putc(unsigned char);
void Init_Serial_Port_One(void);
void Write_Serial_Port_One(unsigned char);
void Tx_1_Int_Handler(void);
#endif
// if needed, declare functions and global variables that
// are specific to serial port two receiver functionality
#ifdef ENABLE_SERIAL_PORT_TWO_RX
void Init_Serial_Port_Two(void);
unsigned char Serial_Port_Two_Byte_Count(void);
unsigned char Read_Serial_Port_Two(void);
void Rx_2_Int_Handler(void);
extern volatile unsigned char RX_2_Framing_Errors;
extern volatile unsigned char RX_2_Overrun_Errors;
#endif
// if needed, declare functions that are specific to serial
// port two transmit functionality
#ifdef ENABLE_SERIAL_PORT_TWO_TX
extern unsigned char stdout_serial_port;
void _user_putc(unsigned char);
void Init_Serial_Port_Two(void);
void Write_Serial_Port_Two(unsigned char);
void Tx_2_Int_Handler(void);
#endif
#endif
user_routines.c
************************************************** *****************************/
#include <stdio.h>
#include "user_routines.h"
#include "p18f452.h"
#include "accel.h"
#include "default.h"
#include "interrupts.h"
#include "encoders.h"
#include "ifi_utilities.h"
#include "serial_ports.h"
/************************************************** *****************************
* FUNCTION NAME: User_Initialization
* PURPOSE: This routine is called first (and only once) in the Main function.
* You may modify and add to this function.
* CALLED FROM: main.c
* ARGUMENTS: none
* RETURNS: void
************************************************** *****************************/
void User_Initialization(void)
{
/* FIRST: Set up the I/O pins you want to use as digital INPUTS. */
/* SECOND: Set up the I/O pins you want to use as digital OUTPUTS. */
/* THIRD: Initialize the values on the digital outputs. */
/* Add any other initialization code here. */
Initialize_Accel();
Initialize_Interrupts();
Initialize_Timer_0();
Initialize_Timer_1();
Initialize_Timer_2();
Initialize_Timer_3();
Initialize_Encoder();
Init_Serial_Port_Two();
stdout_serial_port = SERIAL_PORT_TWO;
}
/************************************************** *****************************
* FUNCTION NAME: Process_Data
* PURPOSE: duh, processes all the data
* CALLED FROM: main.c
* ARGUMENTS: none
* RETURNS: void
************************************************** *****************************/
void Process_Data(void)
{
static unsigned char i;
static unsigned char delay;
int leftencode;
int rightencode;
int x_accel;
int y_accel;
leftencode=Get_Left_Encoder_Count();
rightencode=Get_Right_Encoder_Count();
x_accel=get_x_accel();
y_accel=get_y_accel();
//printf("lefte = %d\n",leftencode);
//printf("righte = %d\n",rightencode);
//printf("yaccel = %d\n",x_accel);
//printf("xaccel = %d\n",y_accel);
printf("matt's a noob\r\n");
}
/************************************************** ****************************/
/************************************************** ****************************/
/************************************************** ****************************/
//the following used to be in a seperate file but it was unecessary for my purposes
/************************************************** *****************************
* FUNCTION NAME: InterruptVectorLow
* PURPOSE: Low priority interrupt vector
* CALLED FROM: nowhere by default
* ARGUMENTS: none
* RETURNS: void
* DO NOT MODIFY OR DELETE THIS FUNCTION
************************************************** *****************************/
#pragma code InterruptVectorLow = LOW_INT_VECTOR
void InterruptVectorLow (void)
{
_asm
goto InterruptHandlerLow /*jump to interrupt routine*/
_endasm
}
/************************************************** *****************************
* FUNCTION NAME: InterruptHandlerLow
* PURPOSE: Low priority interrupt handler
* If you want to use these external low priority interrupts or any of the
* peripheral interrupts then you must enable them in your initialization
* routine.
* CALLED FROM: this file, InterruptVectorLow routine
* ARGUMENTS: none
* RETURNS: void
************************************************** *****************************/
void InterruptHandlerLow()
{
if(PIR1bits.CCP1IF && PIE1bits.CCP1IE) // CCP1 Interrupt?
{
PIR1bits.CCP1IF = 0;
CCP_1_ISR(); // call the CCP1 ISR (in accel.c)
}
else if (PIR2bits.CCP2IF && PIE2bits.CCP2IE) // CCP2 Interrupt?
{
PIR2bits.CCP2IF = 0;
CCP_2_ISR(); // call the CCP2 ISR (in accel.c)
}
else if (INTCONbits.RBIF && INTCONbits.RBIE) // external interrupts 3 through 6?
{
//Port_B = PORTB; // remove the "mismatch condition" by reading port b
INTCONbits.RBIF = 0; // clear the interrupt flag [89]
EncoderTask(PORTB);
}
///////////////////////////////////////////////////////////////////////////
else if (PIR1bits.RCIF && PIE1bits.RCIE) // rx2 interrupt?
{
#ifdef ENABLE_SERIAL_PORT_TWO_RX
Rx_2_Int_Handler(); // call the rx2 interrupt handler (in serial_ports.c)
#endif
}
else if (PIR1bits.TXIF && PIE1bits.TXIE) // tx2 interrupt?
{
#ifdef ENABLE_SERIAL_PORT_TWO_TX
Tx_2_Int_Handler(); // call the tx2 interrupt handler (in serial_ports.c)
#endif
}
///////////////////////////////////////////////////////////////////////
}