FB pixel

Renesas RL78 - 5. Interrupt FunctionsNew

Published


Hi! Welcome back again to CircuitBread. In our previous tutorial, we discussed RL78 GPIOs and reviewed the code in our first project. I hope you have successfully programmed the 8 LED chaser without looking at my code. In this tutorial, we are going to discuss interrupts or interrupt functions.

Interrupt function is definitely not a beginner-level topic. However, we need to discuss it now because most of the RL78 peripherals generate an interrupt. But don’t worry, we will not discuss all of the interrupt sources in this tutorial. We will just use one to demonstrate how it works and as we discuss other peripherals in future tutorials, we will also show how to handle the interrupt generated by those peripherals.

Polling vs Interrupt

Let’s say we have nine tasks and one of these tasks requires special attention. We need to monitor the signal (flag/bit set to 1) that it provides so that the CPU can immediately perform the tasks under it.

Polling Flowchart Example (Blocking Mode)
Figure 1. Polling Flowchart Example (Blocking Mode).

In polling, whether it’s blocking or non-blocking mode, the CPU will check first if the request flag/bit is already set before it executes an important task. Figure 1 shows an example of a blocking mode polling flowchart. As you can see, tasks 1 to 4 are executed first and after executing task 4, the CPU will check if the request flag/bit is set to 1. If it’s not, then the CPU will wait until the request flag/bit is set to 1 before proceeding to task 6. As you can see in this example, the disadvantages here are: 1) The CPU will execute first tasks 1 to 4 before checking the more important task. What if, during task 1 or 2, the request flag/bit was already set to 1? So there will be a delay before the more important task is executed. 2) If the request flag/bit is not yet set, then the CPU will still need to wait before it can execute the important task and proceed to tasks 6 to 9.

Polling Flowchart Example (Non-blocking Mode)
Figure 2. Polling Flowchart Example (Non-blocking Mode).

Figure 2 shows an example of a non-blocking mode polling flowchart and its advantage compared to blocking mode is that the CPU will proceed to the other tasks if the request flag/bit is not yet set. But the delay of executing the more important task is longer. Let’s say the request flag/bit was set while the CPU is executing task 6. Then, the CPU will still need to execute tasks 7-9 and tasks 1-4 again before it executes the important task.

Interrupt Flowchart Example
Figure 3. Interrupt Flowchart Example.

Interrupts overcome the limitations of polling. If interrupts are enabled and the peripheral used in a task generates an interrupt, the CPU doesn’t need to monitor the request flag/bit anymore. When an interrupt event occurs, once the CPU is done with the current instruction, the program then jumps to the assigned interrupt service routine (ISR) of that interrupt. After executing the ISR, the program will return to the instruction or task where it was interrupted. Using interrupts makes our code efficient and important tasks are executed faster.

Interrupt Sources, Types, and Configurations

The number of interrupt sources in RL78 MCUs depends on the number of pins of the device. In the RL78/G14, there are at least 30 maskable interrupt sources in MCUs with the smallest number of pins (30-pins). You can check table 21-1 to table 21-4 of the RL78/G14 hardware user’s manual for the list of interrupt sources and their types.

There are two types of interrupt functions used in RL78 MCUs, Maskable/Non-Maskable Interrupts and Software Interrupts.

  1. Maskable interrupts are interrupts that can be enabled or disabled by clearing or setting the interrupt mask flag registers, respectively. Disabling interrupts is usually done during hardware initialization to avoid problems. Once hardware initialization is done, interrupts are enabled again. Non-Maskable interrupts are interrupts that can’t be disabled and ignored by the CPU because they are very important. Examples of non-maskable interrupt sources are reset on reset pin input, overflow of the watchdog timer (WDT), low voltage detection, etc.
  2. Software Interrupt is generated when the BRK instruction is executed. It can’t be disabled and doesn’t undergo interrupt priority control. Even if interrupts are disabled, the software interrupt is still acknowledged and the program will jump to its ISR when this instruction is executed.

There might be more or less configurations in other RL78 MCUs, but in an RL78/G14 MCU, there are four basic interrupt configurations: internal, external (INTPn) , external (INTKR), and software configuration.

RL78/G14 Internal Maskable Interrupt Configuration
Figure 4. RL78/G14 Internal Maskable Interrupt Configuration.

Internal interrupts are generated by peripherals for example when the ADC is done converting or the UART is done transferring data. You can check table 21-1 to table 21-4 of the RL78/G14 hardware user’s manual which interrupt sources have this kind of configuration. As shown in figure 4, in order to use an internal interrupt, we need to set or clear these flags, which we will discuss momentarily: IF, MK, IE, PR0, PR1, ISP0, and ISP1.

RL78/G14 External Maskable Interrupt (INTPn) Configuration
Figure 5. RL78/G14 External Maskable Interrupt (INTPn) Configuration.

External interrupts are generated by inputting a rising or falling edge (or both depending on the EGP and EGN settings) to the external interrupt input pins INTPn or a falling edge to the key interrupt input pins KRn. External interrupt configurations are similar to internal interrupt configuration except that there are additional registers that we need to set or clear, EGP and EGN registers for external interrupt pins (see figure 5), and KRM register for key interrupt pins (see figure 6). The external interrupt and key interrupt pins are almost similar but I think the key interrupt function is more suitable for reading a key matrix.

RL78/G14 External Maskable Interrupt (INTKR) Configuration
Figure 6. RL78/G14 External Maskable Interrupt (INTKR) Configuration.

The software interrupt has a very simple configuration. As shown in figure 7, only an interrupt request is needed for it to be processed. You don’t need to set or clear any registers.

RL78/G14 Software Interrupt Configuration
Figure 7. RL78/G14 Software Interrupt Configuration.

Registers Controlling Interrupt Functions

As mentioned earlier, in order to use an interrupt function, we need to set or clear some flags and registers that control it.

  • Interrupt Enable Flag IE

    The IE flag which is in the program status word PSW register is used to enable or disable all of the maskable interrupts in the RL78 MCU. If you clear this flag, you won’t be able to use any maskable interrupt functions. In our first project, if you follow the hardware initialization steps by clicking the Step Into button or pressing the F5 function key, you will notice the DI() and EI() functions which disables and enables the IE flag, respectively. After reset, the value of the IE flag is 0.
Program Status Word (PSW) Register
Figure 8. Program Status Word (PSW) Register.
  • Interrupt Request Flag IF

    When there’s a request from an interrupt source, the IF of that interrupt source is set to 1. According to the hardware user’s manual, the IF can also be set to 1 by executing an instruction. For now, I can’t think of a situation where we need to set the IF through software so let’s just ignore it for now.
XXIFXInterrupt Request Flag
0No interrupt request signal is generated
1Interrupt request is generated, interrupt request status
  • When the IF is set to 1 and the CPU acknowledges the interrupt, the program then jumps to the interrupt service routine. If there’s a situation where we need to disable or mask an interrupt, the IF can still be accessed by software to check if there’s a request. The IFs are located in these registers: IF0L, IF0H, IF1L, IF1H, IF2L, IF2H (check chapter 21.3.1 of the RL78/G14 hardware user’s manual).

    You can set or clear an IF using a 1-bit memory manipulation instruction or set the entire interrupt request flag register using an 8-bit memory manipulation instruction. You can also use a 16-bit memory manipulation instruction if you combine the L and H registers, for example IF0L and IF0H → IF0. A reset signal clears the interrupt request flag registers to 00H (hex).
  • Interrupt Mask Flag MK
XXMKXInterrupt Servicing Control
0Interrupt Servicing Enabled
1Interrupt Servicing Disabled
  • If an interrupt is maskable, we can mask or disable the interrupt by setting its MK flag to 1. By clearing (write 0 to it) the MK flag, the interrupt is enabled. The MK flags are located in these registers: MK0L, MK0H, MK1L, MK1H, MK2L, MK2H (check chapter 21.3.2 of the RL78/G14 hardware user’s manual). An MK flag can be set using a 1-bit memory manipulation instruction or you can set the entire interrupt mask flag register using an 8-bit memory manipulation instruction. You can also use a 16-bit memory manipulation instruction if you combine the L and H registers, for example MK0L and MK0H → MK0. A reset signal sets the interrupt request flag registers to FFH (hex).
  • Priority Specification Flags PR0 and PR1

    We prioritize important tasks by using interrupts. However, in RL78 MCUs or even in other MCUs, there are a lot of interrupt sources and if they occur simultaneously, the CPU will respond first to the interrupt with higher priority level.

    The PR0 and PR1 flags set the priority level of an interrupt source and since there are two flags, it means that we can set the priority level of an interrupt to one of four levels, 00 (level 0 - high priority level), 01 (level 1), 10 (level 2), 11 (level 3 - low priority level).
XXPR1XXXPR0XPriority level selection
00Specify level 0 (high priority level)
01Specify level 1
10Specify level 2
11Specify level 3 (low priority level)
  • But what if two or more interrupt sources with the same priority level occur at the same time? In this case, the CPU will respond to the interrupt source with a lower default priority number. You can check the default priority number of an interrupt source in table 21-1 to table 21-4 of the RL78/G14 hardware user’s manual.

    The priority specification flags of an interrupt source are located in these registers: PR00L, PR00H, PR01L, PR01H, PR02L, PR02H, PR10L, PR10H, PR11L, PR11H, PR12L, PR12H (chapter 21.3.3 of the RL78/G14 hardware user’s manual) or I think it is easier to identify the priority specification flags of an interrupt source by checking table 21-5 to table 21-8 of the RL78/G14 hardware user’s manual. Again, the PR0 and PR1 flags can be set using a 1-bit memory manipulation instruction or you can set the priority specification flag registers using 8-bit memory manipulation instruction. The L and H registers can be also combined and set using a 16-bit memory manipulation instruction, for example PR00L and PR00H → PR00. A reset signal sets priority specification flag registers to FFH (hex).
  • In-Service Priority Flags ISP0 and ISP1

    While the PR0 and PR1 flags set the priority level of an interrupt source, the ISP0 and ISP1 which are located in the program status word PSW register set the current interrupt priority level of the CPU.
ISP1ISP0Priority of interrupt currently being serviced
00Enables interrupt of level 0

(while interrupt of level 1 or 0 is being serviced)

01Enables interrupt of level 0 and 1

(while interrupt of level 2 is being serviced)

10Enables interrupt of level 0 to 2

(while interrupt of level 3 is being serviced)

11Enables all interrupts

(waits for acknowledgment of an interrupt)

  • As you can see in figure 8, if the value of the priority level of an interrupt source (set by PR0 and PR1 flags) is greater than the value of the ISP0 and ISP1 flags, the CPU will ignore that interrupt source. In other words, if the priority level of an interrupt source is lower than what is set by the ISP0 and ISP1 flags, the CPU will just ignore the interrupt. For example, if ISP1 and ISP0 is set to 10, all level 3 interrupts will be ignored, only level 0 to 2 interrupt sources will be processed. After reset, the value of ISP1 and ISP0 is 11. So by default, all interrupt levels are acknowledged.
RL78 Interrupt Request Acknowledgment Processing Algorithm
Figure 9. RL78 Interrupt Request Acknowledgment Processing Algorithm.

Since our example in this tutorial uses an external interrupt pin, we will add these two registers to our discussion: External interrupt rising edge enable registers EGP0, EGP1 and External interrupt falling edge enable registers EGN0, EGN1.

The detection enable bits, EGPn and EGNn (n = 0 to 11), which sets what type of edge will generate an external interrupt are located in the EGP0, EGP1 and EGN0, EGN1 registers, respectively. The table below shows what value should we write the detection enable bits to disable edge detection or detect a falling edge, a rising edge, or both.

EGPnEGNnINTPn pin valid edge selection (n = 0 to 11)
00Edge detection disabled
01Falling edge
10Rising edge
11Both rising and falling edges

RL78 MCUs can have up to 12 external interrupt pins depending on the number of the MCU pins. You can check table 21-9 of the RL78/G14 hardware user’s manual to see the detection enable bits of the external interrupts.

After reset, the default value of EGP0, EGP1 and EGN0, EGN1 registers are 00H (hex). So when using external interrupts, the detection enable bits should be configured because by default, edge detection is disabled. Also, if you’re going to use external interrupts, you should also set the PM register of the pin to input mode. If you decide to switch the external interrupt pin to output mode, make sure that you disable edge detection as an interrupt might be generated when it detects an edge from the output. The EGP0, EGP1 and EGN0, EGN1 registers can be set by a 1-bit or 8-bit memory manipulation instruction.

INTP1 External Interrupt

So we’re done discussing interrupts and the registers controlling it. As mentioned earlier, we’re going to have one example and we will try the INTP1 external interrupt. Figure 10 shows the schematic diagram for our interrupt example. Remember our example when we compared polling to interrupt? We will try to follow that.

INTP1 Interrupt Demo Schematic Diagram
Figure 10. INTP1 Interrupt Demo Schematic Diagram.

Here we have nine LEDs which represent the nine tasks mentioned in the Polling vs Interrupt example. LED1 to LED8 are connected to Port 1 pins while LED9 is connected to pin P52. Port 1 pins are connected to the MCU header J2. So if you haven’t yet mounted the MCU headers, this is the time to mount them or you can use other pins in the Arduino headers and just change the code. The external interrupt INTP1 is an alternate function of the GPIO P50 (check chapter 2.1.17 of the hardware user’s manual). So as you can see in the schematic diagram, we connected the pin P50 between the switch S1 and the 10k external pull-up resistor R9. Please check the RL78/G14 FPB schematic diagram to verify which headers Port 1 pins, pin P52, and pin P50 are connected.

RL78/G14 FPB J2, J6, and J8 Headers
Figure 11. RL78/G14 FPB J2, J6, and J8 Headers.
INTP1 External Interrupt Demo
Figure 12. INTP1 External Interrupt Demo.

The flow of our program here is very simple. The normal operation is “turn on LED1 to LED8 and then turn them off from LED8 to LED1”. Each time an interrupt event occurs when we press switch S1, the normal operation is interrupted and then LED9 will be toggled. But before we demonstrate interrupts, we will try the blocking and non-blocking mode polling first. Let’s first create a project and then replace everything in the r_main.c file with the blocking mode polling code below. In case you forgot how to create a project, check this tutorial again: Renesas RL78 - 3. First Project.

/***********************************************************************************************************************

* DISCLAIMER

* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products.

* No other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all

* applicable laws, including copyright laws.

* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING THIS SOFTWARE, WHETHER EXPRESS, IMPLIED

* OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

* NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY

* LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE FOR ANY DIRECT,

* INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR

* ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability

* of this software. By using this software, you agree to the additional terms and conditions found by accessing the

* following link:

* http://www.renesas.com/disclai...

*

* Copyright (C) 2011, 2021 Renesas Electronics Corporation. All rights reserved.

***********************************************************************************************************************/

/***********************************************************************************************************************

* File Name : r_main.c

* Version : CodeGenerator for RL78/G14 V2.05.06.02 [08 Nov 2021]

* Device(s) : R5F104ML

* Tool-Chain : GCCRL78

* Description : This file implements main function.

* Creation Date: 31/08/2022

***********************************************************************************************************************/

/***********************************************************************************************************************

Includes

***********************************************************************************************************************/

#include "r_cg_macrodriver.h"

#include "r_cg_cgc.h"

/* Start user code for include. Do not edit comment generated here */

/* End user code. Do not edit comment generated here */

#include "r_cg_userdefine.h"

/***********************************************************************************************************************

Global variables and functions

***********************************************************************************************************************/

/* Start user code for global. Do not edit comment generated here */

void forloop_delay(void);

/* End user code. Do not edit comment generated here */

void R_MAIN_UserInit(void);

/***********************************************************************************************************************

* Function Name: main

* Description : This function implements main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void main(void)

{

R_MAIN_UserInit();

/* Start user code. Do not edit comment generated here */

PM1 = 0;

P1 = 0;

PM5_bit.no2 = 0;

P5_bit.no2 = 0;

PMK1 = 1U; /* disable INTP1 operation */

PIF1 = 0U; /* clear INTP1 interrupt flag */

PM5_bit.no0 = 1; /* Set P50 as Input */

EGN0_bit.no1 = 1U; /* Falling Edge */

while (1U)

{

P1_bit.no0 = 1;

forloop_delay();

P1_bit.no1 = 1;

forloop_delay();

P1_bit.no2 = 1;

forloop_delay();

P1_bit.no3 = 1;

forloop_delay();

while (PIF1 != 1){}

forloop_delay();

forloop_delay();

P5_bit.no2 = !P5_bit.no2;

PIF1 = 0U;

P1_bit.no4 = 1;

forloop_delay();

P1_bit.no5 = 1;

forloop_delay();

P1_bit.no6 = 1;

forloop_delay();

P1_bit.no7 = 1;

forloop_delay();

P1_bit.no7 = 0;

forloop_delay();

P1_bit.no6 = 0;

forloop_delay();

P1_bit.no5 = 0;

forloop_delay();

P1_bit.no4 = 0;

forloop_delay();

while (PIF1 != 1){}

forloop_delay();

forloop_delay();

P5_bit.no2 = !P5_bit.no2;

PIF1 = 0U;

P1_bit.no3 = 0;

forloop_delay();

P1_bit.no2 = 0;

forloop_delay();

P1_bit.no1 = 0;

forloop_delay();

P1_bit.no0 = 0;

forloop_delay();

}

/* End user code. Do not edit comment generated here */

}

/***********************************************************************************************************************

* Function Name: R_MAIN_UserInit

* Description : This function adds user code before implementing main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void R_MAIN_UserInit(void)

{

/* Start user code. Do not edit comment generated here */

EI();

/* End user code. Do not edit comment generated here */

}

/* Start user code for adding. Do not edit comment generated here */

void forloop_delay(void)

{

uint16_t forloop_delay_var1;

uint8_t forloop_delay_var2;

for (forloop_delay_var1 = 0; forloop_delay_var1 < 65535; forloop_delay_var1++)

{

for (forloop_delay_var2 = 0; forloop_delay_var2 < 4; forloop_delay_var2++){}

}

}

/* End user code. Do not edit comment generated here */

In line 42 we declared the forloop_delay() function and defined it in line 135 to line 144. We’re going to call this function to generate a software delay.

Inside the main() function line 52 to line 118, we call the R_MAIN_UserInit() function in line 54 which is defined in line 127 to line 132 to enable interrupts globally using the EI() function line 130.

In line 56 to line 60, we set all of the pins of Port 1 and pin P52 as outputs. As you can see, we used the macro name PM1 and P1 to use an 8-bit memory manipulation instruction which sets the entire register.

In line 62, we masked the INTP1 interrupt by writing 1 to its interrupt mask flag PMK1. You can check table 21-5 to table 21-8 of the RL78/G14 hardware user’s manual to see the interrupt mask flag of INTP1 and other interrupt sources. Table 21-5 to table 21-8 also shows which register the interrupt mask flag belongs to. PMK1 is bit no. 3 of the MK0L register (check figure 21-5 of the RL78/G14 hardware user’s manual). So writing 1 to MK0L_bit.no3 will mask INTP1. But we can just directly write 1 to PMK1 since inside the iodefine.h header file, the value of the macro name PMK1 is MK0L_bit.no3#define PMK1 MK0L_bit.no3.

In line 63, we cleared INTP1 interrupt flag PIF1. The interrupt flag of an interrupt source and the register it belongs to are also shown in table 21-5 to table 21-8 of the RL78/G14 hardware user’s manual. The macro name PIF1 is also defined inside the iodefine.h header file.

The purpose of masking PMK1 and clearing PIF1 is to ensure that there will be no interrupt generated as we are setting the other registers that enables edge detection of the external interrupt INTP1. As you can see in line 64 and line 65, we set the pin P50 or INTP1 pin as input and then make it detect a falling edge by writing 1 to bit no.1 of the EGN0 register. As shown in figure 21-10 of the RL78/G14 hardware user’s manual, we need to write 0 to the EGPn bit and 1 to the EGNn bit to select the falling edge. n here is 1 as the external interrupt being used is INTP1. After reset, the value of EGP0_bit.no1 and EGN0_bit.no1 is 0. So in our code we only write 1 to EGN0_bit.no1 since EGP0_bit.no1 is 0 by default.

So that will enable the P50/INTP1 pin to detect a falling edge now. Since we’re using polling here, we don’t need to unmask the PMK1 flag. So our code now enters the infinite while loop in line 67 to line 116.

while (1U)

{

P1_bit.no0 = 1;

forloop_delay();

P1_bit.no1 = 1;

forloop_delay();

P1_bit.no2 = 1;

forloop_delay();

P1_bit.no3 = 1;

forloop_delay();

while (PIF1 != 1){}

forloop_delay();

forloop_delay();

P5_bit.no2 = !P5_bit.no2;

PIF1 = 0U;

P1_bit.no4 = 1;

forloop_delay();

P1_bit.no5 = 1;

forloop_delay();

P1_bit.no6 = 1;

forloop_delay();

P1_bit.no7 = 1;

forloop_delay();

P1_bit.no7 = 0;

forloop_delay();

P1_bit.no6 = 0;

forloop_delay();

P1_bit.no5 = 0;

forloop_delay();

P1_bit.no4 = 0;

forloop_delay();

while (PIF1 != 1){}

forloop_delay();

forloop_delay();

P5_bit.no2 = !P5_bit.no2;

PIF1 = 0U;

P1_bit.no3 = 0;

forloop_delay();

P1_bit.no2 = 0;

forloop_delay();

P1_bit.no1 = 0;

forloop_delay();

P1_bit.no0 = 0;

forloop_delay();

}

/* End user code. Do not edit comment generated here */

As you can see, the code inside the while loop is mostly just composed of lines that turn ON and OFF the LEDs. But in line 78 and line 102, there’s a while loop that blocks the normal operation of the code. It polls the status of the PIF1 flag. While PIF1 is 0 (switch S1 is not pressed), the code is stuck in an infinite loop doing nothing. When the switch S1 is pressed (PIF1 flag is set), the program will exit the while loop, toggle LED9, clear the PIF1 flag, and then return to its normal operation again. Upload the code to your RL78/G14 FPB to see how it works in real life.

So as you can see, the disadvantage of blocking mode polling is that the CPU still needs to wait for the interrupt flag to be set before it executes the task and proceeds to the other tasks.

Now, let’s check the code below for the non-blocking mode polling example.

/***********************************************************************************************************************

* DISCLAIMER

* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products.

* No other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all

* applicable laws, including copyright laws.

* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING THIS SOFTWARE, WHETHER EXPRESS, IMPLIED

* OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

* NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY

* LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE FOR ANY DIRECT,

* INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR

* ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability

* of this software. By using this software, you agree to the additional terms and conditions found by accessing the

* following link:

* http://www.renesas.com/disclai...

*

* Copyright (C) 2011, 2021 Renesas Electronics Corporation. All rights reserved.

***********************************************************************************************************************/

/***********************************************************************************************************************

* File Name : r_main.c

* Version : CodeGenerator for RL78/G14 V2.05.06.02 [08 Nov 2021]

* Device(s) : R5F104ML

* Tool-Chain : GCCRL78

* Description : This file implements main function.

* Creation Date: 31/08/2022

***********************************************************************************************************************/

/***********************************************************************************************************************

Includes

***********************************************************************************************************************/

#include "r_cg_macrodriver.h"

#include "r_cg_cgc.h"

/* Start user code for include. Do not edit comment generated here */

/* End user code. Do not edit comment generated here */

#include "r_cg_userdefine.h"

/***********************************************************************************************************************

Global variables and functions

***********************************************************************************************************************/

/* Start user code for global. Do not edit comment generated here */

void forloop_delay(void);

/* End user code. Do not edit comment generated here */

void R_MAIN_UserInit(void);

/***********************************************************************************************************************

* Function Name: main

* Description : This function implements main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void main(void)

{

R_MAIN_UserInit();

/* Start user code. Do not edit comment generated here */

PM1 = 0;

P1 = 0;

PM5_bit.no2 = 0;

P5_bit.no2 = 0;

PMK1 = 1U; /* disable INTP1 operation */

PIF1 = 0U; /* clear INTP1 interrupt flag */

PM5_bit.no0 = 1; /* Set P50 as Input */

EGN0_bit.no1 = 1U; /* Falling Edge */

while (1U)

{

P1_bit.no0 = 1;

forloop_delay();

P1_bit.no1 = 1;

forloop_delay();

P1_bit.no2 = 1;

forloop_delay();

P1_bit.no3 = 1;

forloop_delay();

if (PIF1 == 1)

{

forloop_delay();

forloop_delay();

P5_bit.no2 = !P5_bit.no2;

PIF1 = 0U;

}

P1_bit.no4 = 1;

forloop_delay();

P1_bit.no5 = 1;

forloop_delay();

P1_bit.no6 = 1;

forloop_delay();

P1_bit.no7 = 1;

forloop_delay();

P1_bit.no7 = 0;

forloop_delay();

P1_bit.no6 = 0;

forloop_delay();

P1_bit.no5 = 0;

forloop_delay();

P1_bit.no4 = 0;

forloop_delay();

P1_bit.no3 = 0;

forloop_delay();

P1_bit.no2 = 0;

forloop_delay();

P1_bit.no1 = 0;

forloop_delay();

P1_bit.no0 = 0;

forloop_delay();

}

/* End user code. Do not edit comment generated here */

}

/***********************************************************************************************************************

* Function Name: R_MAIN_UserInit

* Description : This function adds user code before implementing main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void R_MAIN_UserInit(void)

{

/* Start user code. Do not edit comment generated here */

EI();

/* End user code. Do not edit comment generated here */

}

/* Start user code for adding. Do not edit comment generated here */

void forloop_delay(void)

{

uint16_t forloop_delay_var1;

uint8_t forloop_delay_var2;

for (forloop_delay_var1 = 0; forloop_delay_var1 < 65535; forloop_delay_var1++)

{

for (forloop_delay_var2 = 0; forloop_delay_var2 < 4; forloop_delay_var2++){}

}

}

/* End user code. Do not edit comment generated here */

Actually, the code is just similar to the blocking mode polling code except that we use an if statement instead of a while loop. Also, we only used one if statement so that in real life, the disadvantage of the non-blocking mode polling will be noticeable.

Inside the while loop line 67 to line 111, the code will just repeatedly turn on LED1 to LED8 and then turn off LED8 to LED1. The code in line 78 to line 84 will not block the operation. This is the advantage of non-blocking mode polling over the blocking mode polling as the operation of the program will not be blocked. Upload the code to your RL78/G14 FPB to see how it works in reality.

However, If we want to execute the code inside the if statement so we press the switch S1 to set the PIF1 flag but the code is now in line 86 turning on LED5, we still need to wait for the program to finish the loop and execute line 69 to line 76 again before the code inside the if statement will be executed. If the code inside the if statement needs to be executed as soon as possible, then using a non-blocking mode polling is not a good idea.

Now, let’s check the code that uses interrupt to see how interrupts overcome the limitations of polling.

/***********************************************************************************************************************

* DISCLAIMER

* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products.

* No other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all

* applicable laws, including copyright laws.

* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING THIS SOFTWARE, WHETHER EXPRESS, IMPLIED

* OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

* NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY

* LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE FOR ANY DIRECT,

* INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR

* ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability

* of this software. By using this software, you agree to the additional terms and conditions found by accessing the

* following link:

* http://www.renesas.com/disclai...

*

* Copyright (C) 2011, 2021 Renesas Electronics Corporation. All rights reserved.

***********************************************************************************************************************/

/***********************************************************************************************************************

* File Name : r_main.c

* Version : CodeGenerator for RL78/G14 V2.05.06.02 [08 Nov 2021]

* Device(s) : R5F104ML

* Tool-Chain : GCCRL78

* Description : This file implements main function.

* Creation Date: 31/08/2022

***********************************************************************************************************************/

/***********************************************************************************************************************

Includes

***********************************************************************************************************************/

#include "r_cg_macrodriver.h"

#include "r_cg_cgc.h"

/* Start user code for include. Do not edit comment generated here */

/* End user code. Do not edit comment generated here */

#include "r_cg_userdefine.h"

/***********************************************************************************************************************

Global variables and functions

***********************************************************************************************************************/

/* Start user code for global. Do not edit comment generated here */

void forloop_delay(void);

void INTP1_Init(void);

void INTP1_Start(void);

void INTP1_Stop(void);

void INTP1_ISR(void);

/* End user code. Do not edit comment generated here */

void R_MAIN_UserInit(void);

/***********************************************************************************************************************

* Function Name: main

* Description : This function implements main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void main(void)

{

R_MAIN_UserInit();

/* Start user code. Do not edit comment generated here */

PM1 = 0;

P1 = 0;

PM5_bit.no2 = 0;

P5_bit.no2 = 0;

while (1U)

{

P1_bit.no0 = 1;

forloop_delay();

P1_bit.no1 = 1;

forloop_delay();

P1_bit.no2 = 1;

forloop_delay();

P1_bit.no3 = 1;

forloop_delay();

P1_bit.no4 = 1;

forloop_delay();

P1_bit.no5 = 1;

forloop_delay();

P1_bit.no6 = 1;

forloop_delay();

P1_bit.no7 = 1;

forloop_delay();

P1_bit.no7 = 0;

forloop_delay();

P1_bit.no6 = 0;

forloop_delay();

P1_bit.no5 = 0;

forloop_delay();

P1_bit.no4 = 0;

forloop_delay();

P1_bit.no3 = 0;

forloop_delay();

P1_bit.no2 = 0;

forloop_delay();

P1_bit.no1 = 0;

forloop_delay();

P1_bit.no0 = 0;

forloop_delay();

}

/* End user code. Do not edit comment generated here */

}

/***********************************************************************************************************************

* Function Name: R_MAIN_UserInit

* Description : This function adds user code before implementing main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void R_MAIN_UserInit(void)

{

/* Start user code. Do not edit comment generated here */

EI();

INTP1_Init();

INTP1_Start();

/* End user code. Do not edit comment generated here */

}

/* Start user code for adding. Do not edit comment generated here */

void forloop_delay(void)

{

uint16_t forloop_delay_var1;

uint8_t forloop_delay_var2;

for (forloop_delay_var1 = 0; forloop_delay_var1 < 65535; forloop_delay_var1++)

{

for (forloop_delay_var2 = 0; forloop_delay_var2 < 4; forloop_delay_var2++){}

}

}

void INTP1_Init(void)

{

PMK1 = 1U; /* disable INTP1 operation */

PIF1 = 0U; /* clear INTP1 interrupt flag */

/* Set INTP1 high priority */

PPR11 = 0U;

PPR01 = 0U;

PM5_bit.no0 = 1; /* Set P50 as Input */

EGN0_bit.no1 = 1U; /* Falling Edge */

}

void INTP1_Start(void)

{

PIF1 = 0U; /* clear INTP0 interrupt flag */

PMK1 = 0U; /* enable INTP0 interrupt */

}

void INTP1_Stop(void)

{

PMK1 = 1U; /* disable INTP0 interrupt */

PIF1 = 0U; /* clear INTP0 interrupt flag */

}

void INTP1_ISR(void)

{

INTP1_Stop();

P5_bit.no2 = !P5_bit.no2;

forloop_delay();

forloop_delay();

INTP1_Start();

}

/* End user code. Do not edit comment generated here */

As you can see in the code, we’ve declared four new functions in line 43 to line 46: INTP1_Init(), INTP1_Start(), INTP1_Stop(), and INTP1_ISR(). These functions are defined in line 133 to line 165.

When the program enters the main() function after hardware initialization, the first thing it executes is the R_MAIN_UserInit() function which is in line 112 to line 119. Then inside the R_MAIN_UserInit() function, three functions will be called, EI(), INTP1_Init(), and INTP1_Start().

External Maskable Interrupt (INTPn) Configuration

As discussed earlier, to enable maskable interrupts, we need to set the IE flag and we can do that by calling the EI() function.

The INTP1_Init() function initializes the INTP1 interrupt. As shown in figure 5, to use an external interrupt, we need to configure these flags/bits: IE, IF, MK, PR0 and PR1, ISP0 and ISP1, and EGP and EGN. We’ve already discussed how to set or clear the INTP1 IF flag PIF1, MK flag PMK1, and its detection enable bits EGP1 and EGN1 and we’ve just mentioned that the IE flag is set by calling EI(). So this time, we just need to discuss the PR1, PR0 and ISP1, ISP0 flags. Table 21-5 to table 21-8 of the RL78/G14 hardware user’s manual shows the priority specification flags of INTP1 and other interrupt sources. For the configuration of the in-service priority flags, refer to the table below.

ISP1ISP0Priority of interrupt currently being serviced
00Enables interrupt of level 0

(while interrupt of level 1 or 0 is being serviced)

01Enables interrupt of level 0 and 1

(while interrupt of level 2 is being serviced)

10Enables interrupt of level 0 to 2

(while interrupt of level 3 is being serviced)

11Enables all interrupts

(waits for acknowledgment of an interrupt)

void INTP1_Init(void)

{

PMK1 = 1U; /* disable INTP1 operation */

PIF1 = 0U; /* clear INTP1 interrupt flag */

/* Set INTP1 high priority */

PPR11 = 0U;

PPR01 = 0U;

PM5_bit.no0 = 1; /* Set P50 as Input */

EGN0_bit.no1 = 1U; /* Falling Edge */

}

In line 135 to line 136, we masked the INTP1 PMK1 flag and cleared its PIF1 flag first to make sure that INTP1 would not generate an interrupt. In this example, we set the priority of the INTP1 interrupt to high and we did that by writing 0 to its PR1 flag PPR11 and PR0 PPR01 flag (check table below for the configuration of priority level). As you can see in line 139 and line 140, we set both PPR11 and PPR01 to 0. Inside iodefine.h header file, the value of the macro name PPR11 is PR10L_bit.no3#define PPR11 PR10L_bit.no3 and PPR01 is PR00L_bit.no3#define PPR01 PR00L_bit.no3.

XXPR1XXXPR0XPriority level selection
00Specify level 0 (high priority level)
01Specify level 1
10Specify level 2
11Specify level 3 (low priority level)

In this example, we don’t need to filter interrupts according to their priority level so we’ll just enable all interrupts by setting ISP1, ISP0 bits to 11. According to chapter 21.3.5 of the RL78/G14 hardware user’s manual, after reset, the value of the PSW register is 06H (hex). That means that we don’t need to touch the ISP1, ISP0 bits anymore as their value is already 1 by default. As you can see in our code, we didn’t set these bits anymore.

void INTP1_Init(void)

{

PMK1 = 1U; /* disable INTP1 operation */

PIF1 = 0U; /* clear INTP1 interrupt flag */

/* Set INTP1 high priority */

PPR11 = 0U;

PPR01 = 0U;

PM5_bit.no0 = 1; /* Set P50 as Input */

EGN0_bit.no1 = 1U; /* Falling Edge */

}

Now, to enable edge detection at P50/INTP1 pin, we set it as input in line 142 and in line 143, we set it to detect a falling edge. So that’s the end of the INTP1_Init() function and the program returns to the R_MAIN_UserInit() function and calls the INTP1_Start().

void INTP1_Start(void)

{

PIF1 = 0U; /* clear INTP0 interrupt flag */

PMK1 = 0U; /* enable INTP0 interrupt */

}

INTP1_Start() is in line 146 to line 150. The purpose of this function is just to enable INTP1 by clearing first the PIF1 interrupt flag line 148 and then unmasking the PMK1 flag line 149. After this, the function returns again to the R_MAIN_UserInit() function then back to line 60 of the main() function. But before that, let’s first discuss the INTP1_Stop() and the INTP1_ISR() functions.

The INTP1_Stop() function will just mask the PMK1 flag and clear the PIF1 flag. We will call this function each time the program jumps to the INTP1 ISR.

void INTP1_Stop(void)

{

PMK1 = 1U; /* disable INTP0 interrupt */

PIF1 = 0U; /* clear INTP0 interrupt flag */

}

void INTP1_ISR(void)

{

INTP1_Stop();

P5_bit.no2 = !P5_bit.no2;

forloop_delay();

forloop_delay();

INTP1_Start();

}

/* End user code. Do not edit comment generated here */

The INTP1_ISR() function which is in line 158 to line 165 is where the program jumps each time the INTP1 interrupt is generated (PIF1 flag is set to 1). Inside it, as you can see we called the INTP1_Stop() function line 160 to clear the PIF1 flag and mask the INTP1 interrupt. Then we toggle pin P52 line 161 which will turn ON or turn OFF LED9. The purpose of the delay in line 162 and line 163 is just for us to see that the normal operation of the MCU is really interrupted. These delays are just for demo. Good practice is to make the execution time of an ISR as short as possible so that if another interrupt occurs, the CPU will not miss it while already inside an ISR. There are ways in RL78 MCUs to manage multiple interrupts occurring at the same time but we will not discuss it in this tutorial.

In line 164, we clear the PIF1 flag and unmask the PMK1 flag by calling the INTP1_Start() function to enable the INTP1 interrupt again. The program will exit the INTP1_ISR() function and return to where it was interrupted.

So we’ve set the flags/bits related to the INTP1 interrupt and we’ve also created its ISR. However, we still need to do something so that our program will really jump to the ISR when INTP1 detects a falling edge. Actually, there’s an easy way to do this and that is to use the built-in Code Generator in e2 studio for RL78 MCUs. We can even easily generate codes that will configure the interrupt and create its ISR. However, I think we need to know how to do this manually so that later we can remove the unnecessary codes generated by the Code Generator and just use what’s needed.

I found online how to initialize RL78 interrupts using GCC for Renesas RL78 and found this thread in llvm-gcc-renesas.com forum: Interrupts Intialization (GCC for Renesas 4.9.2.201801-GNURL78 Linux Toolchain (ELF Format). Someone from the GNU Tools Team provided these instructions:

  1. The first step is we need to declare interrupt handlers by using attribute pragma. In the project explorer window, let’s open the header file r_cg_interrupt_handlers.h under the src folder and declare the interrupt handler.
Declaring Interrupt Handlers using attribute pragma.
Figure 13. Declaring Interrupt Handlers using attribute pragma.
  • We need to follow this follow this format:

    void interrupt_prototype(void) __attribute__ ((interrupt));

    Replace interrupt_prototype with a valid function name that you want. In our case, we chose INTP1_ISR.
  1. The 2nd step is we need to declare the interrupt vector table, initialize it with interrupt handlers, and place it in a section.

    const void *Vectors[] __attribute__ ((section (".vects"))) = { ..., interrupt_prototype, ... };
Declaring Interrupt Vector Table.
Figure 14. Declaring Interrupt Vector Table.
  • Luckily the IDE and the code generator did this step for us when we created the project. All we need to do is identify the vector address of the INTP1 ISR and replace its dummy name as shown in figure 13. Table 21-1 to table 21-4 of the RL78/G14 hardware user’s manual shows the vector table address of the interrupt sources. The CPU actually uses this table to check which address it will jump to execute the ISR. The address of INTP1 is 0xA.
  1. The final step is to place the reset vector and interrupt vector table at the right address starting from 0:

    MEMORY
    {
    VEC : ORIGIN = 0x0, LENGTH = 4
    IVEC : ORIGIN = 0x4, LENGTH = 188
    ...
    }
    SECTIONS
    {
    .vec 0x0: AT(0x0)
    {
    KEEP(*(.vec))
    } > VEC
    .vects 0x4: AT(0x4)
    {
    KEEP(*(.vects))
    } > IVEC
    ...
    }
Placing reset vector and interrupt vector table at the right address.
Figure 15. Placing reset vector and interrupt vector table at the right address.
  • But this was already configured when the project was created (see figure 14) so no need to do this step.

Finally we’re done initializing the interrupt. Now, let’s go back to the main function.

/***********************************************************************************************************************

* Function Name: main

* Description : This function implements main function.

* Arguments : None

* Return Value : None

***********************************************************************************************************************/

void main(void)

{

R_MAIN_UserInit();

/* Start user code. Do not edit comment generated here */

PM1 = 0;

P1 = 0;

PM5_bit.no2 = 0;

P5_bit.no2 = 0;

while (1U)

{

P1_bit.no0 = 1;

forloop_delay();

P1_bit.no1 = 1;

forloop_delay();

P1_bit.no2 = 1;

forloop_delay();

P1_bit.no3 = 1;

forloop_delay();

P1_bit.no4 = 1;

forloop_delay();

P1_bit.no5 = 1;

forloop_delay();

P1_bit.no6 = 1;

forloop_delay();

P1_bit.no7 = 1;

forloop_delay();

P1_bit.no7 = 0;

forloop_delay();

P1_bit.no6 = 0;

forloop_delay();

P1_bit.no5 = 0;

forloop_delay();

P1_bit.no4 = 0;

forloop_delay();

P1_bit.no3 = 0;

forloop_delay();

P1_bit.no2 = 0;

forloop_delay();

P1_bit.no1 = 0;

forloop_delay();

P1_bit.no0 = 0;

forloop_delay();

}

/* End user code. Do not edit comment generated here */

}

In line 60 to line 64, we set Port 1 pins and pin P52 as outputs. Then inside the infinite while loop line 66 to line 101, all we can see are code lines that will turn on the LEDs from LED1 to LED8 and turn them off from LED8 to LED1. No polling method used. So there’s no way that LED9 will be toggled unless the program jumps to the INTP1 ISR.

So let’s upload the code to the RL78/G14 FPB and see how it works. As you can see, if we don’t press the switch, the normal operation of the MCU is turn on LED1 to LED8 and then turn off LED8 to LED1. Try pressing the switch and you’ll see the normal operation being interrupted. LED9 will be toggled and after that the MCU will go back to its normal operation.

Testing INTP1 External Interrupt
Figure 16. Testing INTP1 External Interrupt.

The advantage of this compared to polling is that the task of toggling LED9 is almost immediately executed. Also, we don’t need to put other tasks on hold just like in blocking mode polling or wait for the MCU to finish the other task to execute an important task just like in non-blocking mode polling.

So that’s all for now! I hope you enjoyed this tutorial. If you have questions, you can leave it in the comments section below or you can message us. See you next time!

Make Bread with our CircuitBread Toaster!

Get the latest tools and tutorials, fresh from the toaster.

What are you looking for?