Creating variant for using UNO R4 WiFi in USB bridge bypass mode

I'm not aware of one. If someone makes one please do share it on the forum. I have a personal interest in such things as I try to keep track of all the available Arduino boards platforms.

I've tried my best to follow the thread, but I'm still having trouble understanding why this bypassing of the ESP32-S3 is necessary in this application of MIDI over serial. I understand why you might do it for something like Delta_G's USB host project, but not for this one.

@ptillisch I had it all compiling at one point on that other thread but I abandoned it and I will have to go back and remember what all I did.

  1. Is this the right repo to fork?

  2. How do I manually install a core in 2.0?

  1. Yes

  2. https://support.arduino.cc/hc/en-us/articles/360021232160-How-to-install-and-use-a-custom-core-version-in-the-IDE

My suggestion is that, rather than making your platform as a fork of arduino/ArduinoCore-renesas, you instead use that platform as a dependency of your own platform, referencing all the resources from the "Arduino UNO R4 Boards" platform using the feature documented here:

https://arduino.github.io/arduino-cli/latest/platform-specification/#referencing-another-core-variant-or-tool

This means your platform can consist only of the specific files that contain your own customizations (e.g. boards.txt and the core variant) instead of having to maintain a copy of every one of the many files of arduino/ArduinoCore-renesas in your own project.

A good example of a platform like that is David Mellis's foundational "attiny" platform:

That platform leverages the resources of the "Arduino AVR Boards" platform using the referencing system.

1 Like

Every now and then you pull out a little bitty plug and get a huge flood of new knowledge. This post was once of those. I ended up scrolling back up and reading most of that whole document. I never really knew how all the boards.txt and platforms.txt stuff worked. I just took that stuff for granted.

Thanks!

I think I'll do both. I can't figure out how to turn off auto updates on my IDE and I'm afraid of an update messing something up. So I'll let the IDE be the IDE, and I'll keep a clean clone of the core-master branch in my hardware folder. I'll use that to link my board.txt against as you say in a separate folder and put that in a separate repo.

My plan is pretty simple. Now that we think we know how to switch the line over, I'm just going to copy over what's going on in the Minima. It should work theoretically to just cut the ESP-32 out of the equation.

1 Like

Does the MD / P201 (26) pin impact such a variant?
On the Minima it is tied to 5V thru R6 and you short BOOT to GND in order to program the bootloader.

On the WiFi it is connected thru the Logic Level Translator to ESP_IO9.
They updated the WiFi schematic a few weeks ago to show R8 removed (the Interactive Viewer is still incorrect), but MD is also connected to BOOT on the WiFi header for some reason.

Probably. I'm studying the bootloader code right now.

Guess who I found there!

Our old MAGIC friend:

#if defined(RENESAS_CORTEX_M23)
#define BOOT_DOUBLE_TAP_DATA              (*((volatile uint32_t *)0x20007FF0))
#else
#define BOOT_DOUBLE_TAP_DATA              (*((volatile uint32_t *) &R_SYSTEM->VBTBKR[0]))
#endif
#define DOUBLE_TAP_MAGIC                  0x07738135

And then in the bootloader it skips a section if that value isn't right:

int main(void)
{
  board_init();

  // set magic for double tap
  if (BOOT_DOUBLE_TAP_DATA == DOUBLE_TAP_MAGIC) {
    set_double_tap_data(0);
    goto bootloader;
  }

  if (!R_SYSTEM->RSTSR0_b.PORF) {
	set_double_tap_data(DOUBLE_TAP_MAGIC);
  }
#if !defined(RENESAS_CORTEX_M23) /* no double tap support in Muxto */
#ifdef TURN_OFF_CHARGER_LED
  i2c_begin();
  i2c_write(0x8, 0x9C, (1 << 7));
  i2c_write(0x8, 0x9E, (1 << 5));
#endif
  while (board_millis() < 500) {
  }
#endif
	set_double_tap_data(0);

  int app_valid = (((*(uint32_t *) SKETCH_FLASH_OFFSET) & 0xFF000000) == 0x20000000);

  if (app_valid) {
      boot5(SKETCH_FLASH_OFFSET);
  }

bootloader:

  R_IOPORT_Open(&port_ctrl, &pin_cfg);

  R_GPT_Open(&pwm_ctrl, &pwm_cfg);
  R_GPT_PeriodSet(&pwm_ctrl, PERIOD);
  R_GPT_DutyCycleSet(&pwm_ctrl, 0, pwm_channel);
  R_GPT_Start(&pwm_ctrl);

#if BSP_FEATURE_FLASH_HP_VERSION
  R_FLASH_HP_Open(g_flash.p_ctrl, g_flash.p_cfg);
  R_FLASH_HP_Reset(g_flash.p_ctrl);
  R_FLASH_HP_StartUpAreaSelect(g_flash.p_ctrl, FLASH_STARTUP_AREA_BLOCK0, true);
#endif

#if BSP_FEATURE_FLASH_LP_VERSION
  R_FLASH_LP_Open(g_flash.p_ctrl, g_flash.p_cfg);
  R_FLASH_LP_Reset(g_flash.p_ctrl);
#endif

  run_bootloader();

  return 0;
}

This might explain why I saw different results than @Grumpy_Mike while looking at the pin 21 thing. I was using the function out of variant.cpp that was modifying that value and causing the bootloader to do something extra.

I still want to study and learn what exactly. There's more to this. I'm betting this data handles some configuration stuff for us. That's what the user's manual said was normally stored there.

OK, I get the reset thing now.

I'll try to explain and if I'm off then someone please correct me. But this is my reading of the code.

If you power off the board and then power on, the data from VBTBKR0 : VBTBKR4 will be lost. So it will not equal DOUBLE_TAP_MAGIC and that causes you to not skip anything.

The next line checks RSTSR0_b.PORF. This is our power on reset flag. It sets VBTBKR up to hold double tap magic and then times out for 500ms. If we reset again during that 500ms then next time into the bootloader BOOT_DOUBLE_TAP_DATA will equal DOUBLE_TAP MAGIC and we will skip this part and go to the next part. This sets up some stuff and calls run_bootloader which sits and waits blinking the lights and watching tud_task(). There's a handler for once the flash is done.

If we don't double tap during the 500ms then BOOT_DOUBLE_TAP_DATA gets set back to 0 and we go into boot5 where our sketch gets called.

That's how the double tap to run the bootloader works. Kind of neat.

3 Likes

you can create a variant in the hardware folder
you will need this patch
https://github.com/JAndrassy/my_boards/blob/master/renesas_uno/platform.txt

2 Likes

Thanks @ptillisch and others:

Sorry I missed noticing that you did split this out from the other thread. Which makes total sense!

I thought I would bring over some of my other thoughts from that thread. Some of which I think have been covered:

That is, before that the ESP32 probably has conversation with host, and suddenly the RX/TX pins going to the USB connector are changed. How is that handled. Is there some form of USB reset issued?...


Also if you currently compile for WIFI, it has no USB support built in? how does that work. Serial object is still trying to talk to ESP32 through their UARTs

Now if you hack platform.txt to remove the NO_USB setting, then the UART to SerialX objects are probably messed up. Where Serial1, probably tries to talk to the ESP32, and Serial2 probably talks to pins 0 and 1... Could be wrong.

Where is the USB Descriptor defined?

What about bootloaders? If solder jumper in place, do you need new bootloader? Probably not the same one as MINIMA, if for no other reason, the LEDS (pin 13) are on different IO pins on the two boards. P111 for MINIMA and P102 on WIFI.

What does double click on the reset button do? I am assuming that the ESP32 sees it, and probably resets the main processor...


EDIT: I meant to then say, but creating a new variant should solve many of the above things :smiley:

Here is a thread I started awhile ago, that @ptillisch gave me a lot of good instructions on how to do this:

How to build and change the latest core sources? - UNO R4 / UNO R4 WiFi - Arduino Forum

I still wonder how the switch over from the ESP32 is controlling the USB and suddenly the other processor has it, is handled. Maybe the processor detects USB is available and does a USB reset? at which point the host will query it for it's descriptors and the like?

Will be interesting to try! Wonder if I should have a 2nd WIFI board...

1 Like

Here in the bossa.c file for the bossa loader for the WiFi:

void restore_usb_switch() {
  if (R_SYSTEM->VBTBKR[1] == 40) {
    R_IOPORT_PinCfg(&port_ctrl, BSP_IO_PORT_04_PIN_08, IOPORT_CFG_PORT_DIRECTION_OUTPUT);
    R_IOPORT_PinWrite(&port_ctrl, BSP_IO_PORT_04_PIN_08, BSP_IO_LEVEL_HIGH);
    R_BSP_SoftwareDelay((uint32_t) 2, BSP_DELAY_UNITS_MILLISECONDS);
    R_IOPORT_PinWrite(&port_ctrl, BSP_IO_PORT_04_PIN_08, BSP_IO_LEVEL_LOW);
    R_IOPORT_PinCfg(&port_ctrl, BSP_IO_PORT_04_PIN_08, IOPORT_CFG_PORT_DIRECTION_INPUT);
    R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_PRC1_UNLOCK;
    R_SYSTEM->VBTBKR[1] = 0;
    R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_LOCK;
  }
}

There's the answer to what this mysterious 40 was doing in VBTBKR[1].

I've spent some hours now looking at the differences between the Minima and the WiFi, both in code and bootloader and assembly drawings. There are some key differences.

That's solved here mostly. The double tap sends the RA4M1 into a usb device mode listening for an upload. Without the double tap you go into a different section of the bootloader. It looks like it all happens on the RA4M1.

I need to look at the code on the ESP32. That's the one piece of the equation I haven't studied at all yet. I think it just acts as a serial bridge and runs the bossa loader which is another piece I need to look at.

They don't use the same bootloader. The WiFi has bossa.c included and the Minima has some extra usb callbacks defined. They use different programmers. The WiFi uses bossa (which I don't know anything about) and the Minima uses DFU (which I know a little bit). It seems to me like if you soldered the pads on the back you'd either need a different programmer or you'd need to use the Minima bootloader.

There's a pin on the Minima for burning the bootloader and it's under the control of the ESP on the WiFi but there's a test pint. It's pin 201 at the chip. On the Minima it's pulled up. On the WiFi it's not pulled at all. I need to learn what that's about.

The hope is to keep the WiFi bootloader and upload process intact if I can.

In Arduino.h

#ifndef NO_USB
#define Serial  SerialUSB
#define Serial1 _UART1_
#define Serial2 _UART2_
#define Serial3 _UART3_
#define Serial4 _UART4_
#define Serial5 _UART5_
#else
#define Serial _UART1_
#define Serial1 _UART2_
#define Serial2 _UART3_
#define Serial3 _UART4_
#define Serial4 _UART5_
#endif

If I undef NO_USB then I should have Serial using _USBSerial and going out on 914-915 to the USB connector. Now Serial1 is UART1 and is talking to the ESP and Serial2 is UART2 going to pins 0 and 1. This needs to be tested, but I think that's what's going to happen.

If that's the case, then I still have hope. The bootloader doesn't know about any of that. So as long as I don't solder the pins permanently and leave a way to hook the ESP back up, I should be able to double tap and open up the bootloader and still load code.

I don't know about this bossa. I need to learn about that. I may be able to use that directly over the connection once I have the RA4M1 on the port. I also need to learn what's up with pin 201.

The reset thing is an important point I hadn't really got to yet. There are methods in tinyusb to reset the port. Another possibility would be to modify the bootloader to raise pin 21 before initializing the port. Application code could make it low again to allow for new code uploads or I could put that into the double-tap section. I'd rather not have to mess with the bootloader if I can avoid it.

I found the rest of the bootloader story.

So if you double-tap you get to run_bootloader().

In main.c of the bootloader code it's:

__WEAK void run_bootloader() {
  // init device stack on configured roothub port
  tud_init(BOARD_TUD_RHPORT);
  if (board_init_after_tusb) {
    board_init_after_tusb();
  }

  while (1)
  {
    tud_task(); // tinyusb device task
    led_blinking_task();
  }
} 

That's for the Minima. But notice it's weak so it can be overridden.

If we have the Wifi then BOSSA_LOADER is #defined as 1 and we get a couple of things:

static const ioport_pin_cfg_t extra_pin_cfg[] = {
#ifdef BOSSA_LOADER
  { .pin = BSP_IO_PORT_01_PIN_09, .pin_cfg = (IOPORT_CFG_PERIPHERAL_PIN  | IOPORT_CFG_PULLUP_ENABLE | IOPORT_PERIPHERAL_SCI1_3_5_7_9) },
  { .pin = BSP_IO_PORT_01_PIN_10, .pin_cfg = (IOPORT_CFG_PERIPHERAL_PIN  | IOPORT_CFG_PULLUP_ENABLE | IOPORT_PERIPHERAL_SCI1_3_5_7_9) },
#endif
  { .pin = LED_FADE_GPIO, .pin_cfg = IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_GPT1 }
};

A couple of extra pins. 109 and 110 are the tx and rx going to the ESP board.

But more importantly we also get bossa.c which has:

void run_bootloader() {
  restore_usb_switch();
  R_SCI_UART_BaudCalculate(230400, true, 5000, &uart_baud);
  R_SCI_UART_Open(&g_uart_ctrl, &g_uart_cfg);
  R_SCI_UART_BaudSet(&g_uart_ctrl, (void *) &uart_baud);

  while (1) {
    bossa_task();
    led_blinking_task();
  }
}

Another version of run_bootloader. This is for the bossa loader. And also where we call that function with the magic 40 in it.

The UNO R4 USB Bridge on the ESP32 is programmed as a .ino file. How nice.

Right at the beginning of void setup() we write boot and reset high.

pinMode(GPIO_BOOT, OUTPUT);
  pinMode(GPIO_RST, OUTPUT);
  digitalWrite(GPIO_BOOT, HIGH);
  digitalWrite(GPIO_RST, HIGH);

That's pins 9 and 4. Pin 9 is our signal through the level shifter to pin 201. Answers that question. Pin 4 goes through the level shifter to the reset pin on the RA4M1.

loop function is seriously just a serial bridge and nothing more:

void loop() {
/* -------------------------------------------------------------------------- */  

  if (SERIAL_USER.baudRate() != _baud) {
    _baud = SERIAL_USER.baudRate();
  }

  int i = 0;

  if (SERIAL_USER.available()) {
    i = min((unsigned int)SERIAL_USER.available(), sizeof(buf));
    SERIAL_USER.readBytes(buf, i);
  }

  if (i > 0) {
    SERIAL_USER_INTERNAL.write(buf, i);
  }

  i = 0;
  if (SERIAL_USER_INTERNAL.available()) {
    i = min((unsigned int)SERIAL_USER_INTERNAL.available(), sizeof(buf));
    SERIAL_USER_INTERNAL.readBytes(buf, i);
  }
  if (i > 0) {
    SERIAL_USER.write(buf, i);
  }

  yield();
}

Here's the two types of write to the RA4. According to the cheat sheet, and here it is in code, we use a touch 1200 to trigger an upload and a touch 2400 to trigger a bootloader burn. But in reality all it does is set the boot pin and then reset the RA4M1.

There's nothing in there about bossa. So I assume all of that comes from the PC side.

MD / P201 on the WiFi is connected thru the Logic Level Translator to ESP_IO9 on the ESP32.

May not be useful to the experienced crowd, but I found this bootloader tutorial helpful...
https://www.circuitbread.com/tutorials/renesas-ra-13-introduction-to-mcuboot-using-the-renesas-ra-family-part-4

Edited to add link

Here's my diagnosis so far.

  1. We cannot simply cut the ESP32 out of the deal and run a Minima bootloader with different pins unless we're willing to have to solder wires onto test points to ever load another bootloader. The fact that pin 201 doesn't come out to the header makes sure of that.

  2. I think it would be possible to put the DFU bootloader on the RA4M1 on the WiFi and load to it using native usb. Either the pads on the back would need to be soldered or the bootloader would need to have a line before setting up the port to switch pin 408 (D21). We would need to leave some way to leave the port set to the ESP is we want to be able to burn another bootloader. Or we would need to solder to test points on the board to drop pin 201.

  3. I think we can bypass the ESP32 with USBSerial from the RA4M1 while leaving the original bootloader and upload process in place by utilizing the double tap magic. At least that's what I'm going to try first. It depends on the serial code I think. But I think given the results I saw on the other two threads I'm just going to try it out before I dig too much further. I think hardware wise it's going to work.

Let me correct that. Pin 201 IS at the header. I just checked with my DMM. It's just not labelled.

So I don't see any reason why we couldn't just load the Minima bootloader up with the WiFi pins. This gives us a way to touch 201 without doing anything crazy. We should be able to just treat it as a Minima. We might need to add a pullup to 201, but it's at the header.

Actually, given the way the bootloader is put together, I think we can simply remove the BOSSAC_LOADER = 1 define in the makefile and -DNO_USB. If my reading is right, that will leave all our pin assignments for leds alone, but build everything else for the Minima bootloader. This would take a little more investigation, but I really think it might be that simple.

Is this what you mean? I just found this in pins_arduino.h

#define USB_VID           (0x2341)
#define USB_PID           (0x006D)
#define USB_NAME          "UNO R4 WiFi"

#define VUSB_LDO_ENABLE     1

Although I think there are other places to change it. I think it can also be set in boards. txt

variants/MINIMA/pins_arduino.h

#define SERIAL_HOWMANY		1
#define UART1_TX_PIN        1
#define UART1_RX_PIN        0

variant.cpp

extern "C" const PinMuxCfg_t g_pin_cfg[] = { 
  { BSP_IO_PORT_03_PIN_01,    P301   }, /* (0) D0  -------------------------  DIGITAL  */
  { BSP_IO_PORT_03_PIN_02,    P302   }, /* (1) D1  */
  { BSP_IO_PORT_01_PIN_05,    P105   }, /* (2) D2  */
  { BSP_IO_PORT_01_PIN_04,    P104   }, /* (3) D3~ */
  { BSP_IO_PORT_01_PIN_03,    P103   }, /* (4) D4  */
  { BSP_IO_PORT_01_PIN_02,    P102   }, /* (5) D5~ */
  { BSP_IO_PORT_01_PIN_06,    P106   }, /* (6) D6~ */
  { BSP_IO_PORT_01_PIN_07,    P107   }, /* (7) D7  */
  { BSP_IO_PORT_03_PIN_04,    P304   }, /* (8) D8  */
  { BSP_IO_PORT_03_PIN_03,    P303   }, /* (9) D9~  */
  { BSP_IO_PORT_01_PIN_12,    P112   }, /* (10) D10~ */
  { BSP_IO_PORT_01_PIN_09,    P109   }, /* (11) D11~ */
  { BSP_IO_PORT_01_PIN_10,    P110   }, /* (12) D12 */
  { BSP_IO_PORT_01_PIN_11,    P111   }, /* (13) D13 */
  { BSP_IO_PORT_00_PIN_14,    P014   }, /* (14) A0  --------------------------  ANALOG  */
  { BSP_IO_PORT_00_PIN_00,    P000   }, /* (15) A1  */
  { BSP_IO_PORT_00_PIN_01,    P001   }, /* (16) A2  */
  { BSP_IO_PORT_00_PIN_02,    P002   }, /* (17) A3  */
  { BSP_IO_PORT_01_PIN_01,    P101   }, /* (18) A4/SDA  */
  { BSP_IO_PORT_01_PIN_00,    P100   }, /* (19) A5/SCL  */

  { BSP_IO_PORT_05_PIN_00,    P500   }, /* (20) Analog voltage measure pin  */
  { BSP_IO_PORT_00_PIN_12,    P012   }, /* (21) TX LED  */
  { BSP_IO_PORT_00_PIN_13,    P013   }, /* (22) RX LED  */

  { BSP_IO_PORT_05_PIN_01,    P501   }, /* (23) TX on SWD connector  */
  { BSP_IO_PORT_05_PIN_02,    P502   }, /* (24) RX on SWD connector  */
  { BSP_IO_PORT_01_PIN_08,    P108   }, /* (25) SWDIO  */
  { BSP_IO_PORT_03_PIN_00,    P300   }, /* (26) SWCLK  */
};

variants/UNOWIFIR4/pins_arduino.h

#define SERIAL_HOWMANY		3
#define UART1_TX_PIN        22
#define UART1_RX_PIN        23
#define UART2_TX_PIN        1
#define UART2_RX_PIN        0
#define UART3_TX_PIN        24
#define UART3_RX_PIN        25

variant.cpp:

extern "C" const PinMuxCfg_t g_pin_cfg[] = { 
  { BSP_IO_PORT_03_PIN_01,    P301   }, /* (0) D0  -------------------------  DIGITAL  */
  { BSP_IO_PORT_03_PIN_02,    P302   }, /* (1) D1  */
  { BSP_IO_PORT_01_PIN_04,    P104   }, /* (2) D2  */
  { BSP_IO_PORT_01_PIN_05,    P105   }, /* (3) D3~ */
  { BSP_IO_PORT_01_PIN_06,    P106   }, /* (4) D4  */
  { BSP_IO_PORT_01_PIN_07,    P107   }, /* (5) D5~ */
  { BSP_IO_PORT_01_PIN_11,    P111   }, /* (6) D6~ */
  { BSP_IO_PORT_01_PIN_12,    P112   }, /* (7) D7  */
  { BSP_IO_PORT_03_PIN_04,    P304   }, /* (8) D8  */
  { BSP_IO_PORT_03_PIN_03,    P303   }, /* (9) D9~  */
  { BSP_IO_PORT_01_PIN_03,    P103   }, /* (10) D10~ */
  { BSP_IO_PORT_04_PIN_11,    P411   }, /* (11) D11~ */
  { BSP_IO_PORT_04_PIN_10,    P410   }, /* (12) D12 */
  { BSP_IO_PORT_01_PIN_02,    P102   }, /* (13) D13 */
  { BSP_IO_PORT_00_PIN_14,    P014   }, /* (14) A0  --------------------------  ANALOG  */
  { BSP_IO_PORT_00_PIN_00,    P000   }, /* (15) A1  */
  { BSP_IO_PORT_00_PIN_01,    P001   }, /* (16) A2  */
  { BSP_IO_PORT_00_PIN_02,    P002   }, /* (17) A3  */
  { BSP_IO_PORT_01_PIN_01,    P101   }, /* (18) A4/SDA  */
  { BSP_IO_PORT_01_PIN_00,    P100   }, /* (19) A5/SCL  */

  { BSP_IO_PORT_05_PIN_00,    P500   }, /* (20) Analog voltage measure pin  */
  { BSP_IO_PORT_04_PIN_08,    P408   }, /* (21) USB switch, drive high for RA4  */

  { BSP_IO_PORT_01_PIN_09,    P109   }, /* (22) D22 ------------------------  TX */
  { BSP_IO_PORT_01_PIN_10,    P110   }, /* (23) D23 ------------------------  RX */
  { BSP_IO_PORT_05_PIN_01,    P501   }, /* (24) D24 ------------------------- TX WIFI */
  { BSP_IO_PORT_05_PIN_02,    P502   }, /* (25) D25 ------------------------- RX WIFI */

  { BSP_IO_PORT_04_PIN_00,    P400   }, /* (26) D26  QWIC SCL */
  { BSP_IO_PORT_04_PIN_01,    P401   }, /* (27) D27  QWIC SDA */

  { BSP_IO_PORT_00_PIN_03,    P003   }, /* (28) D28  */
  { BSP_IO_PORT_00_PIN_04,    P004   }, /* (29) D29  */
  { BSP_IO_PORT_00_PIN_11,    P011   }, /* (30) D30  */
  { BSP_IO_PORT_00_PIN_12,    P012   }, /* (31) D31  */
  { BSP_IO_PORT_00_PIN_13,    P013   }, /* (32) D32  */
  { BSP_IO_PORT_00_PIN_15,    P015   }, /* (33) D33  */
  { BSP_IO_PORT_02_PIN_04,    P204   }, /* (34) D34  */
  { BSP_IO_PORT_02_PIN_05,    P205   }, /* (35) D35  */
  { BSP_IO_PORT_02_PIN_06,    P206   }, /* (36) D36  */
  { BSP_IO_PORT_02_PIN_12,    P212   }, /* (37) D37  */
  { BSP_IO_PORT_02_PIN_13,    P213   }, /* (38) D38  */
};