FB pixel

Renesas RA - 14. Resistance and Capacitance Meter using the Comparator Module (Part 1 - High-speed Comparator, Event Link Controller, and SEGGER RTT)

Published


Hi there! RA2A1 family, as I mentioned in the first tutorial, claims to have a rich analog peripheral, which includes:

  • One unit of the 16-bit SAR ADC which has up to 17 single-ended channels, eight of which can act as four differential channels.

Introduction into High-Speed Analog Comparator (ACMPHS)

The analog comparator included in the RA2A1 MCU is a normal comparator but more integrated with the other peripheral modules of the microcontroller. If you are not familiar with comparators at all, you can refer to this tutorial. Being brief, the comparator is a device with two inputs: the analog input itself and the reference input. If the input voltage is higher than the reference voltage, then the comparator’s output becomes high, otherwise, it’s low.

The main features of the ACMPHS are:

  • Switchable reference voltage input. It can either be applied to the dedicated MCU pins or run from the internal sources:
    • output from internal DAC12;
    • output from internal DAC8;
    • the internal reference voltage (which is 1.46V on average).
  • The output of the comparator can also be connected to the dedicated MCU pin. Also, changes in the output state can be monitored from the MCU register and cause the interrupt or ELC event (we will talk about the latter very soon).
  • The interrupt (and ELC event) can be generated on rising, falling, or both edges of the comparator output.
  • Digital filtering can be applied to the comparator’s inputs to prevent false changing in the output if the input is noisy.

If you want to read more about this module, please refer to the corresponding chapter of the user manual.

Introduction to the Event Link Controller (ELC)

Event Link Controller is a simple yet powerful means to interconnect the peripheral modules of the MCU with each other without CPU intervention. Almost every peripheral module can produce an interrupt (for example, timer overflow, data transmission/reception complete, pin state change, comparator output change, etc.) These interrupts pause the normal flow of the program and distract the MCU to process them. Sometimes we only need to run a peripheral module when a certain event happens (for example, start the ADC conversion when timer overflows or capture the timer value when the pin state is changed). In this case, the ELC module can be very helpful.

Instead of generating an interrupt, peripheral modules produce an event which can be connected to the certain peripheral module and run the specified action without the MCU. In the RA2A1 MCU, the ELC controller can accept up to 111 types of the peripheral events and run the actions at the following modules: GTP, ADC16, SD ADC24, DAC12, DAC8, Port 1/2, CTSU, also it can start the DTC transfer. You can also generate the event by the software at any time if needed and connect it with the listed above modules (I’m not actually sure why it is needed because the main idea of using the ELC is to connect the modules without CPU and the software event uses the CPU for sure, but maybe I just don’t understand something, you can correct me in the comments below the tutorial).

Again, for more information, please refer to the user’s manual.

One can wonder why we need this module in the current application. I will explain this in detail a bit later when we talk about the implementation of the capacitance measurement algorithm.

SEGGER Real Time Transfer (RTT)

Well, actually this is not an RA MCU module or feature. It’s not even a Renesas feature but it’s widely used in the example codes provided by Renesas, so I thought why not use it as well? Moreover, it’s very easy and convenient to apply.

As follows from its title, RTT has been developed by the SEGGER which also has designed the J-Link debugger which in its turn is located in the EK-RA2A1 board and used for debugging the target RA2A1 MCU.

On the SEGGER site page devoted to the RTT, they claim the following features:

  • Bi-directional communication with the target application.
  • Very high transfer speed without affecting real-time behavior.
  • Uses a debug channel for communication.
  • No additional hardware or pin on target is required.
  • Supported by any J-Link model.
  • Supported by ARM Cortex-A/R/M, RISC-V, and Renesas RX.
  • Complete implementation code providing functionality and freedom.

So to use the RTT, you don’t need to use any other hardware or communication module, all connection is done via the debug interface.

To be able to use it, you need to download and install the “J-Link Software and Documentation pack” from the SEGGER site. This pack is provided absolutely for free. At the moment of writing this tutorial, the latest version is 7.82c (Figure 1)

Figure 1 - Downloading the J-Link Software and Documentation pack
Figure 1 - Downloading the J-Link Software and Documentation pack

In the right part, select and download the installer corresponding to your operating system (I used the 64-bit Windows installer) and install it. The installation process doesn’t have any peculiarities, so I’ll skip it. The only thing you need to memorize is the path where you have installed this pack, we will need to copy some files from it to our project.

But for now, we will leave it and return to it when we consider the project creation.

Schematics Diagram and Algorithm of the Capacitance Measurement

As we have briefly reviewed the main modules which we will use in the current project, let’s consider the schematic diagram of our capacitance meter. Figure 2).

Figure 2 - Schematics diagram of the capacitance meter
Figure 2 - Schematics diagram of the capacitance meter

As you can see, the schematic diagram is quite simple. Apart from the EK-RA2A1 board itself, it contains just three resistors with known values and the capacitor Cx, whose value we will measure. Please pay attention that this time we will use the J3 connector of the EK-RA2A1 board for the first time. In Figure 2, I showed only its top part, as the rest of the pins are not connected.

The resistors R1 and R2 form a voltage divider, which is supplied from AVCC0 which represents the positive supply voltage of the analog part of the MCU. The AVCC0 value is about 3V but it actually doesn’t matter.

The voltage from the divider goes to pin AN001/P501 which also is the reference voltage input of the high-speed analog comparator. The values of R1 and R2 are not random and were selected with a certain purpose which I will talk about later. And now, let’s just calculate the reference voltage from this divider.

For now, we don’t need the real value in volts, this expression is quite enough.

Actually, we could get rid of the R1 and R2 resistors and use the internal reference voltage of 1.46V but this will make further calculations even more difficult.

Now let’s consider the most important thing - the measurement circuit which consists of the known resistor R3 of 10 kOhm and unknown capacitor Cx. This circuit is supplied from pin P502. In this case, it’s used as a regular digital output which can be either high or low. The voltage from the capacitor Cx goes to pin AN000/P500 which is merged with the analog input of the high-speed comparator.

The algorithm of the operation is the following:

  1. Pin P502 goes high, and the capacitor Cx is charged through resistor R3 for at least 1 second to the AVCC0 voltage.
  2. The application is waiting for a character from the RTT Viewer.
  3. After a character is received, the pin P502 goes low, and simultaneously the 32-bit GPT timer is reset to start the counting from 0.
  4. The capacitor discharges through the resistor R3, and its voltage drops by the known law , where Uc is the voltage on the capacitor Cx, and t is the time.
  5. When the voltage on the capacitor Cx reaches the reference voltage, the comparator’s output becomes low. At this moment the event is generated which causes the capture of the GPT timer value by means of the ELC module.
  6. The time t from the previous equation is measured by the timer, the Uc value at the capture time is also known and is , and the R3 value is also known, so we can now calculate the capacitance:

Let’s shorten both parts by AVCC0 and take the natural logarithm:

Now the value of 2.7 should be understood. It’s very close to the e which is 2.7172. In this case, we can consider that

, and thus:

And finally:

As you can see, the final equation is very simple and doesn’t depend on the supply voltage, only on the resistance R3 and the discharge time t.

One important note should be said here: In the EK-RA2A1 board, the digital supply voltage VCC and the analog supply voltages AVCC0 and AVCC1 are connected together and are all equal. In this case, we can consider that the voltage from the pin P502 which is configured as a digital output is also AVCC0. If VCC AVCC0, then we need to consider this difference or supply the R1-R2 divider from the VCC.

Project Creation and Configuration

Now we have everything to start the project. So let’s open e2 studio and create a regular project based on C language using the “Bare Metal - Minimal” template.

As usual, let’s first configure the required pins. So in the FSP configurator open the “Pins” tab, scroll down to the “Analog:ACMP” list, expand it, and select the “ACMHS0” line. Then change the “Operation Mode” from “Disabled” to “Custom”, “IVREF” from “None” to “P501”, and “IVCMP” from “None” to “P500” (Figure 3).

Figure 3 - Configuration of the ACMPHS pins
Figure 3 - Configuration of the ACMPHS pins

Now let’s expand the “P5” list and select the “P502” pin. We need to change its mode from “Disabled” to “Output Mode (Initial High)”, also it would be good to set the “Symbolic Name” as, for instance, “CHARGE_CONTROL” (Figure 4).

Figure 4 - Configuration of the P502 pin
Figure 4 - Configuration of the P502 pin

Pins P500 and P501 are already configured as inputs of the comparator. So we now can switch to the “Stacks” tab and add and configure the required stacks.

First, let’s add the new GPT timer (Figure 5).

Figure 5 - Adding the GPT stack
Figure 5 - Adding the GPT stack

We considered the main features of the General PWM Timer in tutorial 6 so here I will talk only about the features that I skipped that time. For now, let’s leave all the settings without changes, just make sure that the parameters highlighted with the green frames in Figure 6 correspond to yours.

Figure 6 - Initial configuration of the GPT timer
Figure 6 - Initial configuration of the GPT timer

Actually, the PCLKD is the only option for the clock source of the GPT. We must also use Channel 0 because this is the only channel that has a 32-bit resolution, all other channels are 16 bits. Timer mode is Periodic which means that it will count continuously. The period value is 0x100000000 counts which corresponds to 232, the maximum possible value.

We will also need to configure the timer action from the ELC module, but we first need to add the comparator and the ELC stack, so we’ll do it later.

Now let’s click the “New Stack >”, expand the “Analog” list, and choose “Comparator, High-Speed (r_acmphs)” (Figure 7).

Figure 7 - Adding the ACMPHS stack
Figure 7 - Adding the ACMPHS stack

Now let’s select the new block “g_comparator0 Comparator, High Speed (r_acmphs)”, and configure it according to Figure 8.

Figure 8 - Configuration of the ACMPHS stack
Figure 8 - Configuration of the ACMPHS stack

Let’s consider what the parameters of this module mean.

  • “Name” is the name of the module by which it will be called in the program. We can leave it as default “g_comparator0”.
  • “Channel” selects the hardware channel of the comparator. Actually, for the high-speed comparator only one channel (channel 0) is available, so you shouldn’t change this value.
  • “Trigger Edge Selector” sets the comparator output edge at which the interrupt and/or the ELC event will be generated. According to the algorithm of the capacitance measurement, we need to register the falling edge, so we select this option.
  • “Noise Filter” selects the PCLK divisor for the hardware digital debounce filter. Larger divisors provide a longer debounce and take longer for the output to update. As we expect the capacitor discharge curve to be smooth, we disable this filter.
  • “Maximum Status Retries (CMPMON)”. Actually, this value isn’t clear to me, and it’s not explained well in the documentation. This value must be a valid non-negative integer between 2 and 32-bit maximum value, so let’s leave it as default value 1024.
  • “Output Polarity” allows us to invert the output polarity, so if the input voltage is lower than the reference voltage, the output will be high, and vice versa. This value also affects the trigger edge. We will leave the output polarity as “Not Inverted” so as not to confuse ourselves.
  • “Pin Output” allows connecting the output of the comparator to the physical output pin of the MCU. As we don’t need this feature, we leave it as “Disabled”.
  • “Callback” sets the name of the callback function if the comparator interrupt is enabled. As we are going to use the ELC instead of interrupts, we leave this value as “NULL”.
  • “Comparator Interrupt Priority” sets the priority of the comparator interrupt (obviously). If this field is set as “Disabled”, the interrupts are also disabled.
  • “Analog Input Voltage Source (IVCMP)” selects the pin to which the input voltage of the comparator will be applied. The possible options are AN000, AN005, and AN016. As we are using pin P500 which is merged with the AN000 (see Figure 2), we select this option.
  • “Reference Voltage Input Source (IVREF)” selects the source of the reference voltage. It can be both the MCU pin (AN001, AN004, AN017) and the internal source (DAC80 DA, DAC120 DA, ANALOG0 VREF). As we decided to use the external voltage divider R1-R2 as the reference voltage source, connected to pin AN001/P501 (see Figure 2), we selected the option “AN001”.

For more information about the high-speed comparator stack please refer to this page.

Now let’s add the last stack. Click on the “New Stack”, expand the “System” list, and select the “Event Link Controller (r_elc)” line (Figure 9).

Figure 9 - Adding the ELC stack
Figure 9 - Adding the ELC stack

This stack doesn’t have many parameters (Figure 10).

Figure 10 - Configuration of the ELC stack
Figure 10 - Configuration of the ELC stack

As you can see here you can only change the name of the module. But we will leave it as the default.

Warning! If you want to connect several peripheral modules using ELC, you don’t need to add a new ELC stack for each connection, one is enough to control them all - this stack just adds general support of ELC to your program.

Now let’s select the “g_timer0 Timer, General PWM (r_gtp)” block and configure it according to Figure 11.

Figure 11 - Configuration of the GPT stack
Figure 11 - Configuration of the GPT stack

In tutorial 6, we skipped the explanation of the lists “Input” and “Interrupts”, and now the time has come to consider them.

In the “Input” list there are events that can be controlled by external sources, including the ELC module:

  • “Count Up Source” selects the external source that will increment the counter. If any count-up source is selected, the timer will count the external sources only. It will not count PCLKD cycles.
  • “Count Down Source” selects the external source that will decrement the counter. If any count-down source is selected, the timer will count the external sources only. It will not count PCLKD cycles.
  • “Start source” selects the external source that will start the timer.
  • “Stop source” selects the external source that will stop the timer.
  • “Clear source” selects the external source that will clear the timer.
  • “Capture A source” selects the external source that will trigger a capture A event.
  • “Capture B source” selects the external source that will trigger a capture B event.
  • “Noise Filter A Sampling Clock Select” selects the input filter for GTIOCA.
  • “Noise Filter B Sampling Clock Select” selects the input filter for GTIOCB.

If you expand all these “Source” lists, you can see that they are all the same as for the “Capture A source” which is expanded in Figure 11. You will notice that the list is quite large. And when you add a new stack to your configuration, its events are automatically added to this list. Among all possible events, we choose the “COMP HS0 INT (Comparator Interrupt 0)” as a source of the Capture A event. By doing this, we link the output of the comparator with the Capture A source of channel 0 of the GTP using the ELC module.

We selected the Capture A event because it’s easy to handle it inside the callback function about which we will talk when we consider the program code. When this event occurs, the content of the counter of the timer automatically and instantly is copied to the special capture/compare register from which it can be read without any rush while the timer keeps counting.

Also, we need to make some changes in the “Interrupts” list:

  • “Callback” sets the name of the callback function of the timer. As we are going to use the Capture A interrupt, we need to change this field from “NULL” to some name. I selected “timer0_callback” but you can choose anything.
  • “Overflow/Crest Interrupt Priority” selects the overflow interrupt priority. This is the crest interrupt for triangle-wave PWM. As we don’t need this interrupt, we leave it disabled.
  • “Capture A Interrupt Priority” selects the interrupt priority for capture A. And this is the exact interrupt that we need, so let’s set the priority as 1 (actually in this case we can set any priority because this is the only interrupt in our program, but in general you should set it according to the architecture of your program).
  • “Capture B Interrupt Priority” selects the interrupt priority for capture B. As we don’t need this interrupt, we leave it disabled.
  • “Underflow/Trough Interrupt Priority” selects the interrupt priority for the trough interrupt (triangle-wave PWM only). As we don’t need this interrupt, we leave it disabled.

These are all the configurations we need to do, but before proceeding, let’s check two more things.

Let’s first switch to the “Event Links” tab and make sure that the GTP (A) function is connected to the “COMP HS0 INT (Comparator Interrupt 0)” event (Figure 12).

Figure 12 - Even links configuration
Figure 12 - Even links configuration

Now let’s switch to the “Clocks” tab and check what the frequency is of the PCLKD signal which is used for clocking the GPT timer. In our case, it’s 48 MHz (Figure 13). Let’s remember this value, we will use it in further calculations of the capacitance.

Figure 13 - Clocks configuration
Figure 13 - Clocks configuration

Now that’s really it, so we can click the “Generate Project Content” button and switch to writing the program code.

Program Code of the Project

Before we proceed with creating our own code, we need to add the files for supporting the RTT. To do this, we first need to find the folder in which you installed the J-Link Software and Documentation Pack. In my case, it’s “c:\Program Files\SEGGER\JLink\” (Figure 14).

Figure 14 - Folder where the J-Link Software and Documentation Pack is installed
Figure 14 - Folder where the J-Link Software and Documentation Pack is installed

In this folder, you need to find and open the folder “Samples”, inside it find and open the folder “RTT” and see the archive “SEGGER_RTT_V782c.zip”. The numbers and the last letter can differ depending on the version of the pack you’ve installed. Unzip this archive to any folder where you can easily find it. Inside the archive, you will see the following folders and files (Figure 15).

Figure 15 - Content of the SEGGER_RTT_V782c.zip archive
Figure 15 - Content of the SEGGER_RTT_V782c.zip archive

Here we will need two folders - “Config” and “RTT”. Let’s open the first one and drag and drop the file “SEGGER_RTT_Conf.h” into e2 studio, your project folder, “src” subfolder (Figure 16).

Figure 16 - Drag-and-drop the “SEGGER_RTT_Conf.h” file to the “scr” folder
Figure 16 - Drag-and-drop the “SEGGER_RTT_Conf.h” file to the “scr” folder

When you release the mouse button to drop the file, you will see the following prompt window (Figure 17).

Figure 17 - Copying the “SEGGER_RTT_Conf.h” to the “scr” folder
Figure 17 - Copying the “SEGGER_RTT_Conf.h” to the “scr” folder

You need to select the “Copy files” option to copy this file to the project folder. Now return to the folder “Config”, move one level up and enter the folder “RTT”. There you will see four files: “SEGGER_RTT.h”, “SEGGER_RTT.c”, “SEGGER_RTT_ASM_ARMv7M.S”, and “SEGGER_RTT_printf.c”. You need to select all these files except for the “.S” one and also drag and drop them to the “src” folder (Figure 18).

Figure 18 - Drag-and-drop these three files to the “scr” folder
Figure 18 - Drag-and-drop these three files to the “scr” folder

You will see the same prompt as in Figure 17, and here you also need to do the same, as there.

Now your project folder should look like in Figure 19.

Figure 19 - Project folder
Figure 19 - Project folder

In previous versions of the J-Link Software and Documentation Pack all these four files were initially located in the same folder, so there were no problems. But now they are split, so when you attempt to build the project now you will see the error messages “../src/SEGGER_RTT.h:61:10: fatal error: ../Config/SEGGER_RTT_Conf.h: No such file or directory”. Let’s open the “SEGGER_RTT.h” file and change line 61 (in the current version) from “#include "../Config/SEGGER_RTT_Conf.h"" to “#include "SEGGER_RTT_Conf.h"" (Figure 20).

Figure 20 - Fixing the error in the “SEGGER_RTT_Conf.h” file
Figure 20 - Fixing the error in the “SEGGER_RTT_Conf.h” file

Now save this file and rebuild the project. This time there should not be any errors.

And now the time has come to open the “hal_entry.c” file and write the program code.

#include "hal_data.h"

#include "RTT/SEGGER_RTT.h"

FSP_CPP_HEADER

void R_BSP_WarmStart(bsp_warm_start_event_t event);

FSP_CPP_FOOTER

volatile uint32_t capacitance; //Calculated capacitance

volatile uint8_t measurement_done; //Indicates that the measurement is completed

static int key; //The character read from the RTT

void timer0_callback (timer_callback_args_t * p_args) //Timer0 callback

{

if (TIMER_EVENT_CAPTURE_A == p_args->event) //If event is "Capture A"

{

capacitance = (p_args->capture * 25) /12;//Then calculate the capacitance

measurement_done = 1; //And indicate that measurement is done

}

}

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

* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function

* is called by main() when no RTOS is used.

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

void hal_entry(void)

{

comparator_info_t info; //Variable to get the comparator info

char units[3] = "pF"; //Units of the capacitance to send to RTT

R_ELC_Open(&g_elc_ctrl, &g_elc_cfg); //Initialize the ELC module

R_ELC_Enable(&g_elc_ctrl); //Enable the ELC module

R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg); //Initialize Timer 0

R_GPT_Enable(&g_timer0_ctrl); //Enable external events at Timer 0

R_GPT_Start(&g_timer0_ctrl); //Start the counting of TImer 0

R_ACMPHS_Open(&g_comparator0_ctrl, &g_comparator0_cfg); //Initialize the comparator

R_ACMPHS_InfoGet(&g_comparator0_ctrl, &info); //Get the comparator info

R_BSP_SoftwareDelay(info.min_stabilization_wait_us, BSP_DELAY_UNITS_MICROSECONDS); //Wait while the comparator is stabilized

R_ACMPHS_OutputEnable(&g_comparator0_ctrl); //Enable the comparator's output

SEGGER_RTT_printf("Press any key to start measurement"); //Send the prompt text to RTT

while (1)

{

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(CHARGE_CONTROL, BSP_IO_LEVEL_HIGH); //Set the CHARGE_CONTROL pin high to charge the capacitor

R_BSP_PinAccessDisable(); //Disable access to the IO registers

R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //Wait for at least a second while capacitor is being charged

key = SEGGER_RTT_WaitKey(); //Wait for any key from the RTT

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(CHARGE_CONTROL, BSP_IO_LEVEL_LOW); //Set the CHARGE_CONTROL pin low to start the capacitor discharge

R_GPT_Reset(&g_timer0_ctrl); //Reset the Timer 0 counter

R_BSP_PinAccessDisable(); //Disable access to the IO registers

measurement_done = 0; //Reset the measurement_done variable

while (!measurement_done); //Wait while measurement is done

if (capacitance < 1000000) //If capacitance is less than 1 uF

units[0] = 'p'; //Then set the units pF

else //Otherwise

{

capacitance /= 1000; //Divide the capacitance by 1000

units[0] = 'n'; //And set the units nF

}

SEGGER_RTT_printf(0, "C = %u %s", capacitance, units); //Send the capacitance value to RTT

}

#if BSP_TZ_SECURE_BUILD

/* Enter non-secure code */

R_BSP_NonSecureEnter();

#endif 

}

As you can see, the program is quite short and relatively simple. Let’s consider it in detail.

In line 8 we declare the variable capacitance which represents the capacitance of the measured capacitor in pF.

In line 9 we declare the variable measurement_done which is the flag that indicates that the measurement of the capacitance is complete.

In line 10, there is the declaration of the last global variable key, into which we will copy the character received via the RTT.

In lines 12-19 there is a Timer 0 callback function “timer0_callback” - the name which we defined during the Timer 0 configuration (Figure 11). This function has a single parameter p_args of type timer_callback_args_t which consists of the callback event type and the captured value which we will need to calculate the capacitance.

In line 14 we check if the event that caused the interrupt is really the Capture A event by comparing the event field of the p_args parameter with the macro TIMER_EVENT_CAPTURE_A. Actually, as we didn’t enable any other interrupt sources for Timer 0 (Figure 11) we could skip this check but it’s always better to prevent unexpected behavior.

In line 16, we calculate the capacitance using a weird formula:

capacitance = (p_args->capture * 25) / 12;

The capture field of the p_args parameter consists of the captured value of the timer when the Capture A event occurred. This event in its turn is caused by the high-speed comparator. When the capacitor is discharged to

(about 37%) of AVCC0 voltage, the output of the comparator switches from high to low, which generates an event, which causes the Capture A event by means of the ELC module. You can see throughout the program that we don’t generate the Capture A event explicitly with our code, everything is done automatically with the ELC module.

So, p_args->capture value is the number of Timer 0 ticks since the beginning of the capacitor discharge. Now we need to convert it into seconds. Earlier I pointed your attention to the timer clock frequency which is 48 MHz (Figure 13), and now the time has come to use it.

So each timer tick takes

Also, the resistance R3 is 10kOhm (Figure 2). Let’s put these values into the capacitance formula:

If we calculate the capacitance in pF which is 10-12 of F, then the formula will be changed:

Now we can divide both numerator and denominator by 4, and finally, we will receive:

Which exactly corresponds with line 16. Actually, we could avoid dividing the fraction by 4 but in this case, we can overflow the 32-bit capacitance value if the capacitor is large enough

With this division, we can expand the measurement range 4 times, up to 170F. Also, you can change the measurement range by using different values of the resistor R3. If you put in a greater resistance, you can measure smaller capacitance, and vice versa. But let’s now return to the program.

So after line 16 we have the capacitance value in pF. In line 17 we assign 1 to the measurement_done variable to indicate that the measurement is completed. We will need this information further in the main loop of the program.

In lines 25-72 there is the main function of the program hal_entry. Traditionally it starts with the initialization part (lines 27-42).

In line 27 we declare the local variable info which is used to get the information about the high-speed comparator in line 38.

In line 28 we declare the array of char which will contain the units of the measurement of the capacitance. By default let’s initialize it with the string “pF”.

In lines 30-31, we initialize the ELC module. First, we need to open the ELC stack (as we do with all the stacks) (line 30), then we enable the ELC (line 31). After this all the links from the “Stacks” tab (Figure 12) will become active, so you don’t need to establish them manually. But if you want, you can set and break the links at any moment. If you want to read about this more, you can refer to the FSP documentation.

In lines 33-35, we initialize Timer 0. In line 33 we open the timer stack. Then in line 34, we enable the external trigger events by calling the R_GPT_Enable function. We didn’t use this function the previous time when we used the timer to generate the PWM in tutorial 6. And initially, I missed it here as well. And spent several hours trying to figure out why the comparator doesn’t trigger the timer capture event. So don’t forget to use this function in cases like this, it’s important! In line 35 we start the timer, after which it increments its counter continuously every PCLKD clock tick.

In lines 37-40 we initialize the high-speed comparator. Traditionally we first open the stack (line 37). Then we get the comparator information by invoking the function R_ACMPHS_InfoGet (line 38). This function has two parameters - the pointer to the comparator control variable, and the pointer to the comparator_info_t structure into which the information will be copied. This structure has the single field min_stabilization_wait_us which represents the minimum time required for the comparator’s parameters to stabilize. So before proceeding, we should perform the delay for this time, which we do in line 39. Finally, in line 40 we enable the comparator output by invoking the function R_ACMPHS_OutputEnable. After that, the comparator output becomes available. It can be polled with the application and can generate the events for the ELC module and interrupts, also if the physical output pin is enabled it will change its state as well. Again, for more information about the high-speed comparator please refer to the FSP documentation.

In line 42, we invoke the function SEGGER_RTT_printf which sends the message via the RTT like a normal printf function. It has two mandatory parameters: first is the number of the RTT channel. The default channel is 0, and it is configured automatically when the first write operation is implemented or explicitly by calling the SEGGER_RTT_Init() function (we will not talk about the RTT very deeply, only the things that we need to use it, if you want to read more information about it, please refer to the SEGGER RTT Wiki page). Returning to the SEGGER_RTT_printf function, its second parameter is the pointer to the format string, followed by the arguments for conversion, like in the printf function. So in line 42 we just write the text that tells the user what to do to start the conversion.

This is actually all about the configuration part. In lines 44-66, there is the main loop of the program. First, we set the CHARGE_CONTROL pin (P502) high (lines 46-48) to charge the capacitor Cx through the resistor R3 (Figure 2). This process takes some time, so we perform a 1-second delay (line 49) to fully charge the capacitor.

Now the capacitor keeps charging for an indefinite time while any key is pressed and sent via RTT. In line 51 we invoke the function SEGGER_RTT_WaitKey. This function waits until at least one character is available in the SEGGER RTT buffer. Once a character is available, it is read and this function returns. This function works only with the RTT channel 0, and it is blocking, so it will block the program execution until any character is received. We read the received character into the key variable but as we are waiting for any character, we don’t use it anywhere further.

Once we receive the character from the RTT, we set the CHARGE_CONTROL pin low (lines 52, 53, 55), and simultaneously reset the Timer 0 counter by invoking the function R_GPT_Reset (line 54). So Timer 0 counts the time from the beginning of the capacitor discharge till the moment when the voltage on it reaches

of AVCC0. After the discharge has started, we reset the flag measurement_done (line 56) and wait while it becomes 1 (line 57). This happens when the comparator’s output goes low and triggers the Capture A event of Timer 0, whose interrupt subroutine is processed in lines 12-19. Inside this subroutine, as I described before, the measurement_done flag is set in line 17.

In lines 58-64, we configure the capacitance value and its measurement units. Actually, this configuration is quite primitive and could be implemented in a better way but for demonstration purposes it’s fine. So if the capacitance is lower than 1 000 000 pF (line 58) we set the units as pF (line 59) and use the capacitance value as is. Otherwise, we divide the capacitance by 1000 (line 62) and set the units as nF (line 63).

Finally, we send the capacitance and its units via the RTT using the SEGGER_RTT_printf function (line 65), which we already met in line 42.

And that’s actually all about the program code. As I said initially, it’s quite simple and straightforward. Now let’s assemble the circuit according to the schematics diagram (Figure 2). As Cx let’s use some known capacitors to check if their values match the results of measurement. I selected seven capacitors: five ceramic ones (300 pF, 10 nF, 330 nF, 1.5 uF, and 10 uF) and two electrolytic ones (10 uF and 47 uF). I will connect them one by one to measure their values, starting with the capacitor of 300 pF.

When the circuit is assembled, connect your EK-RA2A1 board to the PC, build the project, start debugging, and run the project execution. Now you need to run the “RTT Viewer” application which you can find either in the “J-Link Software and Documentation Pack” folder (Figure 14) (the file name is “JLinkRTTViewer.exe”) or in the Start menu in the “Segger - J-Link 7.82c” folder (Figure 21).

Figure 21 - Running the RTT Viewer application
Figure 21 - Running the RTT Viewer application

When you run this application, you will first see the following window (Figure 22).

Figure 22 - Configuration of the RTT Viewer
Figure 22 - Configuration of the RTT Viewer

In the field “Connection to J-Link” we need to select the “USB” option. Then we need to specify the target device. You can type it manually or select from the list if you click on the three dots to the right of the selection field. Here we need to select/type “R7FA2A1AB” as this is our MCU. In the “Target Interface & Speed” field we leave the default values “SWD” and “4000 kHz”. In the “RTT Control Block” we also leave the default value “Auto Detection”. Then press the “OK” button.

If everything is correct, you should see the following window (Figure 23).

Figure 23 - Initial view of the RTT Viewer window
Figure 23 - Initial view of the RTT Viewer window

In the top part, there is a terminal where the incoming and outcoming messages will be shown. For now, you can see the incoming text “Press any key to start measurement” which we sent in line 42 of the code. In the bottom part, there is the events log. If everything is correct, you should finally see the “RTT Viewer connected” message (Figure 23).

Now let’s set the cursor in the white field between the Terminal and log fields and press any key. For instance, I will press the button “a”. The result of this action is the following (Figure 24).

Figure 24 - Result of the capacitance measurement
Figure 24 - Result of the capacitance measurement

You can press some keys several times to get the results several times and compare them. As you can see, initially the measured capacitance is lower than the claimed 300 pF but is within the range of ±10%. The result of the consequent measurements is very stable (Figure 25).

Figure 25 - Consequent measurements of the 300 pF ceramic capacitor
Figure 25 - Consequent measurements of the 300 pF ceramic capacitor

Let’s now replace the capacitor with the next one which is claimed to be 1nF. The result of the measurement is shown in Figure 26.

Figure 26 - Results of the measurement of the 1nF capacitor
Figure 26 - Results of the measurement of the 1nF capacitor

Here the real capacitance is even higher than the claimed one, and surprisingly the capacitor gains the capacitance in every measurement.

I will not show all the results with the seven capacitors with the images, I will gather them all in a table

Table 1 - Results of the capacitance measurements

Nominal value

First measurement

Fifth measurement

300 pF (ceramic)

287 pF

287 pF

10 nF (ceramic)

11.5 nF

11.7 nF

330 nF (ceramic)

370 nF

380 nF

1.5 uF (ceramic)

1.46 uF

1.44 uF

10 uF (ceramic)

3.18 uF

3.15 uF

10 uF (electrolytic)

10.7 uF

10.7 uF

47 uF (electrolytic)

49 uF

50 uF

As you can see, almost all capacitances match the nominal values within the ±20% range except for the 10 uF ceramic capacitor whose real capacitance turned out to be three(!!!) times smaller than the nominal one. I don’t know how to explain this fact. Either it has lost its capacitance during storage or it was not good initially.

Finally, I want you to show what’s going on on the low level by connecting the oscilloscope to the capacitor Cx and viewing its discharging curve (Figure 27).

Figure 27 - Oscillogram of the capacitor discharging curve
Figure 27 - Oscillogram of the capacitor discharging curve

Here is the measurement of the capacitor with a nominal 1.5 uF. As you can see, initially the capacitor is charged to voltage 3.04V (value Vpp in the legend). At the moment marked with the first vertical white line, it starts discharging, and discharges till the value of 3.04/2.7 = 1.13 V. This moment is marked with the second vertical white line. The time between these two events is 14.5 ms (𝞓T in the legend). If we divide this time by 10 kOhm we will get 1.45 uF, which corresponds to the value from Table 1.

And I think that's enough for this part. It is already quite meaty. In it, we were acquainted with the high-speed analog comparator, event link controller, and SEGGER RTT protocol.

The attentive reader may ask, why in the tutorial title it’s written about “Resistance and capacitance meter” and we have talked only about the capacitance measurement? Well, you want it - you get it :) I’ll leave the resistance meter for you as homework. To measure the resistance you should use some known capacitance and put the measured resistor as R3. Then you can calculate its value from the formula we used for the capacitance:

In the second part of this tutorial, we will learn how to use the low-power comparator and compare the results that both comparators give. Please continue on to the next tutorial to learn more about this!

Make Bread with our CircuitBread Toaster!

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

What are you looking for?