Example code for RX65N Envision dev board with GCC RX compiler

There seems to be little example code around for the RX65N Envision kit in general, and even less when using the GCC-RX compiler. I've seen a request or two here for a simple hello world type application. The example app that Renesas supplies for this board using their compiler is complicated and tied in to Emwin, so I've put together a much simpler one for anyone who's interested. It has touch screen and LCD drivers incorporated, all using direct register access, but neatly abstracted away into a simple API. The app reads touch screen coordinates and plots a box on the screen displaying the touch point screen coordinate inside it as text. The main function is effectively this...


lcd_filled_rectangle(0, 0, 480, 272, RED);

while (true)
    touched = touch_get_point(&x, &y);
    if (touched)
        if (x < 450 && y < 240)
            lcd_filled_rectangle(x, y, 30, 32, BLACK);
            itoa(x, text, 10);
            lcd_string(x, y, text, WHITE);
            itoa(y, text, 10);
            lcd_string(x, y + 16, text, WHITE);

Colours parameters to the lcd_* functions are in 24 bit (RGB888) format even though the display uses 16 bit (RGB565) format and are downsized in the functions. I know this is inefficient, but in demo code it's more intuitive to deal with RGB888 format colours.

I've created an e2studio project (version 7.2.0) and the GCC RX version used is All the project files and source are in in the EnvisionDemo1 folder in github here...


If anyone finds this useful I'll do some more examples projects. I also have it working using the cc-rx compiler if anyone is interested in that.

  • Hi John,

    Thanks for sharing this one. Yes, if you can share more application projects for RX65N, that would be very helpful. Thank you very much.

    RenesasRulz Forum Moderator


  • I've added another simple project which writes and reads from the on-chip data flash memory. This project includes a driver for writing a variable length data structure always to the start of the data flash memory (this can easily be changed in the driver code if required). The driver uses direct register access but has a simple API so that the main program is this...


    non_vol_save((uint8_t *)&test_data_1, sizeof(test_data_t));
    non_vol_load((uint8_t *)&test_data_2, sizeof(test_data_t));

    if (memcmp(&test_data_2, &test_data_1, sizeof(test_data_t)) == 0)
        success = true;
        success = false;

    This one is in the EnvisionDemo2 sub-folder in the same Github repo as above.

    Also I've added in the repo's README.md file instructions on how to create a project for the Envision Kit using GCC RX, including instructions on how to change the default stack size from the default 256 to something larger.

  • A couple of PWM examples.

    Number 10 uses the simple 8 bit timer in PWM mode to control P55 that's available on the Pmod connector (CN14). By putting a LED in series with a resistor between this connection and the ground line also available on the Pmod connector the brightness of the LED can be varied.

    Number 11 is also PWM, but a bit more complex. This demonstration shows how to use the Multi-Function Timer Pulse Unit 3 module (MTU3) for PWM generation to control the level of the Envision Kit's display's backlight.

    Although this module can do simple PWM output that's not available on the pin that the backlight enable line is on (P66). This pin is only available for MTU3 PWM using one PWM output line of complementary PWM or reset synchronized PWM. Reset synchronized PWM is simpler, so that is used. The MTU3 knows this output as MTIOC7D, and is one of multiple output lines controlled in reset synchronized PWM. The other lines in this example are unused.

    Reset synchronized PWM requires 2 timers to be used, which for this output pin to be controlled need to be timers 6 and 7.

    Buffering of the PWM timing registers is used to keep the timing and counting consistent, although the cycle period is fixed, so it's only the duty value that is changed.

    The code goes straight to I/O registers but a simple API is available to set the backlight percentage. The code in main to control the backlight looks like this...


    while (true)
        duty += 1U;
        if (duty > 100U)
            duty = 0U;




  • Number 12 - a DMA example which uses both peripheral triggered and software triggered DMA to send a bitmap to the Envision kit's display.

    This demonstration shows how to use the DMA to copy from memory to memory. The LCD is set up to show 16 bit RGB565 data. A bitmap is included that is in the same format as the display. DMA is used to copy the bitmap data to the display buffer.

    As the bitmap is less wide than the display, to copy the bitmap to the display buffer, non-contiguous DMA is required. The copy process is split into sections where one section is a horizontal line of the bitmap.

    The copy process is initiated by pressing the user button. DMA channel 0 is initialized to start its transfer on a peripheral interrupt. The user button is initialized to create IRQ13 when it is pressed and the DMA channel is configured to be triggered by this interrupt.

    When a line has been copied by DMA a DMA transfer complete interrupt is generated. This updates the destination address to the next line in the display buffer and the next DMA transfer is initiated by software. All subsequent transfers for the remaining bitmap lines are software initiated.

    The example goes directly to registers. When the application is  running press the user button. On each press the bitmap is copied to the display buffer (for 3 presses after which the display is full).

  • Number 13 - an independent watchdog example.

    This example demonstrates the independent watchdog in window mode. The watchdog cycles is set to approximately 4 seconds and the window in which the watchdog is allowed to be kicked is the middle 2 seconds of that period (after 1s, before 3s). Kicking the watchdog outside of that window, or not kicking it at all, causes a reset. After a reset the reset cause is determined and displayed.

    The watchdog is set up to start running automatically at reset. This requires configuring the watchdog in the Option Setting Memory, in register OFS0. The value of the register is hard coded in the e2studio generated file vects.c near the bottom of the file. This file has been edited manually to set this register value.

    The watchdog in this example is kicked by pressing the user button. The state of the watchdog cycle is shown on the LCD. Pressing the user button at the appropriate time according to the message displayed will allow the application to continue. Pressing the button too early or too late according to the messages will cause a reset and restart.

    The text displayed does not scroll. When it reached the bottom of the display - that's it.

  • Number 14 - Event Link Controller example

    This example demonstrates the event link controller. This module allows events to be communicated between hardware modules independent of interrupts or code.

    This simple example sets up an 8 bit timer, TMR0, and on it's match A value sends an event to the ELC that toggles port B6.

    Port B6 is output on pin 10 of the Envision Kit's Pmod connector. Connecting a scope to this pin will show a square wave output controlled by TMR0 via the ELC.

    The code goes straight to registers.
  • Here's the next one, number 15. This example demonstrates how to put the processor in low power mode, in this example the lowest of them all, deep software standby mode. In addition this example shows how to detect the reason for a reset cycle on wake and counts the number of reset cycles since power-on, saving the count value in one of the deep cycle backup registers.

    It can be found at the link below, along with a readme file which gives more information.


  • Number 16 - a driver for the Macronix MX25L32 QSPI flash chip fitted to the Envision Kit. 

    This example provides a driver for the Envision Kit's on-board flash memory chip which uses a QSPI interface. The driver is at 2 levels, the QSPI level which sets up, reads and writes to the RX65N's QSPI peripheral, and a higher level which prepares and sends commands to the MX25L QSPI flash chip on the Envision Kit board. The test code for this example erases, writes, reads back and compares 3 sections of the flash chip's memory, and looks like this...


        while (write_in_progress);

        qspi_MX25L_page_write(0x3000, 256, test_data_write);

        while (write_in_progress);

        qspi_MX25L_read(0x3000, 256, test_data_read);

    Only a small sub-set of the commands the MX25L supports is implemented in the MX25L layer. It will be easy to extend this for other commands.

    A feature of the MX25L chip to be noted is that writes can be a maximum of 256 bytes at a time and must not cross a 256 page boundary, although apart from that restriction, writes can start at any byte address. For example, writing 10 bytes starting at address 240 is allowed. Writing 20 bytes starting at address 240 is not allowed. The MX25L layer write command enforces this restriction.

    To keep things simple and understandable as an example this code only uses QSPI in single SPI mode and does not use DTC or DMA. However, those extra features can be added if required by building on this example.

    This module does things differently from the FIT QSPI module. The RX QSPI peripheral can control the QSPI chip select line (QSSL) in hardware. This example uses that feature, the FIT module does not, and requires the user to control the QSSL line from software using a GPIO. The reason for this is explained here, and it's to do with the style of the API.

    First, a brief explanation of QSPI as used to communicate with a QSPI flash chip as found on the Envision Kit. QSPI even in single SPI mode is slightly different from standard SPI. In standard SPI the chip select (CS) line really is that. If you only have one SPI slave on your SPI lines you can select CS to asserted and leave it at that. You can even tie it to asserted in hardware.

    In QSPI the QSSL line isn't used only for chip selecting purpose, it's used for signalling the end of transmission as well. A typical QSPI operation is...

    - Assert QSSL
    - Send command details
    - Send data, keep on sending unspecified amount of data until...
    - De-assert QSSL which signals that the operation has ended

    This is shown below with a read command. QSSL# is asserted, a 3 for the read command is sent, then 3 bytes of the address to start reading from (0 48 0), then the data is clocked out until the QSSL# line is de-asserted letting the chip know that the operation has completed.

    Now for how this example's API and the FIT QSPI module's API vary. The example operation described here is to write a page of data. This requires a 4 byte command and 256 bytes of data, 260 bytes in all. The FIT module allows you to write arbitrary sized chunks of that data across multiple calls, for example write 10, 128, 50, 72 bytes to reach the total of 260. This example's API requires one call, giving a pointer to the command, it's length, a pointer to the data, and it's length.

    Given the order of operations above, whether the API is used as an enforced single call or optional multiple calls the QSSL line must be asserted at the beginning and de-asserted at the end. With multiple calls of arbitrary length data sections (as the FIT module allows) - that's a problem, and it's to do with sequences, which control how the data is transferred.

    The RX QSPI module uses the concept of sequences. A sequence defines the size of data to be transferred (byte/short/long) and how many bytes/shorts/longs in the sequence. A complete QSPI operation comprises 1 or more (up to 4) sequences. If a large amount of data is being transferred then the operation should transfer most of it as longs for efficiency, so an operation will comprise a sequence of longs followed by an optional sequence of bytes if there are any bytes left over.

    These sequences must be defined before the QSPI enable flag in the RX QSPI peripheral is asserted which allows transmission to start. Once the QSPI enable flag has been asserted the manual says don't touch these sequences until the operation is finished and the QSPI enable flag is de-asserted. So here's the problem. If you have an API like the FIT module which allows arbitrary length chunks to be sent you cannot configure your sequences once at the start - you have to reconfigure your sequences after every API call to transfer the next chunk of data. That requires the QSPI peripheral to be de-asserted - and that de-asserts QSSL signalling the chip that the transfer has completed, even if more data is to follow. So the only options are give details of all the data in one single API call (as this example does), or require the user to operate the QSSL line via software using a GPIO (as the FIT module does).

    The driver here uses 1 to 3 sequences to implement a complete operation. The first sequence is always present and sends the command in byte chunks. If there is any data following (there may not be, some operations are command only) a following sequence is used. If the amount of data is >= 32 bytes a sequence of longs is used to transfer the data. If there is any data remaining (< 32 bytes) a final sequence of bytes is used. If the data is present but < 32 bytes in total the sequence of longs in the middle is not used.

  • Number 17 - debug i/o redirect.

    This example allows you to redirect normal stdio console input/output to either the Renesas Debug Virtual Console in e2studio or the serial port available on the Pmod connector on the Envision Kit (SCI9). If the serial port on the CN14 Pmod connector is used a small TTL to RS-232 level converter board is required, available on EBay for a few $. A level converter board can be powered from the power lines also available on the Pmod connector.

    All input/output routines available in stdio.h that eventually use puchar() & getchar() can have their i/o redirected using this example code. These include printf(), puts(), scanf(), gets() etc., but not the functions that take a FILE * parameter (i.e. fprintf()). You can write code like this...

        printf("Hello, world\r\n");
        printf("Enter a number...");

        scanf("%d", &i);

        printf("The number you entered was: %d\r\n", i);

    To choose the destination of the redirected input/output modify one of the #defines at the top of debug_io.h...


    This example works by replacing the standard puchar() and getchar() routines in the standard library with versions in debug_io.c. To facilitate this these linker flags are required...


    These linker flags need adding in Properties|C/C++ Build|Settings|Linker. Append them to the list of existing flags in the box labeled 'Expert settings: Command line pattern'.

    Some cautions: all the stdio routines used in this example are blocking, not thread safe, not suitable for use in interrupt handlers, and scanf() and gets() are well known as a security risk from buffer overflow. Use only in test or debug code.

    The declarations and code to write to the Renesas Debug Virtual Console is copied from lowlvl.c, part of the Renesas r_bsp package for RX65N processor.

  • Thanks for the #17 debug console solution. As you noted, redefining putchar() and getchar() subverts their normal operation.

    To preserve the normal putchar() and getchar() operation, one could define the low-level write() and read() functions so that they check for stdin, stdout, and stderr, and then route the those streams data to an appropriately defined user function.

    In the Renesas FIT BSPs, the low-level console I/O functions are routed to the locally defined functions, charput() and charget(). The FIT BSP gives the user a configuration option to use the built-in versions of these functions or to define their own. This works well if one is using CC-RX and the project generator wizard , as the low-level stream I/O routing is provided in the file, "lowsrc.c".

    In e2studio V7.3, a GCC project created with the project generator wizard and Smart Configurator does include a GCC compatible FIT-based BSP, which includes the "lowlvl.c" file that defines the charput() and charget functions. But, unfortunately (at this time), doesn't include lowsrc.c. Here is a simplified version, that is sufficient for standard console I/O only. To add file stream I/O, these functions would need to be expanded to check for other file handles and perform the usual routing to locally defined I/O drivers.

    Note that this isn't using the reentrant version functions that are available, so wouldn't be thread safe. You can find the stubs for the Newlib reentrant versions in the GCC distribution folder, "..\GCC for Renesas RX\rx-elf\rx-elf\bin\newlib\libc\syscalls\"


    #include <stdint.h>
    #include <stdio.h>

    /* file number */
    #define STDIN  0                    /* Standard input (console)        */
    #define STDOUT 1                    /* Standard output (console)       */
    #define STDERR 2                    /* Standard error output (console) */
    /* Special character code */
    #define CR 0x0d                     /* Carriage return */
    #define LF 0x0a                     /* Line feed       */
    /* Output one character to standard output */
    extern void charput(unsigned char);
    /* Input one character from standard input */
    extern unsigned char charget(void);
    int write (int fd, const void *buf, size_t cnt)
        char *bufptr = (char *)buf;  /* The address of source buffer */
        int  count = cnt;            /* The number of characters to write    */
        unsigned char    c;          /* An output character            */
        if( fd == STDIN )
            return -1;            /* Standard Input     */
        else if((fd == STDOUT) || (fd == STDERR)) /* Standard Error/output   */
        /* WAIT_LOOP */
            for(int i = count; i > 0; --i )
                c = *bufptr++;
            return count;        /*Return the number of written characters */
            return -1;                  /* Incorrect file number          */
    int read(int fd, void *buf, size_t count )
        char *bufptr = (char *)buf;  /* The address of destination buffer */
        if( fd == STDIN )
            /* WAIT_LOOP */
            for(int i = count; i > 0; i--)
                *bufptr = charget();
                if( *bufptr == CR )
                {              /* Replace the new line character */
                   *bufptr = LF;
            return count;
            return -1; /* unknown file */


  • This example demonstrates the DTC in conjunction with the serial port SCI9 available on the Envision Kit's Pmod connector (CN14).

    The serial port is set up in asynchronous mode (19200, 8, N, 1) using no flow control or error handling. Interrupts are enabled for transmit, transmit end and receive. Two DTC channels are created linked to interrupts 102 and 103 for Rx and Tx for SCI9. These DTC channels intercept the interrupt requests and perform data transfer autonomously, only reaching the interrupt handlers' code after the configured number of bytes have been transferred. When a Tx interrupt is serviced the transmit end interrupt is enabled to detect when transmission of the last byte has been completed.

    Although only 2 DTC channels are used space is allocated for an array of 256 DTC vectors and slots 102 and 103 contain pointers to the configuration structures for these 2 channels. In this example the DTC vector array is located at the top of the RX65N's extended RAM at address 0x0085FC00. This is after both the display buffers used in EnvisionDemo3.

    To position the vector array at this location the generated linker script .ld file needs amending. In the MEMORY list at the top where RAM, ROMS and OFS regions are defined a new region needs adding:

        ERAM : ORIGIN = 0x800000, LENGTH = 0x60000

    In the SECTIONS list a new section needs adding like this:

        .dtc_vectors 0x0085FC00: AT(0x0085FC00)
        } > ERAM

    To define the array in C source to be located at this address a GCC __attribute needs to be used when declaring the array like this:

        static volatile uint32_t __attribute__((section(".dtc_vectors"))) dtc_vectors[256];

    Looking in the .map file after linking shows that this array is at the required address:

        .dtc_vectors 0x0085fc00 0x400 ./src/serial_dtc.o

    When the example is run and connected to a serial terminal the code does an endless loop of writing and reading fixed sized packets of data to/from the serial port.

  • Hi Gerald
    I might try your suggestion as an alternative. The rest of read and write that are not stdin, stdout or stderr could link to the FatFS example I did earlier so that the standard C file handling library is available, although other functions would need diverting as well, like seek. I guess read and write are part of the standard library somewhere in its depths, so the GCC flags would have to be changed to -fno-builtin-read and -fno-builtin-write.
  • Hi Gerald
    I might try your suggestion as an alternative. The rest of read and write that are not stdin, stdout or stderr could link to the FatFS example I did earlier so that the standard C file handling library is available, although other functions would need diverting as well, like seek. I guess read and write are part of the standard library somewhere in its depths, so the GCC flags would have to be changed to -fno-builtin-read and -fno-builtin-write.
  • John,

    One thing I discovered when implementing those read and write functions is that the console I/O can use up a lot of stack. I ended up just dropping in 2KB to get printf to work with some minimal formatting, but not sure yet what an optimal size is.

    I haven't checked, but I think maybe those low level stream I/O functions may be declared as 'weak' in the GCC library, because the compiler doesn't complain when I add the user defined versions. As you say it would be nice to implement the full set of low level calls for full file support. This would permit using C's stream I/O high level calls for everything.

    I have done this under CC-RX a few years ago, linking in the FatFS file system and a SPI MMC memory driver, so maybe it's time to port it over to GCC.