學習使用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++ );
}
 
 
 
 
 
 
 
 
 
 
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 凱文先生的部落格 的頭像
    凱文先生的部落格

    凱文的八卦俱樂部小天地

    凱文先生的部落格 發表在 痞客邦 留言(4) 人氣()