I am using a Blue Pill (BP) with generic_boot20_pc13.bin loaded.
At power up and using “lsusb” the BP enumerates as “Bus 020 Device 014: ID 1eaf:0004 1eaf Maple”
and it is possible to connect serially to a device “/dev/cu.usbmodem1411”.
After programming a simple sketch “lsusb” shows the following device: “Bus 020 Device 024: ID 1eaf:0003 1eaf Maple 003 Serial: LLM 003”
at this point it is not possible to serial connect or reprogram without a hard reset of the BP. There is a clue at the end of the upload dialogue, “Resetting USB to switch back to runtime mode”.
Deducing device DFU version from functional descriptor length
Opening DFU capable USB device...
ID 1eaf:0003
Run-time device DFU version 0110
Claiming USB DFU Interface...
Setting Alternate Setting #2 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 0110
Device returned transfer size 1024
Copying data from PC to DFU device
Download [ ] 0% 0 bytes
Download [= ] 6% 1024 bytes
Download [=== ] 13% 2048 bytes
Download [==== ] 19% 3072 bytes
Download [====== ] 26% 4096 bytes
Download [======== ] 33% 5120 bytes
Download [========= ] 39% 6144 bytes
Download [=========== ] 46% 7168 bytes
Download [============= ] 53% 8192 bytes
Download [============== ] 59% 9216 bytes
Download [================ ] 66% 10240 bytes
Download [================== ] 72% 11264 bytes
Download [=================== ] 79% 12288 bytes
Download [===================== ] 86% 13312 bytes
Download [======================= ] 92% 14336 bytes
Download [======================== ] 99% 14420 bytes
Download [=========================] 100% 14420 bytes
Download done.
state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present
Done!
Resetting USB to switch back to runtime mode
There is related topic to that issue. I’ve modified the bootloader for blue-pill to solve the problem.
Failed to reconnect serial monitor after uploading on macOS
http://www.stm32duino.com/viewtopic.php?f=21&t=2050
I attached the modified bootloader. Please try this if you can flash the bootloader to your blue-pill.
/Users/johare/Documents/Arduino/hardware/Arduino_STM32/tools/macosx/maple_upload cu.usbmodem1411 2 1EAF:0003 /var/folders/6c/gxr4fz9s0_1cfcfnq7b8fmr40000gp/T/arduino_build_661694/rik.ino.bin
dfu-util 0.8
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
dfu-util: Invalid DFU suffix signature
This program is Free Software and has ABSOLUTELY NO WARRANTY
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Deducing device DFU version from functional descriptor length
dfu-util: No DFU capable USB device available
An error occurred while uploading the sketch
/Users/johare/Documents/Arduino/hardware/Arduino_STM32/tools/macosx/maple_upload cu.usbmodem1411 2 1EAF:0003 /var/folders/6c/gxr4fz9s0_1cfcfnq7b8fmr40000gp/T/arduino_build_661694/rik.ino.bin
dfu-util 0.8
dfu-util: Invalid DFU suffix signature
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Deducing device DFU version from functional descriptor length
Opening DFU capable USB device...
ID 1eaf:0003
Run-time device DFU version 0110
Claiming USB DFU Interface...
Setting Alternate Setting #2 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 0110
Device returned transfer size 1024
Copying data from PC to DFU device
Download [ ] 0% 0 bytes
Download [= ] 6% 1024 bytes
Download [=== ] 13% 2048 bytes
Download [==== ] 19% 3072 bytes
Download [====== ] 26% 4096 bytes
Download [======== ] 33% 5120 bytes
Download [========= ] 39% 6144 bytes
Download [=========== ] 46% 7168 bytes
Download [============= ] 52% 8192 bytes
Download [============== ] 59% 9216 bytes
Download [================ ] 66% 10240 bytes
Download [================== ] 72% 11264 bytes
Download [=================== ] 79% 12288 bytes
Download [===================== ] 86% 13312 bytesprocessing.app.SerialException: Error opening serial port '/dev/cu.usbmodem1411'.
at processing.app.Serial.<init>(Serial.java:125)
at processing.app.Serial.<init>(Serial.java:66)
at processing.app.SerialMonitor$3.<init>(SerialMonitor.java:93)
at processing.app.SerialMonitor.open(SerialMonitor.java:93)
at processing.app.AbstractMonitor.resume(AbstractMonitor.java:110)
at processing.app.Editor.resumeOrCloseSerialMonitor(Editor.java:2226)
at processing.app.Editor.access$2400(Editor.java:77)
at processing.app.Editor$DefaultExportHandler.run(Editor.java:2204)
at java.lang.Thread.run(Thread.java:745)
Caused by: jssc.SerialPortException: Port name - /dev/cu.usbmodem1411; Method name - openPort(); Exception type - Port not found.
at jssc.SerialPort.openPort(SerialPort.java:167)
at processing.app.Serial.<init>(Serial.java:114)
... 8 more
Error opening serial port '/dev/cu.usbmodem1411'.
Download [======================= ] 92% 14336 bytes
Download [======================== ] 99% 14444 bytes
Download [=========================] 100% 14444 bytes
Download done.
state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present
Done!
Resetting USB to switch back to runtime mode
void setup() {
delay(1000);
//Initialize USBSerial and wait for port to open:
Serial.begin();
.
.
Deducing device DFU version from functional descriptor length
dfu-util: No DFU capable USB device available
An error occurred while uploading the sketch
...
Download [================== ] 72% 11264 bytes
Download [=================== ] 79% 12288 bytes
Download [===================== ] 86% 13312 bytesprocessing.app.SerialException: Error opening serial port '/dev/cu.usbmodem1411'.
at processing.app.Serial.<init>(Serial.java:125)
at processing.app.Serial.<init>(Serial.java:66)
at processing.app.SerialMonitor$3.<init>(SerialMonitor.java:93)
at processing.app.SerialMonitor.open(SerialMonitor.java:93)
at processing.app.AbstractMonitor.resume(AbstractMonitor.java:110)
at processing.app.Editor.resumeOrCloseSerialMonitor(Editor.java:2226)
at processing.app.Editor.access$2400(Editor.java:77)
at processing.app.Editor$DefaultExportHandler.run(Editor.java:2204)
at java.lang.Thread.run(Thread.java:745)
Caused by: jssc.SerialPortException: Port name - /dev/cu.usbmodem1411; Method name - openPort(); Exception type - Port not found.
at jssc.SerialPort.openPort(SerialPort.java:167)
at processing.app.Serial.<init>(Serial.java:114)
... 8 more
Error opening serial port '/dev/cu.usbmodem1411'.
Download [======================= ] 92% 14336 bytes
Download [======================== ] 99% 14444 bytes
Download [=========================] 100% 14444 bytes
Download done.
...
When you get this error of “dfu-util: No DFU capable USB device available”, you should check the red LED on the BP to see if the maple_upload reset the board successfully with the Arduino_STM32/tools/macosx/upload-reset utility.
This exact same thing happens to me if I leave the serial monitor open and then press the upload button.
The answer to this problem is to close the serial monitor window before you try to upload new code.
That is true if you use old Arduino IDE. Latest IDE will close a serial port automatically when you push the upload button and re-open the serial port again after uploading.
FWIW: my bluepill has a blue user led (PC13) and a red power led.
One of mine have blue power led and red user led. And another one of mine have red power led and green user led. ![]()
That is true if you use old Arduino IDE. Latest IDE will close a serial port automatically when you push the upload button and re-open the serial port again after uploading.
diff --git a/tools/macosx/maple_upload b/tools/macosx/maple_upload
index 8d15eff..ef581c8 100755
--- a/tools/macosx/maple_upload
+++ b/tools/macosx/maple_upload
@@ -50,4 +50,13 @@ if [ ! -x ${DFU_UTIL} ]; then
exit 2
fi
+echo ${DFU_UTIL} -d ${usbID} -a ${altID} -D ${binfile} -R ${dfuse_addr} -R
${DFU_UTIL} -d ${usbID} -a ${altID} -D ${binfile} -R ${dfuse_addr} -R
+
+# take a breath waiting for restarting the target device
+/bin/echo -n wait for ${dummy_port_fullpath}...
+while [ ! -c ${dummy_port_fullpath} ]; do
+ sleep 0.1
+done
+sleep 0.3
+echo done.
That is true if you use old Arduino IDE. Latest IDE will close a serial port automatically when you push the upload button and re-open the serial port again after uploading.
I still see an issue where a delay is used immediately after setup():
void setup() {
delay(1000);
//Initialize USBSerial and wait for port to open:
Serial.begin();
while (!Serial) { ; }
while (!Serial.isConnected() ) { ; }
–edit: Why is the bootloader affected by detail in the sketch?
–edit: Doesn’t explain the programming not working. Does the boot loader spend 265ms trying to set up the serial path before doing the programming?
void setup() {
//Initialize USBSerial and wait for port to open:
Serial.begin();
delay(1000);
–edit: Doesn’t explain the programming not working. Does the boot loader spend 265ms trying to set up the serial path before doing the programming?
–edit: Does that explain the programming? A delay greater than 0.265s in the sketch causes the programming to fail? I still don’t quite see it.
Are there any errors from maple_upload nor upload-reset? You can’t program if it fails to reset the board.
If you still have these two lines below in the setup(), they may block the sketch and upload-reset may not work. Because, upload-reset do open/close in 1200bps to notify resetting to the user application (sketch) running on the board and the sketch must handle the open/close in 1200bps properly.
while (!Serial) { ; }
while (!Serial.isConnected() ) { ; }
based on the debug output JohnO posted, it appears the IDE is trying to resume the serial port before the download is complete.
Am I correct to understand that Serial.begin must be in any sketch for the uploader/bootloader to function no matter that the sketch has no other need for serial?
Am I correct to understand that Serial.begin must be in any sketch for the uploader/bootloader to function no matter that the sketch has no other need for serial?
No, sorry. My understanding was not correct.
I can upload my simple sketch which does not include Serial.begin() repeatedly.
So, have all of your problems been solved? Is the longer wait of the bootloader needed?
However, I would like to understand why having a delay(1000) as the first line after setup() causes the subsequent program upload to fail. That is, if the active code loaded into flash contains delay(1000) as the first line in setup() then the next upload will fail with:
/Users/johare/Documents/Arduino/hardware/Arduino_STM32/tools/macosx/dfu-util/dfu-util -d 1EAF:0003 -a 2 -D /var/folders/6c/gxr4fz9s0_1cfcfnq7b8fmr40000gp/T/arduino_build_996/rik.ino.bin -R -R
dfu-util 0.8
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Deducing device DFU version from functional descriptor length
dfu-util: No DFU capable USB device available
An error occurred while uploading the sketch
while(!Serial.available()) delay(10);Have you been able to verify my experience in this?
Everything else is spot on.
It *feels* that the delay is preventing the bootloader from interacting with dfu-util.
No… I can upload this sketch repeatedly.
void setup() {
delay(1000); // 2000, 3000...
Serial.begin(9600);
while (!Serial);
Serial.println("Hello!");
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
Serial.println("running...");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
to install my sketches e.g.using a shell script
#!/bin/bash
dfu-util -l
dfu-util -a 2 -R -D $*
for the maple mini boards, there is a (user) button (actually boot0 is *reused* for this purpose) that you can press to tell the bootloader to go into ‘perpetual boot loader mode’
this option is not feasible on bluepill because it is simply a jumper, if you set the boot0 jumper to high and press reset, it would drop into uart bootloader/install mode (this is ST’s native bootloader), not the stm32duino boot loader. to get into ‘perpetual boot loader’ mode would mean that you have to keep boot0 low when you press reset, and quickly set boot0 high *during* the fast blinks, this is very difficult short of making boot0 a button or switch for that matter
i’ve thought that normally serial monitor is manually started from the menu, isn’t it the case?
note that a sketch install is not necessary if it is simply to run the sketch, a reset on the blue pill/maple mini would restart the sketch.
to keep things more predictable, e.g. i’m anticipating prints on serial terminal, i often patch some codes to wait for key presses on the serial console before the rest of the sketch runs, the other tricks i often do with problematic sketches is to turn on the board led, this would give an indication if after all the sketch crashed e.g.//board led pin 33 PB1 on maple mini
#define BOARD_LED_PIN 33
void setup() {
Serial.begin(115200);
pinMode(BOARD_LED_PIN,OUTPUT);
digitalWrite(BOARD_LED_PIN,HIGH);
Serial.println("press any key to start");
while(!Serial.available()) delay(10);
digitalWrite(BOARD_LED_PIN,LOW); //led off after keypress is received
}
i’ve thought that normally serial monitor is manually started from the menu, isn’t it the case?
Have you been able to verify my experience in this?
Everything else is spot on.
It *feels* that the delay is preventing the bootloader from interacting with dfu-util.
No… I can upload this sketch repeatedly.
void setup() {
delay(1000); // 2000, 3000...
Serial.begin(9600);
while (!Serial);
Serial.println("Hello!");
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
Serial.println("running...");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
i tried the patches by hanyazou, it is basically editing the shell script that’s part of the Arduino_STM32 core. i’m using linux opensuse 13.1
viewtopic.php?f=28&t=2107#p28441
it works pretty well with those patches to set a little delay after the sketch install
the shell script is part of Arduino/hardware/Arduino_STM32/tools/{macosx,linux,linux64,win}/maple_upload
hence we’d not need to wait for the arduino ide implementers to provide any fixes
i’m able to compile/uploaded a sketch and connect to it in serial terminal within the arduino ide after the patch.
(usb)serial is working as expected as well
@JohnO,
you may like to try the boot1 as ‘perpetual boot loader’ bootloader from roger’s repository
http://www.stm32duino.com/viewtopic.php … =20#p28466
this may help to keep the BP waiting for the install, instead of manually racing between pressing reset and clicking on sketch install on the arduino ide
i tried your code with some additions to wait for a key press
apparently the delay before Serial.begin() has an impact.
if i set delay(450); before Serial.begin() the sketch still runs normally
however, if i set delay(500); before Serial.begin() the sketch stalls, the led remains on and is not responsive to the keypress (i.e. the led did not turn off) based on my key press wait
if however, if you place that delay(1000) after Serial.begin() everything works. this i think may have something to do with USB-Serial initialization on the host. perhaps too long a delay cause a timeout on the host and the host no longer treats that as a usb serial (i.e. CDC ACM) device.
hence, we should not put a delay before Serial.begin(). Serial.begin() should start as early as Setup() is initiated so that the usb-serial enumeration and initialization can start as soon as possible.
#include "Streaming.h" //http://arduiniana.org/libraries/streaming/
//board led pin 33 PB1 on maple mini
#define BOARD_LED_PIN 33
static const bool USE_STREAMING = true;
void setup() {
//delay(1000);
//Initialize USBSerial and wait for port to open:
Serial.begin();
delay(1000);
pinMode(BOARD_LED_PIN,OUTPUT);
digitalWrite(BOARD_LED_PIN,HIGH);
Serial.println("press any key to start");
while(!Serial.available()) delay(10);
digitalWrite(BOARD_LED_PIN,LOW); //led off after keypress is received
if ( USE_STREAMING ) {
Serial << "ASCII Table ~ Character Map" << endl;
}
else {
// prints title with ending line break
Serial.println("ASCII Table ~ Character Map");
}
}
// first visible ASCIIcharacter '!' is number 33:
int thisByte = 33;
void loop() {
if ( USE_STREAMING ) {
Serial << (char)thisByte
<< ", dec: " << _DEC(thisByte)
<< ", hex: " << _HEX(thisByte)
<< ", oct: " << _OCT(thisByte)
<< ", bin: " << _BIN(thisByte)
<< endl;
}
else {
Serial.write((char)thisByte);
Serial.print(", dec: ");
Serial.print(thisByte, DEC);
Serial.print(", hex: ");
Serial.print(thisByte, HEX);
Serial.print(", oct: ");
Serial.print(thisByte, OCT);
Serial.print(", bin: ");
Serial.println(thisByte, BIN);
}
if (thisByte == 126) { // you could also use if (thisByte == '~') {
// This loop loops forever and does nothing
// while (true) {
// delay(2000);
thisByte = 33;
// }
}
// go on to the next character
thisByte++;
}
My head struggles to accept that delay(1000) before Serial.begin should have these implications and I need to understand why this is happening before taking on board a workaround. Some sketches are not impacted by the delay while others screw up the programming, we have an issue here. If delay has these implications there may be others and debugging them in the *next* project may be even more difficult than unravelling it now.
The answer will be in the boot loader since the bootloader is releasing the user level code before it has completed its handling of start up.
i’ve been installing sketch manually using dfu-util and using an external serial terminal
i think hanyazou’s fixes in the dfu-install shell script should be commited to roger’s Arduino_STM32 repository, at least for linux and mac, i’m not sure about windows though
if Serial.begin() is not called as a default in the sketch initiation before setup() (this is in the libmaple core codes), this can be read as a ‘good thing’. i.e. if you need Serial, you need to call Serial.begin() as early as possible in setup();. in this way if you want to use usb for any other purposes, you could call usb_some_other_device_class.begin(), this would allow the Blue pill/Maple mini to be used as a ‘generic’ usb device, e.g. a different sketch makes BP/MM works as a different usb device serial/mass storage/HID/audio etc
the rest are ‘stock’ from Arduino_STM32
So I can confirm, we may have an issue here.
Johns-MacBook-Pro:~ john$ ./dfu-install.sh /var/folders/6c/gxr4fz9s0_1cfcfnq7b8fmr40000gp/T/arduino_build_696853/rik.ino.bin
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Deducing device DFU version from functional descriptor length
Found Runtime: [05ac:821d] ver=0154, devnum=8, cfg=1, intf=3, path="29-3", alt=0, name="UNKNOWN", serial="UNKNOWN"
Found DFU: [1eaf:0003] ver=0201, devnum=7, cfg=1, intf=0, path="20-2", alt=2, name="STM32duino bootloader v1.0 Upload to Flash 0x8002000", serial="LLM 003"
Found DFU: [1eaf:0003] ver=0201, devnum=7, cfg=1, intf=0, path="20-2", alt=1, name="STM32duino bootloader v1.0 Upload to Flash 0x8005000", serial="LLM 003"
Found DFU: [1eaf:0003] ver=0201, devnum=7, cfg=1, intf=0, path="20-2", alt=0, name="STM32duino bootloader v1.0 ERROR. Upload to RAM not supported.", serial="LLM 003"
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Deducing device DFU version from functional descriptor length
dfu-util: More than one DFU capable USB device found! Try `--list' and specify the serial number or disconnect all but one device
you have another device that’s dfu capable hence the message, add the option -d 1eaf:0003 in the last line which installs the sketch itself
#!/bin/bash
dfu-util -l
dfu-util -d 1eaf:0003 -a 2 -R -D $*
The thing is…
Serial.begin() is already called in boards_setup.cpp in board_setup_usb() which is called from boards.cpp in init();
So if I have
void setup() {
// put your setup code here, to run once:
Serial.end();
delay(1000);
Serial.begin();
}
int c=0;
void loop() {
// put your main code here, to run repeatedly:
Serial.println(c++);
delay(500);
}
i’d think it is ‘simplier’ to leave USBSerial.begin() in the libmaple core initialization codes.
the reason is that a lot of arduino sketches expect Serial to be ‘there’ and we’d likely get more ‘complaints’ about a non-functional Serial.
using that ‘flag’ sounds like an interesting solution. but i’m half wondering if in this case, the second Serial.begin() that’s started by the sketch in this particular case actually initialise the usb-serial interface! i.e. the first initialization failed. it seem there might be some other possibilities, e.g. between USBSerial.begin() to the 2nd Serial.begin(), a time out of some kind occurred if a delay is inserted before the second Serial.begin(), but i’m not sure though
i may spend a little time playing with usb.end(); usb_reset() /* i.e. pulling D+/D- low for 10ms */; usb_something_else.begin()
i think that’s a feasible way to implement a multi-function usb device.
usb serial should stay there as it is very useful to have a serial usb right at the start, the sketch can always ‘warp’ into another usb device in its code e.g. the sketch can ‘listen on usb-serial’ and then end usb-serial, do a usb reset, and initialise as usb something else. this would pave the way for a ‘multi-function’ usb device, i.e. usb-serial is always a default mode to listen to ‘commands’
just 2 cents ![]()
you have another device that’s dfu capable hence the message, add the option -d 1eaf:0003 in the last line which installs the sketch itself
#!/bin/bash
dfu-util -l
dfu-util -d 1eaf:0003 -a 2 -R -D $*
@hanyazou Does your bootloader still have the Boot0 option to pull high during the fast flashes to enter perpetual bootloader?
My BP has a soft pulldown on Boot0 and I don’t seem able to pull it high to trigger perpetual bootloader mode.
The enhanced @hanyazou bootloader together with the script amendments appears to do it all except the for the Serial.begin issue as explained by @Roger.
From what I recall, I think I added the BKP register checking into the bootloader, but I didnt put the code in the core to set the value.
So you could try modifying the core to set the reg.
Edit.
Re-reading about this problem, locking the bootloader into perpetual mode via that flag sounds like ot would only partially fix the problem.
void setup() {
delay(500);
- Serial.begin(9600);
+ Serial.begin();
while (!Serial);
Serial.println("Hello!");
void setup() {
delay(500);
- Serial.begin(9600);
+ Serial.begin();
while (!Serial);
Serial.println("Hello!");
the USB states and state transitions are rather complicated, among the states are a ‘stall’ state. once the usb host gets into the ‘stall’ state, i think it may stop sending responses to the device.
i tend to be able to reproduce this if i insert a delay in excess of 450ms before the Serial.begin() gets called. I’m not too sure why after Serial.begin() is called, it can tolerate a delay of 1000ms, i’ve yet to try further delays after the (second) Serial.begin()
http://www.stm32duino.com/viewtopic.php … =30#p28483
in the mean time, rather than trying to chase down the issue, my thoughts are that to avoid possible issues, call Serial.begin() as soon as Setup() starts.
The other test i’m yet to do and confirm is can we simply skip this Serial.begin() in the sketch as it has after all been called prior during the initialization stages even before it reaches main(). I’m not too sure if by virtue of an accident of timing, the 2nd Serial.begin() actually initialise the usb. this seem unlikely nevertheless. My thoughts are that the 2nd Serial.begin() simply re-initialise USB.
this may be quite involved to troubleshoot short of using things like logic analyzers and protocol analysers to examine the frame interchanges. most of us may not have those equipment and may need to improvise from other means to troubleshoot it.
i’m thinking if somehow we can use say an stm32f407 as a protocol analyser since it has 2 usb and has both otg and device mode. But still nevertheless, this won’t be easy as it would at least need to write the ‘sketch’ / s/w to do that.
just 2 cents
kindly note that i’ve submitted a PR for hanyazou’s and danieleff’s fixes for inserting a short delay just after flashing the sketch
http://www.stm32duino.com/viewtopic.php … 107#p28441
http://www.stm32duino.com/viewtopic.php … =10#p28452
kindly comment directly on github in the pull request with your feedback
>>> https://github.com/rogerclarkmelbourne/ … 2/pull/292
void setup() {
delay(500);
- Serial.begin(9600);
+ Serial.begin();
while (!Serial);
Serial.println("Hello!");
( the compiler may make it into an int, but its still best if its coded as a boolean. )
I will update the repo
This patch works well. (but initial value is wrong.)
USBSerial::USBSerial(void) : running(1) {
#if !BOARD_HAVE_SERIALUSB
ASSERT(0);
#endif
}
void USBSerial::begin(void) {
if (running)
return;
running = 1;
#if BOARD_HAVE_SERIALUSB
usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT);
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook);
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, ifaceSetupHook);
#endif
}
Yes.
I noticed that something strange is happening
I put in some code to set a variable _hasBegun = true; in USBSerial::begin()
void USBSerial::begin(void) {
#if BOARD_HAVE_SERIALUSB
if (_hasBegun==true)
{
return;
}
usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT);
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook);
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, ifaceSetupHook);
_hasBegun=true;
#endif
}
https://github.com/rogerclarkmelbourne/ … er/pull/22
I know the code is run really early in the init, but I”m not sure how the variable could be cleared, or perhaps its not getting set.
Perhaps making it static may help, as there is only one USB device on the F1
e.g. in the F4 (black/generic) branch that’s currently in development on steve’s repository, here is how init() is written, SerialUSB.begin() is run just before exiting from init()
https://github.com/stevstrong/Arduino_S … boards.cpp
my guess is we could actually call init() from main, just that there is a catch. the hardware initializations would not have happened until main() is entered and then main() calls init(). it could lead to unpredictable behaviour say if an interrupt happens prior to reaching there.
——- stm32f1duino bootstrap ———–
there are things that run before main() executes, this is how it works
in the variant’s folder e.g. STM32F1/variants/maple_mini/wirish there is start.S and start_c.c, the call graph starts as
start.s -> start_c() -> __libc_init_array();
__libc_init_array() is part of newlib which looks like this
https://github.com/eblot/newlib/blob/ma … isc/init.c
/* Iterate over all the init routines. */
void
__libc_init_array (void)
{
size_t count;
size_t i;
count = __preinit_array_end - __preinit_array_start;
for (i = 0; i < count; i++)
__preinit_array_start[i] (); //*********
_init ();
count = __init_array_end - __init_array_start;
for (i = 0; i < count; i++)
__init_array_start[i] ();
}
i think this recent fix by edogaldo for the ringbuffer is committed in the recent updates on master branch
http://www.stm32duino.com/viewtopic.php?f=3&t=2091
if USB serial uses the ringbuffer, perhaps it may have resolved, alleviated the issues?
![]()
The USB code change to volatile is probably needed because its changed in an interrupt, but I’m not sure if the initialisation code is via an interrupt.
The F1 only has 1 USB interface, so the variable could be declared as static, but that solution would not work for the F4 as it has 2 USB posts
i.e. the variable is deemed ‘volatile’ if the value can change even if the cpu itself ‘did nothing’. this would be true for gpio registers
but i’ve not dug deeper into the usb codes yet
i could imagine that if perhaps there are 2 statements
a = gpio_register;
b = gpio_register;
the compiler may read gpio_register and simply use cpu registers to update a and b. declaring gpio_register volatile i’d guess would make it always read from the memory location
——- stm32f1duino bootstrap ———–
there are things that run before main() executes, this is how it works
in the variant’s folder e.g. STM32F1/variants/maple_mini/wirish there is start.S and start_c.c, the call graph starts as
start.s -> start_c() -> __libc_init_array();
init() is not really different from main(), they are plain c functions
i’ve been toying with the idea that we call init(), setup() and loop() in main()
main() {
init();
setup();
while(1)
loop();
}
You can’t call Serial.begin() in Init() because init() should be called brefore ctors.
init() is not really different from main(), they are plain c functions
[/code]
i think this is cleaner and easier to read
but the usb codes and usb itself is rather complex and it would likely take some time to analyse and determine the root cause of issues
note that the *specai statement* ‘constructor’ is not the normal c++ constructors, they are c function hooks so that __libc_init_array() can call them.
in terms of static allocation, some of the global memory setups e.g. for global variables and static variables are done in start_c() itself
there could be a case in which c++ constructors uses the same __libc_init_array() mechanism, but i’m yet to examine it in detail, even then they are calling different functions possibly non-overlapping
alternatively there is actually a different hook
void _init () {}
as to the usb initialization, static object constructor. my thoughts are that one way to ‘fix it’ is that the usb class’s construction should not deal with hardware but simply initialize its own instance and global variables. by not touching hardware, it would not be caught in a situation e.g. that the interrupt vector address is changed.
class UsbSerial {
public:
static UsbSerial* getInstance() {
static UsbSerial static_usbserial;
return &static_usbserial;
}
}
The singleton method relies on a static property in the class, but as all we need to record is whether the USB Serial Begin has been called, cant we just make the _hasBegun property static?
what seemed necessary is some kind of ‘singleton’ codes so that there is only 1 instance and that we won’t be calling UsbSerial’s constructor twice
or perhaps if that 2nd init after all initialised things to work ok. we’d leave it after all. perhaps the ‘accident’ is that there is only one piece of usb hardware so if u initialise it again it is still the same
just 2 cents ![]()
I don’t really like that the Native USB is being instantiated without me (the user) requesting it. I dislike the 3 second delay when you configure your board to use the stm32duino bootloader. I would rather decide if and when I use the native serial. There is something about having something forced on me that just bothers me.
I can see the point of automatically configuring the native USB if you are using the bootloader. Otherwise you would have to press reset each time you upload.
However, I don’t see the point of automatically calling Serial.begin() if your upload method is set to Serial, STLink or BMP. In my opinion, It shouldn’t be called unless the user calls it. Calling it automatically eats up CPU to handle the USB heartbeat IRQ that i might want to use for other things. It delays my code from starting immediately at reset. It chews up flash code space that is never going to be called if I’m not using it.
FWIW: I’d like to see a optional native usb that doesn’t have the hokey in-stream parsing for the magic reset string. It would be a lot cleaner and probably smaller as all it would be doing is acting like a CDC ACM device.
With all that said, whatever is the final decision I can live with what happens. I’ll just ifdef out the stuff I don’t like and move on.
I dont think USB Serial is enabled at all if you upload via Serial.
It is still enabled for STLink, as the STLink dongles dont have the built in serial that the nucleo boards version contains.
I cant remember what the current config is for the BMP, but USB serial could be disabled on the BMP as it also has its own serial.
The reason I added it for bootloader uploads, was because people complained that they could not upload automatically after installing a Blink sketch.
Its just the OP who seems to have a problem with the current situation, as they are calling Serial.begin again after delaying for 1 second.
calling Serial.begin at the top of setup() does not cause any problems – however I agree it should cause problems as the usb subsystem is being init’ed twice
So I think the static var is probably the best work around to mask that Serial.begin() has already been called in the init ( for some configurations)
The only potential problem with the static var would be for multiple instances of the USB Serial class, but we may be able to work around this ,by using bit fields in the static var to indicate the USB Serial instance number.
And on the F1 there is only one instance anyway.
Please note that the constructor of the Serial object does not touch the USB serial HW.
Currently, in my assumption, it goes like below:
0. start_c() called from start.S
1. __libc_init_array() called
2. init() called
3. board_setup_usb() called
4. Serial.begin() called
_hadBegun = true and the serial HW will be initialized.
5. the constructor, ‘USBSerial::USBSerial(void) : _hasBegun(false)’ called from __libc_init_array()
_hasBegun = false
6. main() called
7. setup() called
8. 2nd Serial.begin() called
This overwrite USB HW registers because _hadBegun == false.
The problem is that we call the Serial.begin() before the constructor of the Serial object is called.
__libc_init_array() is actually explicitly called in the startup codes start_c();
this runs the special premain() codes
__attribute__(( constructor (101))) void premain() {
init();
}As far as I can remember, the problem I was trying to solve back then is how to have a USB boot loader work together with a sketch. Since the boot loader needs to initialise and enumerate the USB hardware, I think the issue was to try and avoid re-initialising USB a second time in the sketch, as that would cause re-enumeration (i.e. breaking and restoring the serial connection) in “setup()”.
I’ve not continued on this path, but for anyone interested in this code – the latest version should be available on GitHub, see https://github.com/jeelabs/embello/tree … s/usbserup
Did you step through with a debugger?
__libc_init_array() is called both in 1. and 5. steps?
you are correct about your evaluations about the static class initializations
http://www.stm32duino.com/viewtopic.php … 132#p28861
it would seem the hardware initializations needs to be done in 2 parts those parts that the static constructors depend on needs to be called before the static object constructors
then there are hardware initializations that depend on the static objects e.g. USBSerial class/object. this would need to be called *after* the static constructors initializations are complete.
this could be for instance be in main() before setup().
however, hardware initializations tend to be board and mcu specific e.g. the usb controller on f1 and f4 are different, this would mean calling different functions deep down the call stack. hence in this case, it would be better if a second init() is created but this 2nd init lives in the board specific files. there could possibly be #includes in the board/variant files so that common initializations for a same family e.g. stm32f1 could be called in that common file.
I’ve added init_2nd() and board_setup_usb_2nd() like below,
diff --git a/STM32F1/cores/maple/boards_private.h b/STM32F1/cores/maple/boards_private.h
index 49867ca..690577a 100644
--- a/STM32F1/cores/maple/boards_private.h
+++ b/STM32F1/cores/maple/boards_private.h
@@ -63,6 +63,7 @@ namespace wirish {
void board_setup_clock_prescalers(void);
void board_setup_gpio(void);
void board_setup_usb(void);
+ void board_setup_usb_2nd(void);
void series_init(void);
}
diff --git a/STM32F1/cores/maple/main.cpp b/STM32F1/cores/maple/main.cpp
index 3d78443..73f6f14 100644
--- a/STM32F1/cores/maple/main.cpp
+++ b/STM32F1/cores/maple/main.cpp
@@ -27,6 +27,7 @@
extern void setup(void);
extern void loop(void);
extern void init(void);
+extern void init_2nd(void);
// Force init to be called *first*, i.e. before static object allocation.
// Otherwise, statically allocated objects that need libmaple may fail.
@@ -35,6 +36,7 @@ extern void init(void);
}
int main(void) {
+ init_2nd(); // board initialization using statically allocated objects
setup();
while (1) {
diff --git a/STM32F1/cores/maple/usb_serial.cpp b/STM32F1/cores/maple/usb_serial.cpp
index 5eaf70e..dc86da7 100644
--- a/STM32F1/cores/maple/usb_serial.cpp
+++ b/STM32F1/cores/maple/usb_serial.cpp
@@ -55,13 +55,16 @@ static void ifaceSetupHook(unsigned, void*);
#define USB_TIMEOUT 50
-USBSerial::USBSerial(void) {
+USBSerial::USBSerial(void) : _hasBegun(false) {
#if !BOARD_HAVE_SERIALUSB
ASSERT(0);
#endif
}
void USBSerial::begin(void) {
+ if (_hasBegun)
+ return;
+ _hasBegun = true;
#if BOARD_HAVE_SERIALUSB
usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT);
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook);
@@ -90,6 +93,7 @@ void USBSerial::end(void) {
usb_cdcacm_disable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT);
usb_cdcacm_remove_hooks(USB_CDCACM_HOOK_RX | USB_CDCACM_HOOK_IFACE_SETUP);
#endif
+ _hasBegun = false;
}
size_t USBSerial::write(uint8 ch) {
diff --git a/STM32F1/cores/maple/usb_serial.h b/STM32F1/cores/maple/usb_serial.h
index 96bbefc..cf66347 100644
--- a/STM32F1/cores/maple/usb_serial.h
+++ b/STM32F1/cores/maple/usb_serial.h
@@ -71,6 +71,9 @@ public:
uint8 getDTR();
uint8 isConnected();
uint8 pending();
+
+protected:
+ bool _hasBegun;
};
#ifdef SERIAL_USB
diff --git a/STM32F1/variants/generic_stm32f103c/wirish/boards.cpp b/STM32F1/variants/generic_stm32f103c/wirish/boards.cpp
index 36fcb3e..a6acbb5 100644
--- a/STM32F1/variants/generic_stm32f103c/wirish/boards.cpp
+++ b/STM32F1/variants/generic_stm32f103c/wirish/boards.cpp
@@ -75,6 +75,10 @@ void init(void) {
boardInit();^M
}^M
^M
+void init_2nd(void) {^M
+ wirish::priv::board_setup_usb_2nd();^M
+}^M
+^M
/* Provide a default no-op boardInit(). */^M
__weak void boardInit(void) {^M
}^M
diff --git a/STM32F1/variants/generic_stm32f103c/wirish/boards_setup.cpp b/STM32F1/variants/generic_stm32f103c/wirish/boards_setup.cpp
index 5516282..b1e2c34 100644
--- a/STM32F1/variants/generic_stm32f103c/wirish/boards_setup.cpp
+++ b/STM32F1/variants/generic_stm32f103c/wirish/boards_setup.cpp
@@ -94,10 +94,16 @@ namespace wirish {
for(volatile unsigned int i=0;i<512;i++);// Only small delay seems to be needed, and USB pins will get configured in Serial.begin^M
gpio_set_mode(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit, GPIO_INPUT_FLOATING);^M
#endif ^M
- Serial.begin();// Roger Clark. Changed SerialUSB to Serial for Arduino sketch compatibility^M
#endif^M
}^M
^M
+ __weak void board_setup_usb_2nd(void) {^M
+#ifdef SERIAL_USB^M
+ // Roger Clark. Changed SerialUSB to Serial for Arduino sketch compatibility^M
+ Serial.begin();^M
+#endif^M
+ }^M
+^M
__weak void series_init(void) {^M
// Initialize AFIO here, too, so peripheral remaps and external^M
// interrupts work out of the box.^M
But if you submit a PR for one board e.g. The BluePill, it will allow the community to test it, and if there is an overwhelming majority in favour of the change it may become part of the core.
Although I host the LibMaple core, ultimately it’s open source and if it changes in ways people don’t like, they will just fork the repo and the community will use a different version without those changes.
So, changes which help a minority of users but cause problems for the majority of users, never get accepted.
But if you submit a PR for one board e.g. The BluePill, it will allow the community to test it, and if there is an overwhelming majority in favour of the change it may become part of the core.
Although I host the LibMaple core, ultimately it’s open source and if it changes in ways people don’t like, they will just fork the repo and the community will use a different version without those changes.
So, changes which help a minority of users but cause problems for the majority of users, never get accepted.
I know. I just wanted to hear a comment of you who is a member of the community.
this ‘discovery’ about c++ global or static objects is an ‘enlightenment’ it never occurs to me that static class initialization has to do with a single c function __libc_init_array(), i.e. c++ is designed for ‘everything to happen in main()’ not before it. c++ is designed for ‘system programming’ but i’d guess the designers didn’t managed to solve the problem if there is only an egg (writing the o/s) without the chicken (the o/s itself)
However I have noticed another existing bug.
I added 2 dummy functions for begin
//Roger Clark. Two new begin functions has been added so that normal Arduino Sketches that use Serial.begin(xxx) will compile.
void USBSerial::begin(unsigned long ignoreBaud)
{
volatile unsigned long removeCompilerWarningsIgnoreBaud=ignoreBaud;
ignoreBaud=removeCompilerWarningsIgnoreBaud;
}
void USBSerial::begin(unsigned long ignoreBaud, uint8_t ignore)
{
volatile unsigned long removeCompilerWarningsIgnoreBaud=ignoreBaud;
volatile uint8_t removeCompilerWarningsIgnore=ignore;
ignoreBaud=removeCompilerWarningsIgnoreBaud;
ignore=removeCompilerWarningsIgnore;
}
But I”ve found another issue which seems to affect the Maple mini and would also effect the BluePill
If I call Serial.end(); and then Serial.begin() the USB no longer works.
This seems to be because the code in usb_cdcacm_enable does not really reset the USB, it just “presents” the device to the host
void usb_cdcacm_enable(gpio_dev *disc_dev, uint8 disc_bit) {
/* Present ourselves to the host. Writing 0 to "disc" pin must
* pull USB_DP pin up while leaving USB_DM pulled down by the
* transceiver. See USB 2.0 spec, section 7.1.7.3. */
gpio_set_mode(disc_dev, disc_bit, GPIO_OUTPUT_PP);
gpio_write_bit(disc_dev, disc_bit, 0);
/* Initialize the USB peripheral. */
usb_init_usblib(USBLIB, ep_int_in, ep_int_out);
}
does set the usb disc bit
gpio_write_bit(disc_dev, disc_bit, 1);

![[Pending Enhancement] RTC values resetting](https://sparklogic.ru/wp-content/uploads/2019/11/nucleo-l476rg-zestaw-startowy-z-mikrokontrolerem-z-rodziny-stm32-stm32l476-90x90.jpg)