Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions include/uart.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef UART_H_
#define UART_H_

#include "common.h"
#include <stdbool.h>
#include <stdint.h>

/*
* Initialize UART module. Set up rx and tx buffers, set up module,
* and enable the requisite interrupts
*/
w_status_t uart_init(uint32_t baud_rate, uint32_t fosc, bool enable_flow_control);

/*
* A lot like transmitting a single byte, except there are multiple bytes. tx does
* not need to be null terminated, that's why we have the len parameter
*
* tx: pointer to an array of bytes to send
* len: the number of bytes that should be sent from that array
*/
void uart_transmit_buffer(uint8_t *tx, uint8_t len);

/*
* returns true if there's a byte waiting to be read from the UART module
*/
bool uart_byte_available(void);

/*
* pops a byte from the receive buffer and returns it. Don't call this
* function unless uart_byte_available is returning true. Don't call this
* function from an interrupt context.
*/
uint8_t uart_read_byte(void);

/*
* handler for all UART1 module interrputs. That is, PIR3:U1IF, U1EIF, U1TXIF, and U1RXIF
* this function clears the bits in PIR3 that it handles.
*/
void uart_interrupt_handler(void);

#endif /* UART_H */

139 changes: 139 additions & 0 deletions pic18f26k83/uart.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include <pic18f26k83.h>
#include <stdbool.h>
#include <stdint.h>

#include "canlib.h"
#include "canlib/util/safe_ring_buffer.h"
#include "common.h"
#include "uart.h"

// safe ring buffers for sending and receiving
static srb_ctx_t rx_buffer;
static srb_ctx_t tx_buffer;

// memory pools to use for those srbs. 100 is a completely arbitrary number
uint8_t rx_buffer_pool[100], tx_buffer_pool[100];

w_status_t uart_init(uint32_t baud_rate, uint32_t fosc, bool enable_flow_control)

// TRISB3 = 1;
// TRISB2 = 1;
// ANSELB3 = 0;
// ANSELB2 = 0;
// // set RX pin location
// U1RXPPS = (0b001 << 3) | // port B
// (0b011); // pin 30
// // set CTS pin location
// U1CTSPPS = (0b001 << 3) | // port B
// (0b010); // pin 2
//
// TRISB4 = 0;
// TRISB1 = 0;
// // Set B4 to TX
// RB4PPS = 0b010011; // UART1 TX
// // Set B1 to RTS
// RB1PPS = 0b010101; // UART1 RTS

// only 12 or 48 MHz for pic
if (fosc != 12000000UL && fosc != 48000000UL) return W_INVALID_PARAM;

// bool controlled flow control.
U1CON2bits.FLO = enable_flow_control ? 0b10 : 0b00;

// low speed
U1CON0bits.BRGS = 0;
uint32_t divisor = (U1CON0bits.BRGS == 1) ? 4 : 16;

// checks if baud rate param within range
if (baud_rate == 0 || baud_rate > (fosc / divisor)) {
return W_INVALID_PARAM;
}

// don't autodetect baudrate
U1CON0bits.ABDEN = 0;
// normal mode (8 bit, no parity, no 9th bit)
U1CON0bits.MODE = 0;
// enable transmit
U1CON0bits.TXEN = 1;
// enable receive
U1CON0bits.RXEN = 1;

// keep running on overflow, never stop receiving
U1CON2bits.RUNOVF = 1;

// dynamic baud rate
uint16_t brg = (fosc / (divisor * baud_rate)) - 1;
U1BRGH = (brg >> 8) & 0xFF;
U1BRGL = brg & 0xFF;

// we are go for UART
U1CON1bits.ON = 1;

// initialize the rx and tx buffers
srb_init(&rx_buffer, rx_buffer_pool, sizeof(rx_buffer_pool), sizeof(uint8_t));
srb_init(&tx_buffer, tx_buffer_pool, sizeof(tx_buffer_pool), sizeof(uint8_t));

// enable receive interrupt
IPR3bits.U1RXIP = 1;
PIE3bits.U1RXIE = 1;
// Do not enable transmit interrupt, that interrupt enable signals that
// there is data to be sent, which at init time is not true

return W_SUCCESS;
}

void uart_transmit_buffer(uint8_t *tx, uint8_t len) {
// just call uart_transmit_byte with each byte they want us to send
while (len--) {
srb_push(&tx_buffer, tx);
tx++;
}
// If the module isn't enabled, give it a byte to send and enable it
if (PIE3bits.U1TXIE == 0) {
srb_pop(&tx_buffer, &tx);
U1TXB = tx;
U1CON0bits.TXEN = 1;
// also enable the interrupt for when it's ready to send more data
PIE3bits.U1TXIE = 1;
}
}

bool uart_byte_available(void) {
return !srb_is_empty(&rx_buffer);
}

uint8_t uart_read_byte(void) {
uint8_t rcv;
srb_pop(&rx_buffer, &rcv);
return rcv;
}

void uart_interrupt_handler(void) {
if (PIR3bits.U1TXIF) {
// check if there are any bytes we still want to transmit
if (!srb_is_empty(&tx_buffer)) {
// if so, transmit them
uint8_t tx;
srb_pop(&tx_buffer, &tx);
U1TXB = tx;
} else {
// If we have no data to send, disable this interrupt
PIE3bits.U1TXIE = 0;
// if not, disable the TX part of the uart module so that TXIF
// isn't triggered again and so that we reenable the module on
// the next call to uart_transmit_byte
U1CON0bits.TXEN = 0;
}
PIR3bits.U1TXIF = 0;
} else if (PIR3bits.U1RXIF) {
// we received a byte, need to push into RX buffer and return
uint8_t rcv = U1RXB;
srb_push(&rx_buffer, &rcv);
PIR3bits.U1RXIF = 0;
} else if (PIR3bits.U1EIF) {
// Some sort of error, ignore for now (TODO?)
PIR3bits.U1EIF = 0;
} else if (PIR3bits.U1IF) {
PIR3bits.U1IF = 0;
}
}