Menu Close

UART software engineering main function

1. fii_uart1.c

 

Related reference articles:

RISC-V teaching plan

 

fii_uart1.c is mainly used to implement the declared UART1_IRQ_register function in fii_uart1.h

#include <stdio.h>
#include <stdint.h>
#include "fii_uart1.h" 
#include "platform.h"


/*
The parameter list of the function includes baud rate, parity check, interrupt type, 
number of stop bits, receive watermark threshold, transmit watermark threshold and 
interrupt enable
*/
void UART1_IRQ_register(    u32_t uart1_baud_rate, u32_t uart1_parity, u32_t irq_mask,
                            E_UART1_NSTOP nstop, E_RXCTRL rxwm, E_TXCTRL txwm, E_UART1_IRQ_SW uart1_sw)
{
    //set baud rate
    UART1_REG(UART1_DIV) = uart1_baud_rate;

    //Set the parity bit
    UART1_REG(UART1_LCR) = uart1_parity;

    //set stop bit
    if(nstop == UART1_NSTOP_1)
        UART1_REG(UART1_TX_CTRL) &= ~(1UL << 1);    //nstop bit is 0 ====> 1 bit stop bit
    else
        UART1_REG(UART1_TX_CTRL) |= (1UL << 1);     //nstop bit is 1 ====> 2 bit stop bits


    //Set the watermark threshold
    //Clear the previous watermark threshold before setting
    UART1_REG(UART1_TX_CTRL) &= ~(7UL << 16);       // clear bits 16-18, 7UL --> 3'b111
    UART1_REG(UART1_RX_CTRL) &= ~(7UL << 16);
    UART1_REG(UART1_TX_CTRL) |= (txwm.txctrl_reg);  // send watermark bit
    UART1_REG(UART1_RX_CTRL) |= (rxwm.rxctrl_reg);  //Receive watermark bit

    //interrupt enable
    if(uart1_sw == UART1_IRQ_DIS)        //interrupt disable 
        UART1_REG(UART1_IE) &= ~(1UL << irq_mask);
    else                                 //enable interrupt
        UART1_REG(UART1_IE) |= (1UL << irq_mask);

    return;

}

 

2.main.c

The design of the main function here is: after setting the watermark threshold interrupt, every time the rx interrupt is entered, the received data (the data sent by the serial port debugging tool SSCOM connected to the serial port in the experiment) will be written into the heap, and the tx interrupt will be turned on. After entering the tx interrupt, the data written by rx in the heap will be read out, sent out, and the rx interrupt will be turned on. The heap space is allocated with the malloc() function, and three pointers are used, which are combined together for reading and writing data in the heap.

In addition, based on the PLIC software construction introduced before, and the GPIO and PWM software engineering in the external interrupt source, only the newly added parts in the main function will be described here. The previous PLIC software engineering details are shown in Sections 18-21 in the RISC-V teaching plan (click here ), as shown in Figure 1.

 

Figure 1 PLIC-related articles in the RISC-V teaching plan

#include <stdio.h>
#include <stdlib.h>         //malloc is a subfunction in the standard function library
#include "umm_heap.h"       //For malloc function
#include "fii_types.h"
#include "platform.h"
#include "plic_driver.h"


#define  NOP_DELAY  0x400000
#define  MEM_SIZE   0x120
#define  FIFO_SIZE  0x100

//Declare global variables for configuration of watermark thresholds
E_RXCTRL rxwm;
E_TXCTRL txwm;


//declare the external function
extern void trap_entry();

//Declare 3 global character pointers for heap reading and writing
unsigned char *glb_p;             //rx use
unsigned char *glb_k;             //tx use 
unsigned char *glb_end;           //end of memory 


//rx receives data and writes to memory
void memory_input (void)
{
    unsigned int temp;            // used to read rx data and fifo empty 
    
    if(glb_p == NULL)             // if the memory allocation is 0  
    {
        printf("\r\n No valid memry \r\n");
        return;
    }
    else if(glb_p == glb_end)    // if the rx pointer has reached the end of the memory allocation
        printf("\r\n Momery is full!!!\r\n");
    else
    {
        temp = UART1_REG(UART1_RX_DATA);       //Read rx data and fifo empty
        
        while(temp != UART1_RX_FIFO_EMPTY)     //If fifo is not empty, rx data is valid 
        {
            *glb_p = temp;                     //write to memory
            glb_p++;                           //The pointer moves to the next unwritten memory area
            temp = UART1_REG(UART1_RX_DATA);   //Read out the next rx data and fifo empty for the judgment of the next loop
        }

        //Turn on tx interrupt
        UART1_IRQ_register( BAUD_RATE_115200, ODD_PARITY, UART1_TXWM_MASK,
        UART1_NSTOP_1, rxwm, txwm, UART1_IRQ_EN);
    }

    return;

}


//tx reads the memory data and sends it out
void memory_output (void)
{
    unsigned char temp;

    if(glb_k == NULL)      //if the memory allocation is 0  
    {
        printf("\r\n No valid memry \r\n");
        return;
    }
    else if(glb_k >= glb_p)        //If the rx pointer has reached the location of the tx pointer, that is, the data in the memory has been read   
        printf("\r\n Momery is empty!!!\r\n");
    else
    {
        while(glb_k < glb_p)                    //when there is still data to read   
        {
            temp = *glb_k;                      //Read the data of the current pointer
            UART1_REG(UART1_TX_DATA) = temp ;   //Write to TXDATA register
            glb_k++;                            //move pointer to next unread data memory area
        }

        //Open the interrupt of rx
        UART1_IRQ_register( BAUD_RATE_115200, ODD_PARITY, UART1_RXWM_MASK,
        UART1_NSTOP_1, rxwm, txwm, UART1_IRQ_EN);
    }

    return;

}

//UART1 interrupt handler
void uart1_handler(void) {
    //Determine the reason for the UART1 interrupt: check error/tx watermark/rx watermark
    unsigned int irq_case;

    irq_case = UART1_REG(UART1_IP);
    
    switch(irq_case)   // Determine the reason for the interruption 
    {
    case 0x2:                //rx watermark 
        printf("\r\n UART1 receive watermark interrupt\r\n");
        //Clear UART1 interrupt
        UART1_REG(UART1_IC) |= 1 << UART1_RXWM_MASK;
        
        // disable rxwm interrupt
        UART1_REG(UART1_IE) &= ~(1UL << UART1_RXWM_MASK);

        memory_input();     //Enter the memory write function
        break;
    case 0x1:               //tx watermark 
        printf("\r\n UART1 transmit watermark interrupt\r\n");
        //Clear UART1 interrupt
        UART1_REG(UART1_IC) |= 1 << UART1_TXWM_MASK;

        // disable txwm interrupt
        UART1_REG(UART1_IE) &= ~(1UL << UART1_TXWM_MASK);

        memory_output();       //Enter the read memory function
        break;
    case 0x4:            //check error
        printf("\r\n UART1 transmit parity error\r\n");
        //Clear UART1 interrupt
        UART1_REG(UART1_IC) |= 1 << UART1_PERROR_MASK;

        // disable check error interrupt
        UART1_REG(UART1_IE) &= ~(1UL << UART1_PERROR_MASK);
        break;
    default:         // not the interrupts listed above
        printf("\r\n No UART1 interrupt\r\n");
        break;
    }

    return;

};


//initialization function
void _init(void)
{
    //Enable the txen and rxen bits of TXCTRL and RXCTRL
    UART1_REG(UART1_TX_CTRL) |= UART1_TXEN;
    UART1_REG(UART1_RX_CTRL) |= UART1_RXEN;

    // disable UART1 interrupt
    UART1_REG(UART1_IE) = 0;

    write_csr(mtvec, &trap_entry);//Global interrupt entry 

    return;
}


//interrupt configuration function
void IRQ_register (){
    // Until setup is complete, disable machine and timer interrupts.
    clear_csr(mie, MIP_MEIP);  // disable external interrupt
    clear_csr(mie, MIP_MTIP);  // disable timer interrupt
    
    for (int ii = 0; ii < PLIC_NUM_INTERRUPTS; ii ++){
        g_ext_interrupt_handlers[ii] = no_interrupt_handler;
    }

    //UART1 interrupt handling
    g_ext_interrupt_handlers[INT_UART1_BASE] = uart1_handler;

    // Interrupts must be enabled at both the UART1 level and the PLIC level
    enable_plic_int(INT_UART1_BASE);

    // The interrupt priority must be set above 0 to trigger an interrupt
    PLIC_set_priority(&g_plic, INT_UART1_BASE, 3);

    //--------------------------UART1 level set interrupt------------------------------
    txwm.bit.txwm_bit = 0x3;    // Define tx watermark threshold
    rxwm.bit.rxwm_bit = 0x7;    // define the rx watermark threshold

    //Set the baud rate of TX to 115200, odd check, one stop bit, and the watermark threshold is 3
    UART1_IRQ_register( BAUD_RATE_115200, ODD_PARITY, UART1_TXWM_MASK,
    UART1_NSTOP_1, rxwm, txwm, UART1_IRQ_EN);

    //Set the baud rate of RX to 115200, odd check, one stop bit, and the watermark threshold is 7
    UART1_IRQ_register( BAUD_RATE_115200, ODD_PARITY, UART1_RXWM_MASK,
    UART1_NSTOP_1, rxwm, txwm, UART1_IRQ_EN);

    //----------------------------UART1 level set interrupt end--------------------------
    // Enable external interrupt bit in machine mode in MIE
    set_csr(mie, MIP_MEIP);

    // Enable machine mode global interrupts
    set_csr(mstatus, MSTATUS_MIE);
}


// main function
int main(void)
{
    //initialize memory
    mm_heap_initialize();

    glb_p = malloc (MEM_SIZE);       // allocate memory to the global pointer ptr, glb_p 
    glb_k = glb_p;                   // At the beginning, two pointers point to a memory location
    glb_end = glb_p + MEM_SIZE;      // point the glb_end pointer to the end of memory


    //call the initialization function
    _init();
    printf("\r\nRun Segment Timer IRQ Program \r\n");

    //Call the interrupt configuration function
    IRQ_register();

    while ( 1 )
    {
        asm("nop");
    }

}

 

Engineering code download:

Posted in FPGA, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!