157 lines
2.8 KiB
C
157 lines
2.8 KiB
C
|
#include <pic.h>
|
||
|
#include "config.h"
|
||
|
#include "serial.h"
|
||
|
|
||
|
volatile uint8_t stq[STQ_SIZE], stq_start, stq_len;
|
||
|
volatile uint8_t bank1 srq[SRQ_SIZE], srq_start, srq_len;
|
||
|
|
||
|
/*
|
||
|
* Initialization
|
||
|
*/
|
||
|
|
||
|
void serial_init(void)
|
||
|
{
|
||
|
/* Set up serial transmit queue and serial receive queue */
|
||
|
stq_start = stq_len = 0;
|
||
|
srq_start = srq_len = 0;
|
||
|
|
||
|
/* Initialize serial port with baudrate specified in config.h */
|
||
|
TRISRX = 1;
|
||
|
TRISTX = 1;
|
||
|
|
||
|
BRGH = 1;
|
||
|
#define BRG (((FOSC + (8 * BAUDRATE - 1)) /(16 * BAUDRATE)) - 1)
|
||
|
#if (BRG < 1) || (BRG > 255)
|
||
|
#error Cannot achieve baudrate
|
||
|
#endif
|
||
|
#define ACT_BR (FOSC / (16 * (BRG + 1)))
|
||
|
#if ((ACT_BR * 100 / BAUDRATE) > 105) || ((ACT_BR * 100 / BAUDRATE) < 94)
|
||
|
#error Actual baudrate is too far from requested baudrate. Ratio is
|
||
|
#error FOSC/(16*floor((FOSC+(8*BAUDRATE-1))/(16*BAUDRATE)))/BAUDRATE
|
||
|
#endif
|
||
|
|
||
|
SPBRG = BRG;
|
||
|
SYNC = 0;
|
||
|
SPEN = 1;
|
||
|
CREN = 1;
|
||
|
SREN = 0;
|
||
|
TX9 = 0;
|
||
|
RX9 = 0;
|
||
|
TXEN = 1;
|
||
|
|
||
|
/* Enable serial port interrupts. To avoid being interrupted
|
||
|
all the time, TXIE is kept off until we have data to
|
||
|
transmit. */
|
||
|
TXIE = 0;
|
||
|
RCIE = 1;
|
||
|
PEIE = 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Interrupt handling
|
||
|
*/
|
||
|
|
||
|
/* Get a byte from the transmit queue and send it */
|
||
|
void serial_interrupt_tx(void)
|
||
|
{
|
||
|
if(stq_len != 0) {
|
||
|
TXREG = stq[stq_start];
|
||
|
stq_start = (stq_start + 1) & STQ_MASK;
|
||
|
stq_len--;
|
||
|
}
|
||
|
|
||
|
/* If queue is empty, disable interrupt */
|
||
|
if(stq_len == 0)
|
||
|
TXIE = 0;
|
||
|
}
|
||
|
|
||
|
/* Receive a byte and write it to the receive queue. */
|
||
|
void serial_interrupt_rx(void)
|
||
|
{
|
||
|
if(FERR) {
|
||
|
/* Framing error: discard byte */
|
||
|
(void) RCREG;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(OERR) {
|
||
|
/* Overflow error, clear it */
|
||
|
CREN = 0;
|
||
|
CREN = 1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!(srq_len & SRQ_SIZE)) {
|
||
|
srq[(srq_start + srq_len) & SRQ_MASK] = RCREG;
|
||
|
srq_len++;
|
||
|
} else {
|
||
|
/* Queue is full; discard data */
|
||
|
(void) RCREG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Userspace interface
|
||
|
*/
|
||
|
|
||
|
/* Send byte to PC. Blocks iff transmit queue is full. */
|
||
|
void serial_put(uint8_t x)
|
||
|
{
|
||
|
while(!serial_can_put())
|
||
|
continue;
|
||
|
|
||
|
GIE = 0;
|
||
|
stq[(stq_start + stq_len) & STQ_MASK] = x;
|
||
|
stq_len++;
|
||
|
TXIE = 1;
|
||
|
GIE = 1;
|
||
|
}
|
||
|
|
||
|
/* Get byte from PC. Blocks iff receive queue is empty. */
|
||
|
uint8_t serial_get(void)
|
||
|
{
|
||
|
uint8_t x;
|
||
|
|
||
|
while(!serial_can_get())
|
||
|
continue;
|
||
|
|
||
|
x = srq[srq_start];
|
||
|
GIE = 0;
|
||
|
srq_start = (srq_start + 1) & SRQ_MASK;
|
||
|
srq_len--;
|
||
|
GIE = 1;
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* I/O helpers
|
||
|
*/
|
||
|
|
||
|
void serial_put_string(const uint8_t *s)
|
||
|
{
|
||
|
while(s && *s)
|
||
|
serial_put(*s++);
|
||
|
}
|
||
|
|
||
|
const uint8_t hex[16]={'0','1','2','3','4','5','6','7',
|
||
|
'8','9','a','b','c','d','e','f'};
|
||
|
void serial_put_hex(uint8_t x)
|
||
|
{
|
||
|
serial_put(hex[x >> 4]);
|
||
|
serial_put(hex[x & 15]);
|
||
|
}
|
||
|
|
||
|
void serial_put_hex32(uint32_t x)
|
||
|
{
|
||
|
serial_put_hex((x >> 24) & 0xFF);
|
||
|
serial_put_hex((x >> 16) & 0xFF);
|
||
|
serial_put_hex((x >> 8) & 0xFF);
|
||
|
serial_put_hex((x) & 0xFF);
|
||
|
}
|
||
|
|
||
|
void serial_crlf(void)
|
||
|
{
|
||
|
serial_put('\r');
|
||
|
serial_put('\n');
|
||
|
}
|