close
學習使用MCC去產生UART程式,在慢慢研究與學習PIC是如何設置UART暫存器
根據下述的設定

MCC產生了eusart1.h及eusart1.c
內有根據上圖設定,EUSART設成Asynchronous,9600N81
Enable Transmit及Enable Continuous Receive需要勾選,MCC才會勾選RC6與RC7 pin腳

這邊可能有人會覺得很奇怪(其實是我啦)
就是GPIO的TX怎麼會設成Input

其實若翻略Datasheet是USART的Tx GPIO是這樣設定的

void EUSART1_Initialize(void)
{
// Set the EUSART1 module to the options selected in the user interface.
// ABDOVF no_overflow; CKTXP async_noninverted_sync_fallingedge; BRG16 16bit_generator; WUE disabled; ABDEN disabled; DTRXP not_inverted;
BAUDCON1 = 0x08;
// SPEN enabled; RX9 8-bit; CREN enabled; ADDEN disabled; SREN disabled;
RCSTA1 = 0x90;
// TX9 8-bit; TX9D 0; SENDB sync_break_complete; TXEN enabled; SYNC asynchronous; BRGH hi_speed; CSRC slave_mode;
TXSTA1 = 0x24;
// Baud Rate = 9600;
SPBRG1 = 0x19;
// Baud Rate = 9600;
SPBRGH1 = 0x00;
}
除了產生初始化函式外,
還有讀與寫的函式
uint8_t EUSART1_Read(void)
{
while(!PIR1bits.RC1IF)
{
}
if(1 == RCSTA1bits.OERR)
{
// EUSART1 error - restart
RCSTA1bits.CREN = 0;
RCSTA1bits.CREN = 1;
}
return RCREG1;
}
void EUSART1_Write(uint8_t txData)
{
while(0 == PIR1bits.TX1IF)
{
}
TXREG1 = txData; // Write the data byte to the USART.
}
這邊也稍微瞭解一下鮑率的設定
由上述MCC產生的Code可以知道鮑率是由SPBRG1及SPBRGH1兩Register設定

EUSART Baud Rate Generator(BRG)
The Baud Rate Generate(BRG)是一個8-bit或16-bit的timer,專門支援asynchronous及synchronous EUSART操作。預設BRG是操作在8-bit mode,藉由設定BRG16 bit在BAUDCONx就可以把BRG換成16-bit mode
The SPBRGHx:SPBRGx register pair用來判斷Baud rate timer的週期。在asynchronous mode下,Baud Rate Period或其它的倍數被TXSTAx的BRGH bit和BAUDCONx的BRG16 bit來判斷。而在Synchronous mode,BRGH bit被忽略
以下是計算鮑率的公式

上述把SPBRGHx=25,SPBRGx=0就可以使BRG產生鮑率9600
要注意的是上圖公式會因為下圖的bit而做一點變化,所以就是看應用是什麼去挑選,只是要注意
Baud Rate的Error %就這樣吧

因為常用的Baud Rare就那幾個,所以Datasheet已經把它Table化了





接下來解析MCC產生的函式
void EUSART1_Write(uint8_t txData)
{
while(0 == PIR1bits.TX1IF)
{
}
TXREG1 = txData; // Write the data byte to the USART.
}
TX1IF表示
Tx Buffer(TXREGx)內若是空的=1
Tx Buffer(TXREGx) 內若是還有資料未傳送=0
所以while迴圈就是用來等待若是TXREGx內是空的,才會填新值去傳送。
接收部分,同樣地
uint8_t EUSART1_Read(void)
{
while(!PIR1bits.RC1IF)
{
}
if(1 == RCSTA1bits.OERR)
{
// EUSART1 error - restart
RCSTA1bits.CREN = 0;
RCSTA1bits.CREN = 1;
}
return RCREG1;
}
RC1IF用來表示
Receive FIFO內若是有新的資料且還未讀取=1
Receive FIFO內若是沒有新的資料=0
而OERR則是Receive Overrun Error
The Receive FIFO能夠hold兩個字元
會發生OERR是第三個字元在FIFO尚未處理前而被接收所發生的error,
這將會設置OERR bit在RCSTAx register內。除非error被清掉不然將不能
再去接收Rx
清掉OERR的方式是設定CREN bit在RCSTAx register
不過上述函式都是用polling的方式來使用,現在使用MCC使用Interrupt

勾選Enable ESUART Interrupt
Software Transmit/Receive Buffer Size則可以調整array size
MCC產生的Code,在比對先前的
先前的Software Transmit/Receive Buffer Size的調整
是產生一組Tx與Rx的Ring Buffer
#define EUSART1_TX_BUFFER_SIZE 8
#define EUSART1_RX_BUFFER_SIZE 8
static uint8_t eusart1TxHead = 0;
static uint8_t eusart1TxTail = 0;
static uint8_t eusart1TxBuffer[EUSART1_TX_BUFFER_SIZE];
volatile uint8_t eusart1TxBufferRemaining;
static uint8_t eusart1RxHead = 0;
static uint8_t eusart1RxTail = 0;
static uint8_t eusart1RxBuffer[EUSART1_RX_BUFFER_SIZE];
volatile uint8_t eusart1RxCount;
在EUSART1_Initialize(void)內增加一些使用中斷的初始化程式
// disable interrupts before changing states
PIE1bits.RC1IE = 0;
PIE1bits.TX1IE = 0;
...
...
...
// initializing the driver state
eusart1TxHead = 0;
eusart1TxTail = 0;
eusart1TxBufferRemaining = sizeof(eusart1TxBuffer);
eusart1RxHead = 0;
eusart1RxTail = 0;
eusart1RxCount = 0;
// enable receive interrupt
PIE1bits.RC1IE = 1;
先看接收部分的中斷函式
當有資料要接收時,透過遞增eusart1RxHead來先把接收資料填進eusart1RxBuffer內
因為是Ring buffer的架構,所以也要判斷eusart1RxHead的值是否有超過buffer的最大值
有的話,就要回復為0,eusart1RxHead=0
只是要注意的是,當有資料接收時,也遞增了eusart1RxCount
void EUSART1_Receive_ISR(void)
{
if(1 == RCSTA1bits.OERR)
{
// EUSART1 error - restart
RCSTA1bits.CREN = 0;
RCSTA1bits.CREN = 1;
}
// buffer overruns are ignored
eusart1RxBuffer[eusart1RxHead++] = RCREG1;
if(sizeof(eusart1RxBuffer) <= eusart1RxHead)
{
eusart1RxHead = 0;
}
eusart1RxCount++;
}
剛剛上述是透過中斷來接收資料,但是User還沒有真正取出資料來用
真正取出接收資料是透過下列函式與方式
uint8_t EUSART1_Read(void)
{
uint8_t readValue = 0;
while(0 == eusart1RxCount)
{
}
PIE1bits.RC1IE = 0;
readValue = eusart1RxBuffer[eusart1RxTail++];
if(sizeof(eusart1RxBuffer) <= eusart1RxTail)
{
eusart1RxTail = 0;
}
eusart1RxCount--;
PIE1bits.RC1IE = 1;
return readValue;
}
取出接收資料時會先把接收中斷給關閉,
取出資料的方式是透過eusart1RxTail遞增來調整eusart1RxBuffer的資料
一樣也是Ring Buffer的特性,當eusart1RxTail超過eusart1RxBuffer最大值時
eusart1RxTail=0
當資料從buffer取出後,就回復接收中斷並遞減eusart1RxCount
注意,當接收中斷尚未收到資料時,在程式中呼叫EUSART1_Read()
有可能會造成程式停在while迴圈內
while(0 == eusart1RxCount)
{
}
接下來就是瞭解MCC產生傳送的程式
一般使用者要傳送資料,透過函式void EUSART1_Write(uint8_t txData)
參數txData就是要填入傳送的字元
函式內的if...else...判斷式的意思是
當傳送中斷沒有被設時,先把txData填到TXREG1 Register內,然後再啟動傳送中斷去傳送
如果傳送中斷已經被設定了(通常是傳送的是連續的字元),表示先前欲傳送的字元還沒處理完,
先關閉傳送中斷,在把其他字元先存到eusart1TxBuffer內,在打開中斷傳送,藉由中斷去傳送
eusart1TxBuffer內的資料,和傳送也一樣,要檢查eusart1TxHead是否有超buffer的最大容量,
以及遞減eusart1TxBufferRemaining
void EUSART1_Write(uint8_t txData)
{
while(0 == eusart1TxBufferRemaining)
{
}
if(0 == PIE1bits.TX1IE)
{
TXREG1 = txData;
}
else
{
PIE1bits.TX1IE = 0;
eusart1TxBuffer[eusart1TxHead++] = txData;
if(sizeof(eusart1TxBuffer) <= eusart1TxHead)
{
eusart1TxHead = 0;
}
eusart1TxBufferRemaining--;
}
PIE1bits.TX1IE = 1;
}
剛剛使用者呼叫EUSART1_Write(),資料還沒有傳送出去,只是填到buffer內,
真正把資料傳出去是透過EUSART1_Transmit_ISR(void)
if(sizeof(eusart1TxBuffer) > eusart1TxBufferRemaining)
如果條件不成立,表示目前沒有資料需要傳送,就關閉傳送中斷
如果條件成立,就把buffer內的資料填到eusart1TxBuffer內
並遞增eusart1TxBufferRemaining
void EUSART1_Transmit_ISR(void)
{
// add your EUSART1 interrupt custom code
if(sizeof(eusart1TxBuffer) > eusart1TxBufferRemaining)
{
TXREG1 = eusart1TxBuffer[eusart1TxTail++];
if(sizeof(eusart1TxBuffer) <= eusart1TxTail)
{
eusart1TxTail = 0;
}
eusart1TxBufferRemaining++;
}
else
{
PIE1bits.TX1IE = 0;
}
}
這裡兩個函式void ConvertADCVoltage ( unsigned int adc_conv_data ) 和void putrs1USART(const char *data) ,可以幫助ADC轉成實際電壓後,再透過RS232傳送出去
#define VDD (50000) // Supply Voltage of 5.0V is represented as 50000*10^-4 V
// to get the required ADC_RESOLUTION
#define ADC_STEPS (1024) // 10 bit ADC has 1024 Levels
#define ADC_RESOLUTION (VDD/ADC_STEPS) // Calculate the smallest resolution of the ADC
/*
Function:
void ConvertADCVoltage ( unsigned int adc_conv_data )
Description:
Converts digital voltage to analog voltage
*/
void ConvertADCVoltage ( unsigned int adc_conv_data )
{
adcVoltValue = (ADC_RESOLUTION * adc_conv_data); // ADC 輸入值 乘以 4882.8 mV (為一個 ADC 一個解析單位的電壓值)
// 16 進制轉換成 10 進制
A1000_mV = adcVoltValue / 10000; // 取出 V
V_temp = adcVoltValue % 10000;
A100_mV = V_temp / 1000; // 取出 0.1V 的電壓值
V_temp = V_temp % 1000;
A10_mV = V_temp / 100; // 取出 0.01V
V_temp = V_temp % 100;
mV = V_temp / 10; // 取出 0.001V
}
void putrs1USART(const char *data)
{
do
{ // Transmit a byte
EUSART1_Write(*data); // 呼叫 enusart1.c 的函數 (from MCC)
} while( *data++ );
}
全站熱搜