1. fii_i2c0.c
Related reference articles:
RISC-V teaching plan
fii_i2c0.c mainly implements some functions declared in fii_i2c0.h.
#include <stdio.h> #include <stdint.h> #include "fii_i2c0.h" #include "platform.h" /*I2C0_IRQ_register function parameter list includes frequency division, read watermark threshold, write watermark threshold, interrupt type, interrupt enable, restart enable */ void I2C0_IRQ_register ( u32_t i2c0_div, u32_t rwm, u32_t wwm, u32_t irq_mask, E_I2C0_IRQ_SW i2c0_sw, E_I2C0_RESTART restart) { //Set the data transfer rate, such as 100k bits/s I2C0_REG(I2C0_DIV) = (i2c0_div & 0XFFFF); //Only the lower 16 bits of the DIV register are valid //Set the corresponding read/write watermark threshold, before setting, clear the previous watermark threshold I2C0_REG(I2C0_CTRL) &= ~(I2C0_3_MASK << I2C0_CTRL_WWM); I2C0_REG(I2C0_CTRL) &= ~(I2C0_3_MASK << I2C0_CTRL_RWM); //Write watermark threshold I2C0_REG(I2C0_CTRL) |= ((wwm & I2C0_3_MASK) << I2C0_CTRL_WWM); // read watermark threshold I2C0_REG(I2C0_CTRL) |= ((rwm & I2C0_3_MASK) << I2C0_CTRL_RWM); //interrupt enable if (i2c0_sw == I2C0_IRQ_DIS )//Disable interrupt I2C0_REG(I2C0_IE) &= ~(1UL << irq_mask); else //enable interrupt I2C0_REG(I2C0_IE) |= (1UL << irq_mask); // restart enable if (restart == I2C0_N_RESTART )//Disable restart I2C0_REG(I2C0_CTRL) &= ~(1UL << I2C0_CTRL_RESTART); else // enable restart I2C0_REG(I2C0_CTRL) |= (1UL << I2C0_CTRL_RESTART); return ; } //The parameter list of the I2C0_RD_DATA function includes register address, slave device address, read data pointer, data length void I2C0_RD_DATA (u32_t reg_addr, u32_t slave_addr, u8_t* data, u32_t i2c0_data_len) { //There is no limit to reading, you can read up to 15 bytes/the maximum value of the count register u32_t len_tmp; u32_t data_offset = 0; //Clear the lower 16 bits of the slave address register before writing I2C0_REG(I2C0_SADDR) &= ~(0xFFFF); //Set the lowest bit of the slave address register to 'read' I2C0_REG(I2C0_SADDR) |= 1UL; //Write to slave address I2C0_REG(I2C0_SADDR) |= ((slave_addr & 0X7F) << I2C0_DADDR); //0X7F = 111_1111 //Main loop, as long as data_len > 0, the data needs to be read out while (i2c0_data_len > 0) { //Read up to 15 bytes of data at a time, because the maximum value of the count register is 15 len_tmp = (i2c0_data_len > 0xf) ? 0xf : i2c0_data_len; //Write the current data length to the count register, and subtract the current data length read this time from data_len I2C0_REG(I2C0_CNT) = len_tmp; i2c0_data_len -= len_tmp; // Before writing to the register address, clear the previous value I2C0_REG(I2C0_SADDR) &= ~(0xFF << I2C0_SREG); I2C0_REG(I2C0_SADDR) |= ((reg_addr & 0xFF) << I2C0_SREG); // Advance the register address to avoid repeated writing on the same register address in the EEPROM reg_addr += len_tmp; data_offset = len_tmp; //After setting the registers needed to read the data, start I2C0_START(); //read data while (data_offset > 0) { //If the read FIFO is empty, do not continue reading while ((I2C0_REG(I2C0_STATUS) & I2C0_ST_FIFO_EMPTY_MASK)) continue ; //Read the data and write the data to the address pointed to by the data pointer *data = I2C0_REG(I2C0_RDATA); printf ("%02x ",*data); // pointer advance data++; //After reading a byte of data, data_offset is decremented by 1, until at most 15 bytes of data are read at a time data_offset--; } } printf ("\r\n\r\n"); return ; } // start function void I2C0_START () { //Before the initialization register is completed, the start bit needs to be disabled // After all settings are done, enable the start bit I2C0_REG(I2C0_CTRL) |= (1UL << I2C0_CTRL_START); return ; } //Write all 0xff in EEPROM, I2C0_WR_FF function parameter list only has slave address void I2C0_WR_FF ( u32_t slave_addr) { u32_t len_tmp; u32_t data_offset = 0; //Clear the lower 16 bits of the slave address register before writing I2C0_REG(I2C0_SADDR) &= ~(0xFFFF); I2C0_REG(I2C0_SADDR) &= ~ 1UL; //Set the lowest bit of the slave address register to 'write' I2C0_REG(I2C0_SADDR) |= ((slave_addr & 0X7F) << I2C0_DADDR); 、 //Set data_len to the size of the EEPROM u32_t i2c0_data_len = 256; u32_t reg_addr = 0; while (i2c0_data_len > 0) { len_tmp = (i2c0_data_len > 0x8) ? 0x8 : i2c0_data_len; //AT24C02 can only write up to 8 bytes of data at a time //Write the current data_len to the count register, and subtract the current data length read from data_len I2C0_REG(I2C0_CNT) = len_tmp; i2c0_data_len -= len_tmp; // Before writing to the register address, clear the previous value I2C0_REG(I2C0_SADDR) &= ~(0xFF << I2C0_SREG); I2C0_REG(I2C0_SADDR) |= ((reg_addr & 0xFF) << I2C0_SREG); // Advance the register address to avoid repeated writing on the same register address in the EEPROM reg_addr += len_tmp; data_offset = len_tmp; printf ("%02x\r\n",reg_addr); //write data while (data_offset > 0) { //When the write FIFO is full, do not continue to write while ((I2C0_REG(I2C0_STATUS) & I2C0_ST_FIFO_FULL_MASK)) continue ; //write 0xff I2C0_REG(I2C0_WDATA) = 0xff; //After writing a byte of data, the data_offset is reduced by 1 until at most 8 bytes of data are written at one time data_offset--; } // start function I2C0_START(); // Determine whether the write FIFO is empty (actually detect the word count of the FIFO), and the next operation can only be performed after confirmation while ((I2C0_REG(I2C0_STATUS) >> I2C0_ST_FIFO_WCNT) & 0x0f) continue ; } return ; } //The parameter list of the I2C0_WR_DATA function includes register address, slave device address, write data pointer, data length void I2C0_WR_DATA (u32_t reg_addr, u32_t slave_addr, u8_t* data, u32_t i2c0_data_len) { u32_t len_tmp; u32_t data_offset = 0; u32_t reg_tmp = 0; //If the write data pointer is not null if (data != NULL) { //Clear the lower 16 bits of the slave address register before writing I2C0_REG(I2C0_SADDR) &= ~(0xFFFF); I2C0_REG(I2C0_SADDR) &= ~ 1UL; //Set the lowest bit of the slave address register to 'write' I2C0_REG(I2C0_SADDR) |= ((slave_addr & 0X7F) << I2C0_DADDR); if (reg_addr % 8 == 0) //if the register address is 8-byte aligned { while (i2c0_data_len > 0)//Main loop { //AT24C02 can only write up to 8 bytes of data at a time len_tmp = (i2c0_data_len > 0x8) ? 0x8 : i2c0_data_len; //Write the current data_len to the count register, and subtract the current data length read from data_len I2C0_REG(I2C0_CNT) = len_tmp; i2c0_data_len -= len_tmp; // Before writing to the register address, clear the previous value I2C0_REG(I2C0_SADDR) &= ~(0xFF << I2C0_SREG); I2C0_REG(I2C0_SADDR) |= ((reg_addr & 0xFF) << I2C0_SREG); // Advance the register address to avoid repeated writing on the same register address in the EEPROM reg_addr += len_tmp; data_offset = len_tmp; //write data while (data_offset > 0) { //When the write FIFO is full, do not continue to write while ((I2C0_REG(I2C0_STATUS) & I2C0_ST_FIFO_FULL_MASK)) continue ; //write data I2C0_REG(I2C0_WDATA) = *data; printf ("%02x ",*data); data++; //write pointer move data_offset--; //After writing a byte of data, data_offset is reduced by 1, until at most 8 bytes of data are written at one time } // start function I2C0_START(); // Determine whether the write FIFO is empty (actually detect the word count of the FIFO), and the next operation can only be performed after confirmation while ((I2C0_REG(I2C0_STATUS) >> I2C0_ST_FIFO_WCNT) & 0x0f) continue ; } } else // (reg_addr % 8 != 0), the register address is not 8-byte aligned //Write the unaligned part first, (except for the last operation) subsequent write operations are all aligned with 8 bytes { reg_tmp = reg_addr - ((reg_addr/8)*8); //The register address is not aligned len_tmp = 8 - reg_tmp; //current data length //Write the current data_len to the count register, and subtract the current data length read from data_len I2C0_REG(I2C0_CNT) = len_tmp; i2c0_data_len -= len_tmp; // Before writing to the register address, clear the previous value I2C0_REG(I2C0_SADDR) &= ~(0xFF << I2C0_SREG); I2C0_REG(I2C0_SADDR) |= ((reg_addr & 0xFF) << I2C0_SREG); // Advance the register address to avoid repeated writing on the same register address in the EEPROM reg_addr += len_tmp; data_offset = len_tmp; //When the write FIFO is full, do not continue to write while ((I2C0_REG(I2C0_STATUS) & I2C0_ST_FIFO_FULL_MASK)) continue ; //write data while (data_offset > 0) { I2C0_REG(I2C0_WDATA) = *data; printf ("%02x ",*data); data++; //ptr moves up data_offset--; } I2C0_START(); //If the write FIFO is empty, continue to the next write while ((I2C0_REG(I2C0_STATUS) >> I2C0_ST_FIFO_WCNT) & 0x0f) continue ; //The rest of the alignment data is written while (i2c0_data_len > 0) { len_tmp = (i2c0_data_len > 0x8) ? 0x8 : i2c0_data_len; //AT24C02 can only write up to 8 bytes of data at a time //Write the current data_len to the count register, and subtract the current data length read from data_len I2C0_REG(I2C0_CNT) = len_tmp; i2c0_data_len -= len_tmp; // Before writing to the register address, clear the previous value I2C0_REG(I2C0_SADDR) &= ~(0xFF << I2C0_SREG); I2C0_REG(I2C0_SADDR) |= ((reg_addr & 0xFF) << I2C0_SREG); // Advance the register address to avoid repeated writing on the same register address in the EEPROM reg_addr += len_tmp; data_offset = len_tmp; //When the write FIFO is full, do not continue to write while ((I2C0_REG(I2C0_STATUS) & I2C0_ST_FIFO_FULL_MASK)) continue ; //write data while (data_offset > 0) { I2C0_REG(I2C0_WDATA) = *data; printf ("%02x ",*data); data++; //ptr moves up data_offset--; } I2C0_START(); //If the write FIFO is empty, continue to the next write while ((I2C0_REG(I2C0_STATUS) >> I2C0_ST_FIFO_WCNT) & 0x0f) continue ; } } } printf ("\r\n\r\n"); return ; }
2. main.c
The main function’s processing of interrupts temporarily only prints the current interrupt type after entering the interrupt. Only the newly added parts in the main function compared to the previous ones 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 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 //declare global variables, two string pointers u8_t* wdata; u8_t* wwdata; //I2C0 interrupt handler void i2c0_handler ( void ) { unsigned int irq_case; //Determine what caused the I2C0 interrupt irq_case = I2C0_REG(I2C0_IP); switch (irq_case) { case 0x1: //Write watermark threshold interrupt printf ("\r\n I2C0 write data watermark interrupt\r\n"); //Clear wwm interrupt I2C0_REG(I2C0_IC) |= 1 << I2C0_IRQ_WWM_MASK; // disable wwm interrupts I2C0_REG(I2C0_IE) &= ~(1UL << I2C0_IRQ_WWM_MASK); break ; case 0x2: //Read watermark threshold interrupt printf ("\r\n I2C0 read data watermark interrupt\r\n"); //Clear rwm interrupt I2C0_REG(I2C0_IC) |= 1 << I2C0_IRQ_RWM_MASK; // disable rwm interrupts I2C0_REG(I2C0_IE) &= ~(1UL << I2C0_IRQ_RWM_MASK); break ; case 0x4: //No response interrupt printf ("\r\n I2C0 slave no ack error\r\n"); //Clear the nack interrupt I2C0_REG(I2C0_IC) |= 1 << I2C0_IRQ_NACK_MASK; // disable nack interrupt I2C0_REG(I2C0_IE) &= ~(1UL << I2C0_IRQ_NACK_MASK); break ; case 0x8: //Write data count interrupt printf ("\r\n I2C0 data count mismatch error\r\n"); //Clear DERROR interrupt I2C0_REG(I2C0_IC) |= 1 << I2C0_IRQ_DERROR_MASK; // disable DERROR interrupt I2C0_REG(I2C0_IE) &= ~(1UL << I2C0_IRQ_DERROR_MASK); break ; default : printf ("\r\n No I2C0 interrupt\r\n"); break ; } return ; }; //initialization function void _init ( void ) { time_cnt = 0; curr_IRQ_id = 0; //Disable start in I2C0 control register I2C0_REG(I2C0_CTRL) &= ~(1UL << I2C0_CTRL_START); //Enable i2c0 module enable I2C0_REG(I2C0_CTRL) |= 0x80000000; //Disable I2C0 interrupt I2C0_REG(I2C0_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; } //I2C0 interrupt handling g_ext_interrupt_handlers[INT_I2C0_BASE] = i2c0_handler; // Interrupts must be enabled at both the UART1 level and the PLIC level enable_plic_int(INT_I2C0_BASE); // The interrupt priority must be set above 0 to trigger an interrupt PLIC_set_priority(&g_plic, INT_I2C0_BASE, 3); //==================Interrupt setting variable================== int rwm = 3; // read watermark threshold setting int wwm = 3; // write watermark threshold setting int reg_addr = 9; // register address int slave_addr = 0x50; // slave address u32_t data_len = 50; // read/write data length u8_t* rdata = malloc (data_len);// Allocate read data pointer //Call functions I2C0_IRQ_register(I2C0_100K, rwm, wwm, I2C0_IRQ_RWM_MASK, I2C0_IRQ_DIS , I2C0_RESTART ); I2C0_WR_FF(slave_addr); I2C0_WR_DATA(reg_addr, slave_addr, wdata, data_len); I2C0_RD_DATA(reg_addr, slave_addr, rdata, data_len); // 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 ) { mm_heap_initialize(); // allocate write data pointer wdata = malloc (MEM_SIZE); wwdata = wdata; // fill the allocated space with numbers for ( int ll = 0; ll < 256 ; ll++) { *wwdata = ll; wwdata++; } //call the initialization function _init(); //Call the interrupt configuration function IRQ_register(); while ( 1 ) { for (i = 0; i < NOP_DELAY ; i ++ ) asm ("nop"); } }
source code download