1. Engineering code
Related reference articles:
RISC-V teaching plan
The interrupt caused by PWM is also an external interrupt, and its interrupt request is passed to the PLIC for processing. The corresponding software processing of PWM interrupt is very similar to GPIO interrupt (click here for details). So only the parts of the PWM interrupt code that are different from the previously introduced code are listed here.
1.1. fii_pwm.c
#include <stdio.h> #include <stdint.h> #include "fii_pwm.h" #include "platform.h" //Implement PWM_IRQ_register function, configure PWM enable, period, duty cycle, count void PWM_IRQ_register( u32_t pwm_pin, u32_t pwm_period, u32_t pwm_period_DC, u32_t pwm_cnt,E_PWM_IRQ_SW pwm_sw) { //verilog source code is located in "RIB_fii_pwm" switch(pwm_pin) //PWM0/1/2/3 { case 0x00: //pwm0 PWM_REG(PWM_PERIOD + 32)= pwm_period; //period PWM_REG(PWM_CNT + 32) = pwm_cnt; //count PWM_REG(PWM_PERIOD_DC + 32) = pwm_period_DC; //duty cycle break; case 0x08: //pwm1 PWM_REG(PWM_PERIOD + 64)= pwm_period; //period PWM_REG(PWM_CNT + 64) = pwm_cnt; //count PWM_REG(PWM_PERIOD_DC + 64) = pwm_period_DC; //duty cycle break; case 0x10: //pwm2 PWM_REG(PWM_PERIOD + 96)= pwm_period; //period PWM_REG(PWM_CNT + 96) = pwm_cnt; //count PWM_REG(PWM_PERIOD_DC + 96) = pwm_period_DC; //duty cycle break; case 0x18: //pwm3 PWM_REG(PWM_PERIOD + 128)= pwm_period; //period PWM_REG(PWM_CNT + 128) = pwm_cnt; //count PWM_REG(PWM_PERIOD_DC + 128) = pwm_period_DC; //duty cycle break; default: printf("No valid pwm pin"); break; } if(pwm_sw == PWM_IRQ_DIS) //disable interrupt { PWM_REG(PWM_IRQ_ENA) &= ~(1UL<<pwm_pin); //Interrupt enable PWM_REG(PWM_IRQ_MASK) &= ~(1UL<<pwm_pin); //interrupt mask } else //enable interrupt { PWM_REG(PWM_IRQ_ENA) |= 1UL<<pwm_pin; //Interrupt enable PWM_REG(PWM_IRQ_MASK) |= 1UL<<pwm_pin; //interrupt mask } return; }
1.2. platform.h
//Define a simple expression for PWM address access #define PWM_REG(offset) (*(volatile unsigned int *)(PWM_ADDR + offset)) //Define PWM interrupt ID //In the current PWM group, only the first PWM of each group is implemented #define INT_PWM0_BASE 44 #define INT_PWM1_BASE 48 #define INT_PWM2_BASE 52 #define INT_PWM3_BASE 56 //Define the PWM offset #define PWM_0_OFFSET (0) #define PWM_1_OFFSET (8) #define PWM_2_OFFSET (16) #define PWM_3_OFFSET (24) //Redefine PWM interrupt ID #define INT_DEVICE_PWM_0 (INT_PWM0_BASE) #define INT_DEVICE_PWM_1 (INT_PWM1_BASE) #define INT_DEVICE_PWM_2 (INT_PWM2_BASE) #define INT_DEVICE_PWM_3 (INT_PWM3_BASE)
2. PWM experimental waveform
Set the IRQ_register function in main.c to generate waveforms with the same frequency, 100 KHz, and duty cycles of 3/5 and 1/5, respectively.
//Set interrupt related registers void IRQ_register (){ // Disable all interrupts until interrupt setup is complete clear_csr(mie, MIP_MEIP); // disable external interrupt clear_csr(mie, MIP_MTIP); // disable timer interrupt //Create an interrupt handler for each external interrupt source for (int ii = 0; ii < PLIC_NUM_INTERRUPTS; ii ++){ g_ext_interrupt_handlers[ii] = no_interrupt_handler; } //Create an interrupt handler for PWM g_ext_interrupt_handlers[INT_DEVICE_PWM_0] = PWM_0_handler; g_ext_interrupt_handlers[INT_DEVICE_PWM_1] = PWM_1_handler; // Enable interrupts at both the PLIC level and the GPIO level, here is the PLIC enable enable_plic_int(INT_DEVICE_PWM_0); enable_plic_int(INT_DEVICE_PWM_1); // The priority must be set to > 0 to effectively trigger the interrupt PLIC_set_priority(&g_plic, INT_DEVICE_PWM_0, 3); PLIC_set_priority(&g_plic, INT_DEVICE_PWM_1, 2); //Set the period, count, and duty cycle of the PWM interrupt PWM_IRQ_register(PWM_0_OFFSET, 500, 300, 0, PWM_IRQ_EN); PWM_IRQ_register(PWM_1_OFFSET, 500, 100, 0, PWM_IRQ_EN); //PWM interrupt module initialization ends }
As you can see, PWM0 and PWM1 are set to waveforms with a frequency of 50 MHz/500 = 100 KHz, respectively. A count of 0 means that the PWM will continue and work forever. The only difference is that the duty cycle of PWM0 is 300/500 = 60%, and the duty cycle of PWM1 is 100/500 = 20%.
Observe PWM0 and PWM through an oscilloscope, as shown in Figure 1, the yellow signal is PWM0, and the red signal is PWM1. It can be seen that the actual waveform is consistent with the setting above.
Figure 1 Oscilloscope to observe PWM0 and PWM1 outputs
Engineering code download: