Interrupt-drive software uart
I wrote a software uart for STM32F10x chips here: http://www.amobbs.com/thread-5542383-1-1.html. On a 24Mhz CM3, it is good up to about 50Kbps, without any optimization.I mentioned that porting it to the code to other mcus should be fairly easy. Here are two examples of porting that piece of code to a PIC (16F684) and a 89C51.
First, porting it to a 16F684. 16F chips don't have vectored interrupts so it is more involved. But here it is:
//==============uartxisr.h========================
#ifndef _UARTXISR_H
#define _UARTXISR_H
//#include "stm32f10x_rcc.h" //we use rcc
//#include "stm32f10x_tim.h" //we use timer
//hardware configuration
//softuart pin definitions
#define UART_PORT PORTC
#define UART_DDR TRISC
#define UART_TX (1<<2) //tx pin
#define UART_SET(tx) IO_SET(UART_PORT, tx)
#define UART_CLR(tx) IO_CLR(UART_PORT, tx)
//pick the timer to be used
//#define UART_TIM TIM3
#define tmrx_init tmr1_init
#define tmrx_act tmr1_act
#include "tmr1.h" //we use timer
//end hardware configuration
//insert into the isr
#define UARTX_TX_ISR() \
if (TMR1IF) { \
TMR1H = tmr1_offset>>8; \
TMR1L = tmr1_offset/* & 0x00ff*/; \
TMR1IF = 0; \
_tmr1_isr_ptr(); \
}
//uartx protocol
//1 start bit
//8 data bits
//1 stop bit
//lsb first
#define UART_SR(dat) (0x0200 | ((dat) << 1) | 0x0000) //form the uart shift register. 1 start bit (low), 8 data bits, 1 stop bit (high), lsb first
//global defines
//define baud rates
#define UART_BR_300 300ul //baudrate=300 - it overflows under 24Mhz F_CPU
#define UART_BR_600 600ul //baudrate=600
#define UART_BR_1200 1200ul //baudrate=1200
#define UART_BR_2400 2400ul //baudrate=2400
#define UART_BR_4800 4800ul //baudrate=4800
#define UART_BR_9600 9600ul //baudrate=9600
#define UART_BR_19200 19200ul //baudrate=19200
#define UART_BR_38400 38400ul //baudrate=38400
#define UART_BR_57600 57600ul //baudrate=57600 - upper limit for 24Mhz F_CPU
#define UART_BR_115200 115200ul //baudrate=115200
//global variables
//softuart_isr handler
//install this in the timer
void uartx_isr(void);
//reset softuart_isr
void uartx_init(unsigned long baud);
//transmit a string
void uartx_puts(unsigned char * str);
//if uart is busy, return 1
unsigned char uartx_busy(void);
#endif
We are using timer1 to generate the baud rate and PORTC.2 as the tx pin.
//================uartxisr.c==========================
#include <htc.h> //we use picc
//#include <stm32f10x.h>
//#include "stm32f10x_gpio.h"
//#include "stm32f10x_tim.h" //we use timer
#include "gpio.h" //we use own macros
#include "tmr1.h" //we use tmr1 as baud rate generator
#include "uartxisr.h" //we use software uart
//hardware configuration
//end hardware configuration
//global defines
//global variables
static unsigned char *_UxTX_ptr;
static unsigned char _UxTX_BUSY=0; //0=u1 transmission done, 1=u1 transmission in process
static unsigned short _UxTX_MASK=0; //current bit being transmitted. 0=end of transmission for the current char
static unsigned short _UxTX_buffer; //software shift register for the transmiter
unsigned short _Ux_OFFSET; //timer offset for uart baud rate
//softuart_isr handler
//install this in the timer
void uartx_isr(void) {
//IO_FLP(UART_PORT, UART_TX); //flip the pin - for debugging
if (_UxTX_MASK!=0x0400) { //current char isn't fully transmitted
if (_UxTX_MASK & _UxTX_buffer) UART_SET(UART_TX); //send '1'
else UART_CLR(UART_TX); //send '0'
_UxTX_MASK = _UxTX_MASK << 1; //shift to the next bit
} else { //current char has been fully transmitted
if (*_UxTX_ptr) { //current char is not a null char
_UxTX_ptr+=1; //increment to the next character
_UxTX_MASK = 0x0001; //1 start bit, 8 data bits, 1 stop bits = 10 bits
_UxTX_buffer = UART_SR(*_UxTX_ptr); //form the buffer to be transmitted
} else { //current char is a null char -> end of transmission
_UxTX_BUSY = 0; //uartx no longer busy
/* TIM IT enable */
//TIM_ITConfig(UART_TIM, TIM_IT_Update, DISABLE); //don't start the isr yet
TMR1IE = 0; //disable tmr1 interrupt
}
}
}
//reset softuart_isr
void uartx_init(unsigned long baud) {
//set up the pin
IO_SET(UART_PORT, UART_TX); //tx idles high
IO_OUT(UART_DDR, UART_TX); //tx as output
//set up the baud rate generator
tmrx_init(0, F_CPU / baud); //set up timer
tmrx_act(uartx_isr); //install the handler
//clear the bits
//TIM_ClearITPendingBit(UART_TIM, TIM_IT_Update);
TMR1IF = 0;
/* TIM IT enable */
//TIM_ITConfig(UART_TIM, TIM_IT_Update, DISABLE); //don't start the isr yet
TMR1IE = 1;
_UxTX_BUSY = 0; //uartx not busy
}
//transmit a string
void uartx_puts(unsigned char * str) {
_UxTX_BUSY = 1; //uartx is busy
_UxTX_ptr = str;
_UxTX_MASK = 0x0001; //1 start bit, 8 data bits, 1 stop bits = 10 bits
_UxTX_buffer = UART_SR(*_UxTX_ptr); //form the buffer to be transmitted
//clear the bits
//TIM_ClearITPendingBit(UART_TIM, TIM_IT_Update);
TMR1IF = 1; //force loading of offsets in the isr
/* TIM IT enable */
//TIM_ITConfig(UART_TIM, TIM_IT_Update, ENABLE); //enable the transmission
TMR1IE = 1;
}
//if uart is busy, return 1
unsigned char uartx_busy(void) {
return _UxTX_BUSY;
}
Three routines are involved: uartx_init() initialize the module and sets the baud rate; uartx_puts() sends a string, and uartx_busy() tests if the uartx module is busy.
You can see great similarity between this code base and the one for STM32F.
here is the application code:
//==============main.c===================
#include <htc.h> //we use picc
#include "config.h" //configuration words
#include "gpio.h"
#include "delay.h" //we use software delays
#include "tmr1.h" //we use timer1 for uartxisr
#include "uartxisr.h" //we use software uart isr
//hardware configuration
#define LED_PORT PORTC
#define LED_DDR TRISC
#define LED (1<<0)
#define LED_DLY 100 //delay, in ms
//end hardware configuration
//global defines
//global variables
unsigned char uRAM[]="16F684 UARTXISR...\n\r"; //test string to be sent
//isr
void interrupt isr(void) {
UARTX_TX_ISR(); //uartx transmitter isr
}
//reset the mcu
void mcu_init(void) {
ANSEL = 0x00; //all pins digital
CMCON0= 0x07; //comparators off
IO_CLR(LED_PORT, LED); //clear led
IO_OUT(LED_DDR, LED); //led as output
}
int main(void) {
mcu_init(); //initialize the mcu
uartx_init(UART_BR_1200); //set the baud rate
ei(); //enable interrupt
while (1) {
if (!uartx_busy()) { //uart is not busy
uartx_puts(uRAM); //send the string
}
IO_FLP(LED_PORT, LED); //flip led
//delay_ms(LED_DLY); //waste some time
}
}
It is fairly simple: sets up the uart to send a string at 1200bps, and then flip a pin (PORTC.0).
Here is the output:
good, i raise you
本帖最后由 millwood0 于 2013-8-18 08:53 编辑
Now, on to 89C51. It is actually a lot easier as the chip has a vectored interrupt controller.
#ifndef _UARTXISR_H
#define _UARTXISR_H
//#include "stm32f10x_rcc.h" //we use rcc
//#include "stm32f10x_tim.h" //we use timer
//hardware configuration
//softuart pin definitions
#define UART_PORT P2
#define UART_DDR P2
#define UART_TX (1<<2) //tx pin
#define UART_SET(tx) IO_SET(UART_PORT, tx)
#define UART_CLR(tx) IO_CLR(UART_PORT, tx)
//pick the timer to be used
//#define UART_TIM TIM3
#define tmrx_init tmr1_init
#define tmrx_act tmr1_act
#include "tmr1.h" //we use timer
//end hardware configuration
//insert into the isr
#define UARTX_TX_ISR() \
//if (TMR1IF) { \
TMR1H = tmr1_offset>>8; \
TMR1L = tmr1_offset/* & 0x00ff*/; \
// TMR1IF = 0; \
// _tmr1_isr_ptr(); \
//}
//uartx protocol
//1 start bit
//8 data bits
//1 stop bit
//lsb first
#define UART_SR(dat) (0x0200 | ((dat) << 1) | 0x0000) //form the uart shift register. 1 start bit (low), 8 data bits, 1 stop bit (high), lsb first
//global defines
//define baud rates
#define UART_BR_300 300ul //baudrate=300 - it overflows under 24Mhz F_CPU
#define UART_BR_600 600ul //baudrate=600
#define UART_BR_1200 1200ul //baudrate=1200
#define UART_BR_2400 2400ul //baudrate=2400
#define UART_BR_4800 4800ul //baudrate=4800
#define UART_BR_9600 9600ul //baudrate=9600
#define UART_BR_19200 19200ul //baudrate=19200
#define UART_BR_38400 38400ul //baudrate=38400
#define UART_BR_57600 57600ul //baudrate=57600 - upper limit for 24Mhz F_CPU
#define UART_BR_115200 115200ul //baudrate=115200
//global variables
//softuart_isr handler
//install this in the timer
void uartx_isr(void);
//reset softuart_isr
void uartx_init(unsigned long baud);
//transmit a string
void uartx_puts(unsigned char * str);
//if uart is busy, return 1
unsigned char uartx_busy(void);
#endif
the corresponding .c file
//===============uartxisr.c===================
//#include <htc.h> //we use picc
#include <regx51.h> //we use keil c51
//#include <stm32f10x.h>
//#include "stm32f10x_gpio.h"
//#include "stm32f10x_tim.h" //we use timer
#include "gpio.h" //we use own macros
#include "tmr1.h" //we use tmr1 as baud rate generator
#include "uartxisr.h" //we use software uart
//hardware configuration
//end hardware configuration
//global defines
//global variables
static unsigned char *_UxTX_ptr;
static unsigned char _UxTX_BUSY=0; //0=u1 transmission done, 1=u1 transmission in process
static unsigned short _UxTX_MASK=0; //current bit being transmitted. 0=end of transmission for the current char
static unsigned short _UxTX_buffer; //software shift register for the transmiter
unsigned short _Ux_OFFSET; //timer offset for uart baud rate
//softuart_isr handler
//install this in the timer
void uartx_isr(void) {
//IO_FLP(UART_PORT, UART_TX); //flip the pin - for debugging
//load the offset
UARTX_TX_ISR(); //realod the offset
if (_UxTX_MASK!=0x0400) { //current char isn't fully transmitted
if (_UxTX_MASK & _UxTX_buffer) UART_SET(UART_TX); //send '1'
else UART_CLR(UART_TX); //send '0'
_UxTX_MASK = _UxTX_MASK << 1; //shift to the next bit
} else { //current char has been fully transmitted
if (*_UxTX_ptr) { //current char is not a null char
_UxTX_ptr+=1; //increment to the next character
_UxTX_MASK = 0x0001; //1 start bit, 8 data bits, 1 stop bits = 10 bits
_UxTX_buffer = UART_SR(*_UxTX_ptr); //form the buffer to be transmitted
} else { //current char is a null char -> end of transmission
_UxTX_BUSY = 0; //uartx no longer busy
/* TIM IT enable */
//TIM_ITConfig(UART_TIM, TIM_IT_Update, DISABLE); //don't start the isr yet
//TMR1IE = 0; //disable tmr1 interrupt
ET1 = 0; //turn off tmr1 interrupt
}
}
}
//reset softuart_isr
void uartx_init(unsigned long baud) {
//set up the pin
IO_SET(UART_PORT, UART_TX); //tx idles high
IO_OUT(UART_DDR, UART_TX); //tx as output
//set up the baud rate generator
tmrx_init(0, F_CPU / baud); //set up timer
tmrx_act(uartx_isr); //install the handler
//clear the bits
//TIM_ClearITPendingBit(UART_TIM, TIM_IT_Update);
//TMR1IF = 0;
TF1 = 0;
/* TIM IT enable */
//TIM_ITConfig(UART_TIM, TIM_IT_Update, DISABLE); //don't start the isr yet
//TMR1IE = 1;
ET1 = 1;
_UxTX_BUSY = 0; //uartx not busy
}
//transmit a string
void uartx_puts(unsigned char * str) {
_UxTX_BUSY = 1; //uartx is busy
_UxTX_ptr = str;
_UxTX_MASK = 0x0001; //1 start bit, 8 data bits, 1 stop bits = 10 bits
_UxTX_buffer = UART_SR(*_UxTX_ptr); //form the buffer to be transmitted
//clear the bits
//TIM_ClearITPendingBit(UART_TIM, TIM_IT_Update);
//TMR1IF = 1; //force loading of offsets in the isr
TF1 = 1;
/* TIM IT enable */
//TIM_ITConfig(UART_TIM, TIM_IT_Update, ENABLE); //enable the transmission
//TMR1IE = 1;
ET1 = 1;
}
//if uart is busy, return 1
unsigned char uartx_busy(void) {
return _UxTX_BUSY;
}
The calling convention is the same:
//==============main.c=================
#include <regx51.h> //we use keil c51
#include "gpio.h"
#include "delay.h" //we use software delay
#include "tmr1.h" //we use timer
#include "uartxisr.h" //we use software uartx
//hardware configuration
#define LED_PORT P2
#define LED_DDR P2
#define LED (1<<0)
#define LED_DLY 100
//end hardware configuration
//global defines
//global variables
unsigned char uRAM[]="89C51 UARTX_ISR...\n\r";
void mcu_init(void) {
IO_CLR(LED_PORT, LED); //clear led
IO_OUT(LED_DDR, LED); //led as output
}
int main(void) {
mcu_init(); //reset the mcu
uartx_init(UART_BR_1200); //set the baud rate
ei(); //enable interrupt
while (1) {
if (!uartx_busy()) { //uartx is not busy
uartx_puts(uRAM); //send the string
}
IO_FLP(LED_PORT, LED); //flip led
//delay_ms(LED_DLY); //waste some time
}
}
The output:
页:
[1]