📄 iserial.c
字号:
//****************************************************************************
// Modified 5 May 1999 by Michael Pearce
// Mods done to use different pins - Rxd on the INT Pin
//
//****************************************************************************
#define InitUart init_uart
/*
* Serial port driver for 16Cxx mid-range PIC devices
* (Interrupt-driven)
*
* Copyright (C) 1997 HI-TECH Software.
* Author: Jeremy Bennett <jeremy@htsoft.com>
*
* Comments:
*
* The major limiting factor in how fast you can go is
* the need to sample the RxData line at several times the
* desired baud rate when looking for a start bit. 8 times is
* used here - less may be possible. With 8 times sampling, the
* maximum baud rate that can be reliably achieved with a 4MHz
* PIC is 1200 baud. This results in about a 100uS interrupt interval,
* with about 30uS used in the interrupt service routine.
*
* Of course once the start bit has been recognised, the interrupt
* interval could be dropped back to the baud rate, but since it's
* pretty axiomatic that a PIC will wait for characters much more
* than it will actually receive them, this wouldn't help much, and
* would make for additional complication.
*
* Another approach would be to use the external interrupt pin for the serial input,
* and start timing from the interrupt. This would have to be carefully done
* to allow simultaneous transmission, though.
*
* This module compiles to about 125 words on a PIC16C84.
*
*/
#include <pic.h>
#include <conio.h>
#ifdef _16C71
bit adcconversionflag; // Used by main so it knows when to do a conversion.
#endif
/*************************************
* Tunable parameters
*/
/* Transmit and Receive port bits */
static volatile bit TxData @ (unsigned)&PORTB*8+1; /* bit3 in port A */
static volatile bit RxData @ (unsigned)&PORTB*8+0; /* bit2 in port A */
static volatile bit TxTris @ (unsigned)&TRISB*8+1; /* bit3 in port A */
static volatile bit RxTris @ (unsigned)&TRISB*8+0; /* bit2 in port A */
#define XTAL 10000000 /* Crystal frequency (Hz). */
#define BRATE 9600 /* Baud rate. */
#define RX_OVERSAMPLE 8 /* Amount of oversampling the receiver does. Must be
a power of two */
#define SAMPLE_ADC 200 /* sample the ADC this many interrupts
/*
* Don't change anything else
************************************/
#define TIMER_VALUE XTAL / (4 * BRATE * RX_OVERSAMPLE)
#define TRANSMIT_NUM_BITS 13 // 1 start bit + 8 data bits + 2 stop bits + safe.
#if (RX_OVERSAMPLE-1)&RX_OVERSAMPLE
#error RX_OVERSAMPLE_value must be a power of 2
#endif
// Receiver states.
enum receiver_state {
RS_HAVE_NOTHING,
RS_WAIT_HALF_A_BIT,
RS_HAVE_STARTBIT,
RS_WAIT_FOR_STOP = RS_HAVE_STARTBIT+8
};
static unsigned char sendbuffer; // Where the character to sent is stored.
static unsigned char receivebuffer; // Where the character is stored as it is received.
static bit receivebufferfull; // 1 = receivebuffer is full.
#ifdef _16C71
static unsigned char adcconvertcount; // How often ADC is read.
#endif
static unsigned char send_bitno;
static unsigned char receivestate; // Initial state of the receiver (0).
static unsigned char skipoversamples; // Used to skip receive samples.
static unsigned char rxshift;
static bit tx_next_bit;
/**
* init_uart
*
* Initialises the serial port:
*
* Sets up the I/O directions for the appropriate PortA pins;
* Sets up Timer0.
*
* */
void
init_uart(void)
{
receivestate = RS_HAVE_NOTHING;
skipoversamples = 1; // check each interrupt for start bit
#ifdef _16C71
adcconvertcount = SAMPLE_ADC;
ADCON1 = 2; // 16C71 requires Port A reconfiguration - make RA2/3 digital I/0,
// leave RA0 and RA1 as analog.
#endif
//### Set Up I/O Initial data and I/O Directions
RxTris=1;
TxData=1;
TxTris=0;
// TRISA = 0x17; // Set up I/O direction.
// TRISB = 0xFE;
/* Set up the timer. */
T0CS = 0; // Set timer mode for Timer0.
TMR0 = (2-TIMER_VALUE); // +2 as timer stops for 2 cycles
// when writing to TMR0
PSA=1; //## Force prescaler to WDT
T0IE = 1; // Enable the Timer0 interrupt.
INTEDG = 0; //## Interrupt on falling edge
INTE = 1; //## Enable external interrupt pin
GIE = 1;
}
void
putch(char c)
{
while(send_bitno)
continue;
tx_next_bit = 0;
sendbuffer = c;
send_bitno = TRANSMIT_NUM_BITS*RX_OVERSAMPLE;
}
char
getch(void)
{
while(!receivebufferfull)
continue;
receivebufferfull = 0;
return receivebuffer;
}
bit
kbhit(void)
{
return receivebufferfull;
}
/**
* serial_isr
*
* Transmits and receives characters which have been
* "putch"ed and "getch"ed.
*
* This ISR runs BRATE * RX_OVERSAMPLE times per second.
*
* */
interrupt void
serial_isr(void)
{
// Reset Timer0 value
// This is added to TMR0 because there is a delay to get to the isr.
PORTB |= 1;
TMR0 += -TIMER_VALUE + 4; // +2 as timer stops for 2 cycles when writing to TMR0 +2 for tweak
T0IF = 0;
#ifdef _16C71
/*** ADC ***/
/* This will be called every SAMPLE_ADCth time. */
if(--adcconvertcount == 0) {
adcconversionflag = 1;
adcconvertcount = SAMPLE_ADC;
}
#endif
/*** RECEIVE ***/
if( --skipoversamples == 0) {
skipoversamples++; // check next time
switch(receivestate) {
case RS_HAVE_NOTHING:
//## Now is activated by Interrupt edge detection
// /* Check for start bit of a received char. */
// if(!RxData){
// skipoversamples = RX_OVERSAMPLE/2;
// receivestate++;
// }
break;
case RS_WAIT_HALF_A_BIT:
if(!RxData)
{ // valid start bit
skipoversamples = RX_OVERSAMPLE;
receivestate++;
}
else
{
receivestate = RS_HAVE_NOTHING;
INTE=1;
}
break;
// case RS_HAVE_STARTBIT: and subsequent values
default:
rxshift = (rxshift >> 1) | (RxData << 7);
skipoversamples = RX_OVERSAMPLE;
receivestate++;
break;
case RS_WAIT_FOR_STOP:
receivebuffer = rxshift;
receivebufferfull = 1;
receivestate = RS_HAVE_NOTHING;
INTE=1;
break;
}
}
/*** TRANSMIT ***/
/* This will be called every RX_OVERSAMPLEth time
* (because the RECEIVE needs to over-sample the incoming
* data). */
if(send_bitno) {
if((send_bitno & (RX_OVERSAMPLE-1)) == 0) {
TxData = tx_next_bit; // Send next bit.
tx_next_bit = sendbuffer & 1;
sendbuffer = (sendbuffer >> 1) | 0x80;
}
send_bitno--;
}
//## Interrupt detection for Start Byte ##
if(INTF)
{
INTF=0;
/* Check for start bit of a received char. */
if(!RxData)
{
skipoversamples = RX_OVERSAMPLE/2;
receivestate++;
INTE=0;
}
}
PORTB &= ~1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -