Skip to content

Commit 164496a

Browse files
committed
nRF52840: USB Serial works even for big sends, and device swaps to USB automatically
1 parent 0fd01d6 commit 164496a

File tree

6 files changed

+110
-85
lines changed

6 files changed

+110
-85
lines changed

ChangeLog

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Raspberry pi pin numbering the same if using wiringPi or filesystem GPIO (fix #1673)
1515
nRF52: Peer manager init errors from Nordic libs now not fatal
1616
SDK15: Writing to flash now works
17+
nRF52840: USB Serial works even for big sends, and device swaps to USB automatically
1718

1819
2v03 : nRF5x: Fix issue when calling NRF.setAdvertising while connected via BLE (fix #1659)
1920
nRF5x: 'dump()' not outputs `NRF.setSecurity` line if it has been called.

misc/45-espruino.rules

+2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
ATTRS{idProduct}=="5740", ATTRS{idVendor}=="0483", ENV{ID_MM_DEVICE_IGNORE}="1", MODE="0666", GROUP="plugdev"
44
# Nordic kits
55
ATTRS{idProduct}=="1015", ATTRS{idVendor}=="1366", ENV{ID_MM_DEVICE_IGNORE}="1", MODE="0666", GROUP="plugdev"
6+
# Nordic nRF52840 USB UART
7+
ATTRS{idProduct}=="1915", ATTRS{idVendor}=="520f", ENV{ID_MM_DEVICE_IGNORE}="1", MODE="0666", GROUP="plugdev"
68
# Microbit
79
ATTRS{idProduct}=="0204", ATTRS{idVendor}=="0d28", ENV{ID_MM_DEVICE_IGNORE}="1", MODE="0666", GROUP="plugdev"

src/jsdevices.c

+1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ void jshTransmit(
213213
jsErrorFlags |= JSERR_BUFFER_FULL;
214214
return;
215215
}
216+
jshBusyIdle();
216217
#ifdef USB
217218
// just in case USB was unplugged while we were waiting!
218219
if (!jshIsUSBSERIALConnected()) jshTransmitClearDevice(EV_USBSERIAL);

src/jshardware.h

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ void jshReset();
4040
/** Code that is executed each time around the idle loop. Prod watchdog timers here,
4141
* and on platforms without GPIO interrupts you can check watched Pins for changes. */
4242
void jshIdle();
43+
44+
/// Called when Espruino is busy waiting (eg for data to send)
45+
void jshBusyIdle();
46+
4347
/** Enter sleep mode for the given period of time. Can be woken up by interrupts.
4448
* If time is 0xFFFFFFFFFFFFFFFF then go to sleep without setting a timer to wake
4549
* up.

src/jshardware_common.c

+4
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ void jshI2CInitInfo(JshI2CInfo *inf) {
4343
inf->bitrate = 100000;
4444
inf->started = false;
4545
}
46+
47+
// Only define this if it's not used elsewhere
48+
__attribute__((weak)) void jshBusyIdle() {
49+
}

targets/nrf5x/jshardware.c

+98-85
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ void WDT_IRQHandler() {
8989
#include "nrf_drv_clock.h"
9090
#include "nrf_drv_power.h"
9191

92+
// TODO: It'd be nice if APP_USBD_CONFIG_EVENT_QUEUE_ENABLE could be 0 but it seems cdc_acm_user_ev_handler isn't called if it is
93+
9294
/**
9395
* @brief Enable power USB detection
9496
*
@@ -130,6 +132,7 @@ static char m_tx_buffer[NRF_DRV_USBD_EPSIZE];
130132
* */
131133
static bool m_usb_connected = false;
132134
static bool m_usb_open = false;
135+
static bool m_usb_transmitting = false;
133136

134137
/**
135138
* @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t (headphones)
@@ -142,38 +145,50 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
142145

143146
switch (event)
144147
{
145-
case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
146-
{
147-
jsiConsolePrintf("APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN\n");
148+
case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN: {
149+
//jsiConsolePrintf("APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN\n");
148150
m_usb_open = true;
151+
m_usb_transmitting = false;
149152
/*Setup first transfer*/
150153
ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
151154
m_rx_buffer,
152155
sizeof(m_rx_buffer));
153156
UNUSED_VARIABLE(ret);
157+
// USB connected - so move console device over to it
158+
if (jsiGetConsoleDevice()!=EV_LIMBO) {
159+
if (!jsiIsConsoleDeviceForced())
160+
jsiSetConsoleDevice(EV_USBSERIAL, false);
161+
}
154162
break;
155163
}
156-
case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
157-
jsiConsolePrintf("APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE\n");
164+
case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE: {
165+
//jsiConsolePrintf("APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE\n");
158166
m_usb_open = false;
167+
m_usb_transmitting = false;
168+
// USB disconnected, move device back to the default
169+
if (!jsiIsConsoleDeviceForced() && jsiGetConsoleDevice()==EV_USBSERIAL)
170+
jsiSetConsoleDevice(jsiGetPreferredConsoleDevice(), false);
171+
jshTransmitClearDevice(EV_USBSERIAL); // clear the transmit queue
159172
break;
160-
case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
161-
// TODO: queue extra transmit here
173+
}
174+
case APP_USBD_CDC_ACM_USER_EVT_TX_DONE: {
175+
// TX finished - queue extra transmit here
176+
m_usb_transmitting = false;
177+
jshUSARTKick(EV_USBSERIAL);
162178
break;
163-
case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
164-
{
179+
}
180+
case APP_USBD_CDC_ACM_USER_EVT_RX_DONE: {
165181
ret_code_t ret;
166-
do
167-
{
168-
/*Get amount of data transfered*/
169-
size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
170-
jshPushIOCharEvents(EV_USBSERIAL, m_rx_buffer, size);
182+
do {
183+
/*Get amount of data transfered*/
184+
size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
185+
jshPushIOCharEvents(EV_USBSERIAL, m_rx_buffer, size);
171186

172187

173-
/*Setup next transfer*/
174-
ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
175-
m_rx_buffer,
176-
sizeof(m_rx_buffer));
188+
/*Setup next transfer*/
189+
ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
190+
m_rx_buffer,
191+
sizeof(m_rx_buffer));
177192
} while (ret == NRF_SUCCESS);
178193
break;
179194
}
@@ -185,42 +200,39 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
185200
static void usbd_user_ev_handler(app_usbd_event_type_t event)
186201
{
187202
jshHadEvent();
188-
switch (event)
189-
{
190-
case APP_USBD_EVT_DRV_SUSPEND:
191-
jsiConsolePrintf("APP_USBD_EVT_DRV_SUSPEND\n");
192-
break;
193-
case APP_USBD_EVT_DRV_RESUME:
194-
jsiConsolePrintf("APP_USBD_EVT_DRV_RESUME\n");
195-
break;
196-
case APP_USBD_EVT_STARTED:
197-
jsiConsolePrintf("APP_USBD_EVT_STARTED\n");
198-
break;
199-
case APP_USBD_EVT_STOPPED:
200-
jsiConsolePrintf("APP_USBD_EVT_STOPPED\n");
201-
app_usbd_disable();
202-
break;
203-
case APP_USBD_EVT_POWER_DETECTED:
204-
jsiConsolePrintf("APP_USBD_EVT_POWER_DETECTED\n");
205-
206-
if (!nrf_drv_usbd_is_enabled())
207-
{
208-
app_usbd_enable();
209-
}
210-
break;
211-
case APP_USBD_EVT_POWER_REMOVED:
212-
jsiConsolePrintf("APP_USBD_EVT_POWER_REMOVED\n");
213-
m_usb_connected = false;
214-
app_usbd_stop();
215-
break;
216-
case APP_USBD_EVT_POWER_READY:
217-
jsiConsolePrintf("APP_USBD_EVT_POWER_READY\n");
218-
app_usbd_start();
219-
m_usb_connected = true;
220-
break;
221-
default:
222-
break;
223-
}
203+
switch (event)
204+
{
205+
case APP_USBD_EVT_DRV_SUSPEND:
206+
//jsiConsolePrintf("APP_USBD_EVT_DRV_SUSPEND\n");
207+
break;
208+
case APP_USBD_EVT_DRV_RESUME:
209+
//jsiConsolePrintf("APP_USBD_EVT_DRV_RESUME\n");
210+
break;
211+
case APP_USBD_EVT_STARTED:
212+
//jsiConsolePrintf("APP_USBD_EVT_STARTED\n");
213+
break;
214+
case APP_USBD_EVT_STOPPED:
215+
//jsiConsolePrintf("APP_USBD_EVT_STOPPED\n");
216+
app_usbd_disable();
217+
break;
218+
case APP_USBD_EVT_POWER_DETECTED:
219+
//jsiConsolePrintf("APP_USBD_EVT_POWER_DETECTED\n");
220+
if (!nrf_drv_usbd_is_enabled())
221+
app_usbd_enable();
222+
break;
223+
case APP_USBD_EVT_POWER_REMOVED:
224+
//jsiConsolePrintf("APP_USBD_EVT_POWER_REMOVED\n");
225+
m_usb_connected = false;
226+
app_usbd_stop();
227+
break;
228+
case APP_USBD_EVT_POWER_READY:
229+
//jsiConsolePrintf("APP_USBD_EVT_POWER_READY\n");
230+
app_usbd_start();
231+
m_usb_connected = true;
232+
break;
233+
default:
234+
break;
235+
}
224236
}
225237

226238
#endif
@@ -296,7 +308,7 @@ void SysTick_Handler(void) {
296308
ticksSinceStart++;
297309
/* One second after start, call jsinteractive. This is used to swap
298310
* to USB (if connected), or the Serial port. */
299-
if (ticksSinceStart == 5) {
311+
if (ticksSinceStart == 6) {
300312
jsiOneSecondAfterStartup();
301313
}
302314
}
@@ -479,15 +491,11 @@ void jshInit() {
479491

480492
ret = nrf_drv_clock_init();
481493
APP_ERROR_CHECK(ret);
482-
483-
jsiConsolePrintf("USBD init\n");
484494
ret = app_usbd_init(&usbd_config);
485495
APP_ERROR_CHECK(ret);
486-
jsiConsolePrintf("ok\n");
487496
app_usbd_class_inst_t const * class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);
488497
ret = app_usbd_class_append(class_cdc_acm);
489498
APP_ERROR_CHECK(ret);
490-
jsiConsolePrintf("cdc ok\n");
491499
#endif
492500

493501
jsble_init();
@@ -516,21 +524,15 @@ void jshInit() {
516524
#endif
517525

518526
#ifdef NRF_USB
519-
if (USBD_POWER_DETECTION)
520-
{
521-
jsiConsolePrintf("app_usbd_power_events_enable\n");
522-
ret = app_usbd_power_events_enable();
523-
APP_ERROR_CHECK(ret);
524-
}
525-
else
526-
{
527-
jsiConsolePrintf("No USB power detection enabled\nStarting USB now\n");
528-
529-
app_usbd_enable();
530-
app_usbd_start();
531-
}
532-
533-
jsiConsolePrintf("USB init done\n");
527+
if (USBD_POWER_DETECTION) {
528+
//jsiConsolePrintf("app_usbd_power_events_enable\n");
529+
ret = app_usbd_power_events_enable();
530+
APP_ERROR_CHECK(ret);
531+
} else {
532+
//jsiConsolePrintf("No USB power detection enabled\nStarting USB now\n");
533+
app_usbd_enable();
534+
app_usbd_start();
535+
}
534536
#endif
535537

536538

@@ -551,18 +553,15 @@ void jshKill() {
551553

552554
// stuff to do on idle
553555
void jshIdle() {
554-
#ifdef NRF_USB
556+
#if defined(NRF_USB)
555557
while (app_usbd_event_queue_process()); /* Nothing to do */
558+
#endif
559+
}
556560

557-
int l = 0;
558-
int c;
559-
while ((l<sizeof(m_tx_buffer)) && ((c = jshGetCharToTransmit(EV_USBSERIAL))>=0))
560-
m_tx_buffer[l++] = c;
561-
if (l) {
562-
// TODO: check return value?
563-
// This is asynchronous call. User should wait for @ref APP_USBD_CDC_ACM_USER_EVT_TX_DONE event
564-
uint32_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, l);
565-
}
561+
void jshBusyIdle() {
562+
// When busy waiting for USB data to send we still have to poll USB :(
563+
#if defined(NRF_USB)
564+
while (app_usbd_event_queue_process()); /* Nothing to do */
566565
#endif
567566
}
568567

@@ -1296,6 +1295,20 @@ void jshUSARTKick(IOEventFlags device) {
12961295
while (jshGetCharToTransmit(EV_SERIAL1)>=0);
12971296
}
12981297
}
1298+
#ifdef USB
1299+
if (device == EV_USBSERIAL && m_usb_open && !m_usb_transmitting) {
1300+
int l = 0;
1301+
int c;
1302+
while ((l<sizeof(m_tx_buffer)) && ((c = jshGetCharToTransmit(EV_USBSERIAL))>=0))
1303+
m_tx_buffer[l++] = c;
1304+
if (l) {
1305+
// This is asynchronous call. We wait for @ref APP_USBD_CDC_ACM_USER_EVT_TX_DONE event
1306+
uint32_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, l);
1307+
APP_ERROR_CHECK(ret);
1308+
m_usb_transmitting = true;
1309+
}
1310+
}
1311+
#endif
12991312
}
13001313

13011314

@@ -1610,7 +1623,7 @@ bool jshSleep(JsSysTime timeUntilWake) {
16101623
while (!hadEvent) {
16111624
sd_app_evt_wait(); // Go to sleep, wait to be woken up
16121625
jshGetSystemTime(); // check for RTC overflows
1613-
#ifdef NRF_USB
1626+
#if defined(NRF_USB)
16141627
while (app_usbd_event_queue_process()); /* Nothing to do */
16151628
#endif
16161629
}

0 commit comments

Comments
 (0)