rogerclarkmelbourne / Arduino_STM32

Arduino STM32. Hardware files to support STM32 boards, on Arduino IDE 1.8.x including LeafLabs Maple and other generic STM32F103 boards
Other
2.53k stars 1.26k forks source link

Serial USB: RTS/DTR not working? #773

Closed CURTLab closed 4 years ago

CURTLab commented 4 years ago

I adapted the USB-uart-w-signals.ino example in order to upload firmware to the ESP32 using the STM32 blue pill (STM32f103C8T6). I used the USB Serial RTS & DTR signals to switch pins to bring the ESP32 to the upload mode using the ESP's EN and BOOT (GPIO0) pins. Somehow RTS & DTR is not working correctly. DTR functions sometimes but at the moment RTS never worked. If I put the ESP32 into upload mode manually (BOOT to GND, Short pulse to GND for EN), the upload works.

I tried different sketches, like the USBComposite with nearly the same code, but here nothing works.

I tried everything I can, but I have no glue how to further investigate the problem.

Code:

#include "Arduino.h"
#include <usb_serial.h>
#include <libmaple/usart.h>
#include <libmaple/usb_cdcacm.h>

void configSerial();

uint8 ledPin = LED_BUILTIN;
uint8 dtrPin = PB0 /* EN */, rtsPin = PA1 /* BOOT */;
uint8 dtr, rts;

#define INVERT(x) (~x) & 1

bool bsetlinecoding = false;
static void usbSetupHook(unsigned hook __attribute__((unused)), void *requestvp)
{
  const uint8 request = *(uint8 *)requestvp;
  if (request == USB_CDCACM_SET_LINE_CODING)
    bsetlinecoding = true;
}

void setup()
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  Serial.begin();

  usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, &usbSetupHook);
  usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, nullptr);

  Serial2.begin(115200, SERIAL_8N1);
  dtr = usb_cdcacm_get_dtr();
  rts = usb_cdcacm_get_rts();

  pinMode(dtrPin, OUTPUT);
  digitalWrite(dtrPin, INVERT(dtr));
  pinMode(rtsPin, OUTPUT);
  digitalWrite(rtsPin, INVERT(rts));
}

void loop()
{
  if (bsetlinecoding) {
    configSerial();
    bsetlinecoding = false;
  }

  if (usb_cdcacm_get_dtr() != dtr) {
    dtr = INVERT(dtr);
    digitalWrite(dtrPin, INVERT(dtr));
  }

  if (usb_cdcacm_get_rts() != rts) {
    rts = INVERT(rts);
    digitalWrite(rtsPin, INVERT(rts));
  }

  if (Serial2.available()) {
    digitalWrite(ledPin, HIGH); // blink the led for traffic
    while (Serial2.available()) {
      char c = Serial2.read();
      Serial.write(c);
    }
    digitalWrite(ledPin, LOW);
  }

  if (Serial.available())
  {
    digitalWrite(ledPin, HIGH); // blink the led for traffic
    while (Serial.available())
    {
      char c = Serial.read();
      Serial2.write(c);
    }
    digitalWrite(ledPin, LOW);
  }
  asm("wfi");
}

void configSerial()
{
  const uint32 baud = usb_cdcacm_get_baud();
  Serial2.begin(baud, SERIAL_8N1);

  uint32_t u2cr1 = USART2_BASE->CR1;
  uint32_t u2cr2 = USART2_BASE->CR2;
  uint8 bits = usb_cdcacm_get_n_data_bits();
  if (bits == 9)
    u2cr1 |= USART_CR1_M;
  else //8 bits, and all others, zero out M bit
    u2cr1 &= ~USART_CR1_M;

  const uint8 stop = usb_cdcacm_get_stop_bits();
  u2cr2 &= ~USART_CR2_STOP;
  if (stop == 2)
    u2cr2 |= USART_CR2_STOP_BITS_1;
  else if (stop == 1)
    u2cr2 |= USART_CR2_STOP_BITS_1_POINT_5;
  //else 1 stop bits

  const uint8 parity = usb_cdcacm_get_parity();
  if (parity == 1) { //odd
    u2cr1 |= USART_CR1_PCE; //enable parity
    u2cr1 |= USART_CR1_PS_ODD;
  } else if (parity == 2) { //even
    u2cr1 |= USART_CR1_PCE; //enable parity
    u2cr1 &= ~USART_CR1_PS; //zero out PS - even parity
  } else {
    u2cr1 &= ~(USART_CR1_PCE | USART_CR1_PS);
  }
  USART2_BASE->CR1 = u2cr1;
  USART2_BASE->CR2 = u2cr2;
}

PlatformIO config:

[env:genericSTM32F103C8]
platform = ststm32
board = genericSTM32F103C8
board_build.mcu = stm32f103c8t6
framework = arduino
;build_flags = -DSERIAL_USB

debug_tool = stlink
upload_protocol = stlink
stevstrong commented 4 years ago

Have you tried with Arduino IDE? How do you toggle RTS/DTR? Do you really need to invert twice the signals?

stevstrong commented 4 years ago

Here you can find a small command line utility for Windows to toggle RTS and DTR lines. Launch it in a CMD window and pass the COMxy port as input parameter.

toggle_rts_dtr.zip

Example how to use it: toggle_rts_dtr.exe COM14

Source: https://github.com/xanthium-enterprises/DTR-RTS-Pin-Control-using-Win32-API-C/blob/master/SerialPort_RTS_DTR.c

CURTLab commented 4 years ago

Besides hterm, I also tied it with a small python script.

import serial
import time
ser = serial.Serial('COM23', 115200)
while (True):
    ser.setDTR(1)
    ser.setRTS(0)
    time.sleep(0.1)
    ser.setDTR(0)
    ser.setRTS(1)
    time.sleep(0.1)

But same result: grafik

stevstrong commented 4 years ago

I have found out that the RTS pin control is not reliable, and is dependent on the host. DTR control is always reliable. In my test system using the previously attached app, the RTS pin goes high when I set (on host) to low, and goes low when I set DTR to low. So I don't think that this issue is device (STM32) related, but rather host.

Have you tried to control the DTR / CTS pins of a FTDI usb serial adapter?

stevstrong commented 4 years ago

And do not rely on hterm. I tried Coolterm and produces weird result:

So I don't know how reliable is python. You should first test it with another device to be sure that it works on that.

stevstrong commented 4 years ago

I made more test and found out a reliable way to toggle RTS:

Example: start condition: DTR =x; RTS = 0; Then: RTS = 1 followed by DTR = x => RTS will be set on device. RTS = 0 followed by DTR = x => RTS will be cleared on device. wherein x can be 0 or 1, but same value during the process.

Try out this method.

Btw, here an improved version of toggle app (windows exe) which allows to toggle continuously RTS and/or DTR based on key press ('r' for RTS=0, 'R' for RTS=1, 'd' for DTR=0, 'D' for DTR=1, any other to quit.). toggle_rts_dtr.zip

EDIT And this is the sketch I was using for tests (with Arduino IDE):

//-----------------------------------------------------------------------------
uint8 old_dtr, old_rts;
//-----------------------------------------------------------------------------
void usbHook(void)
{
    uint8 dtr = usb_cdcacm_get_dtr();
    if (old_dtr != dtr)
    {
        old_dtr = dtr;
        // you can toggle a pin instead serial
        Serial1.write('D'); Serial1.write('0'+dtr); Serial1.write('\n');
    }
    uint8 rts = usb_cdcacm_get_rts();
    if (old_rts != rts)
    {
        old_rts = rts;
        // you can toggle a pin instead serial
        Serial1.write('R'); Serial1.write('0'+rts); Serial1.write('\n');
    }
}
//-----------------------------------------------------------------------------
extern void usb_cdcacm_set_hooks(uint8_t, voidFuncPtr);
//-----------------------------------------------------------------------------
void setup()
{
    enableDebugPorts();
    // initialize digital pin PC13 as an output.
    pinMode(LED_BUILTIN, OUTPUT);
    Serial1.begin(115200);

    old_dtr = usb_cdcacm_get_dtr();
    old_rts = usb_cdcacm_get_rts();
    Serial1.write('D'); Serial1.write('0'+old_dtr); Serial1.write('\n');
    Serial1.write('R'); Serial1.write('0'+old_rts); Serial1.write('\n');

    usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, usbHook);
}
//-----------------------------------------------------------------------------
void loop()
{
}
rogerclarkmelbourne commented 4 years ago

Closed because of no response from the OP