Detect UART framing and parity errors and emit them as events on the Serial object

This commit is contained in:
Gordon Williams 2015-11-26 18:07:04 +00:00
parent 8492c5ed0f
commit a35b05c4a9
6 changed files with 90 additions and 19 deletions

View File

@ -5,6 +5,7 @@
Fix lost character on Espruino Startup (fix #704)
Fix duplicated characters when USB+USART IRQs fire at the same time (fix #635)
Fixed Serial.find(...)
Detect UART framing and parity errors and emit them as events on the Serial object
1v82 : Fix debugger regression (where quit/reset/etc don't exit properly)
Fix wakeup when setDeepSleep used at startup (fix #645)

View File

@ -55,6 +55,13 @@ typedef enum {
EV_SERIAL5,
EV_SERIAL6,
EV_SERIAL_MAX = EV_SERIAL6,
EV_SERIAL1_STATUS, // Used to store serial status info
EV_SERIAL2_STATUS,
EV_SERIAL3_STATUS,
EV_SERIAL4_STATUS,
EV_SERIAL5_STATUS,
EV_SERIAL6_STATUS,
EV_SERIAL_STATUS_MAX = EV_SERIAL6_STATUS,
EV_SPI1,
EV_SPI2,
EV_SPI3,
@ -71,6 +78,9 @@ typedef enum {
EV_CHARS_ONE = EV_TYPE_MASK+1,
EV_CHARS_SHIFT = GET_BIT_NUMBER(EV_CHARS_ONE),
EV_CHARS_MASK = 3 * EV_CHARS_ONE, // see IOEVENT_MAXCHARS
// ----------------------------------------- SERIAL STATUS
EV_SERIAL_STATUS_FRAMING_ERR = EV_TYPE_MASK+1,
EV_SERIAL_STATUS_PARITY_ERR = EV_SERIAL_STATUS_FRAMING_ERR<<1,
// ----------------------------------------- WATCH EVENTS
// if the pin we're watching is high, the handler sets this
EV_EXTI_IS_HIGH = EV_TYPE_MASK+1,
@ -79,12 +89,16 @@ typedef enum {
// Return true if the device is a USART
#define DEVICE_IS_USART(X) (((X)>=EV_SERIAL_START) && ((X)<=EV_SERIAL_MAX))
#define DEVICE_IS_USART_STATUS(X) (((X)>=EV_SERIAL1_STATUS) && ((X)<=EV_SERIAL_STATUS_MAX))
// Return true if the device is an SPI.
#define DEVICE_IS_SPI(X) (((X)>=EV_SPI1) && ((X)<=EV_SPI_MAX))
#define DEVICE_IS_I2C(X) (((X)>=EV_I2C1) && ((X)<=EV_I2C_MAX))
#define DEVICE_IS_EXTI(X) (((X)>=EV_EXTI0) && ((X)<=EV_EXTI_MAX))
#define IOEVENTFLAGS_SERIAL_TO_SERIAL_STATUS(X) ((X) + EV_SERIAL1_STATUS - EV_SERIAL1)
#define IOEVENTFLAGS_SERIAL_STATUS_TO_SERIAL(X) ((X) + EV_SERIAL1 - EV_SERIAL1_STATUS)
#define IOEVENTFLAGS_GETTYPE(X) ((X)&EV_TYPE_MASK)
#define IOEVENTFLAGS_GETCHARS(X) ((((X)&EV_CHARS_MASK)>>EV_CHARS_SHIFT)+1)
#define IOEVENTFLAGS_SETCHARS(X,CHARS) ((X)=(((X)&(IOEventFlags)~EV_CHARS_MASK) | (((CHARS)-1)<<EV_CHARS_SHIFT)))

View File

@ -1458,6 +1458,13 @@ void jsiQueueObjectCallbacks(JsVar *object, const char *callbackName, JsVar **ar
jsvUnLock(callback);
}
void jsiExecuteObjectCallbacks(JsVar *object, const char *callbackName, JsVar **args, int argCount) {
JsVar *callback = jsvObjectGetChild(object, callbackName, 0);
if (!callback) return;
jsiExecuteEventCallback(object, callback, (unsigned int)argCount, args);
jsvUnLock(callback);
}
void jsiExecuteEvents() {
bool hasEvents = !jsvArrayIsEmpty(events);
if (hasEvents) jsiSetBusy(BUSY_INTERACTIVE, true);
@ -1635,6 +1642,16 @@ void jsiIdle() {
jsiHandleIOEventForUSART(usartClass, &event);
}
jsvUnLock(usartClass);
} else if (DEVICE_IS_USART_STATUS(eventType)) {
// ------------------------------------------------------------------------ SERIAL STATUS CALLBACK
JsVar *usartClass = jsvSkipNameAndUnLock(jsiGetClassNameFromDevice(IOEVENTFLAGS_GETTYPE(IOEVENTFLAGS_SERIAL_STATUS_TO_SERIAL(event.flags))));
if (jsvIsObject(usartClass)) {
if (event.flags & EV_SERIAL_STATUS_FRAMING_ERR)
jsiExecuteObjectCallbacks(usartClass, JS_EVENT_PREFIX"framing", 0, 0);
if (event.flags & EV_SERIAL_STATUS_PARITY_ERR)
jsiExecuteObjectCallbacks(usartClass, JS_EVENT_PREFIX"parity", 0, 0);
}
jsvUnLock(usartClass);
} else if (DEVICE_IS_EXTI(eventType)) { // ---------------------------------------------------------------- PIN WATCH
// we have an event... find out what it was for...
// Check everything in our Watch array

View File

@ -51,6 +51,8 @@ void jsiQueueEvents(JsVar *object, JsVar *callback, JsVar **args, int argCount);
bool jsiObjectHasCallbacks(JsVar *object, const char *callbackName);
/// Queue up callbacks for other things (touchscreen? network?)
void jsiQueueObjectCallbacks(JsVar *object, const char *callbackName, JsVar **args, int argCount);
/// Execute callbacks straight away (like jsiQueueObjectCallbacks, but without queueing)
void jsiExecuteObjectCallbacks(JsVar *object, const char *callbackName, JsVar **args, int argCount);
/// Execute the given function/string/array of functions and return true on success, false on failure (break during execution)
bool jsiExecuteEventCallback(JsVar *thisVar, JsVar *callbackVar, unsigned int argCount, JsVar **argPtr);
/// Same as above, but with a JsVarArray (this calls jsiExecuteEventCallback, so use jsiExecuteEventCallback where possible)

View File

@ -33,8 +33,35 @@ Methods may be called on the USB, Serial1, Serial2, Serial3, Serial4, Serial5 an
["data","JsVar","A string containing one or more characters of received data"]
]
}
The 'data' event is called when data is received. If a handler is defined with `X.on('data', function(data) { ... })` then it will be called, otherwise data will be stored in an internal buffer, where it can be retrieved with `X.read()`
The `data` event is called when data is received. If a handler is defined with `X.on('data', function(data) { ... })` then it will be called, otherwise data will be stored in an internal buffer, where it can be retrieved with `X.read()`
*/
/*JSON{
"type" : "event",
"class" : "Serial",
"name" : "framing"
}
The `framing` event is called when there was activity on the input to the UART
but the `STOP` bit wasn't in the correct place. This is either because there
was noise on the line, or the line has been pulled to 0 for a long period
of time.
**Note:** Even though there was an error, the byte will still be received and
passed to the `data` handler.
*/
/*JSON{
"type" : "event",
"class" : "Serial",
"name" : "parity"
}
The `parity` event is called when the UART was configured with a parity bit,
and this doesn't match the bits that have actually been received.
**Note:** Even though there was an error, the byte will still be received and
passed to the `data` handler.
*/
// this is created in jsiIdle based on EV_SERIALx_STATUS ecents
/*JSON{
"type" : "staticmethod",
"class" : "Serial",

View File

@ -281,25 +281,35 @@ void RTC_WKUP_IRQHandler(void)
#endif
static void USART_IRQHandler(USART_TypeDef *USART, IOEventFlags device) {
if (USART_GetFlagStatus(USART, USART_FLAG_FE) != RESET) {
// If we have a framing error, push status info onto the event queue
jshPushIOEvent(
IOEVENTFLAGS_SERIAL_TO_SERIAL_STATUS(device) | EV_SERIAL_STATUS_FRAMING_ERR, 0);
}
if (USART_GetFlagStatus(USART, USART_FLAG_PE) != RESET) {
// If we have a parity error, push status info onto the event queue
jshPushIOEvent(
IOEVENTFLAGS_SERIAL_TO_SERIAL_STATUS(device) | EV_SERIAL_STATUS_PARITY_ERR, 0);
}
if(USART_GetITStatus(USART, USART_IT_RXNE) != RESET) {
/* Clear the USART Receive interrupt */
USART_ClearITPendingBit(USART, USART_IT_RXNE);
/* Read one byte from the receive data register */
jshPushIOCharEvent(device, (char)USART_ReceiveData(USART));
}
/* If overrun condition occurs, clear the ORE flag and recover communication */
if (USART_GetFlagStatus(USART, USART_FLAG_ORE) != RESET)
{
(void)USART_ReceiveData(USART);
}
if(USART_GetITStatus(USART, USART_IT_TXE) != RESET) {
/* If we have other data to send, send it */
int c = jshGetCharToTransmit(device);
if (c >= 0) {
USART_SendData(USART, (uint16_t)c);
} else
USART_ITConfig(USART, USART_IT_TXE, DISABLE);
}
/* Clear the USART Receive interrupt */
USART_ClearITPendingBit(USART, USART_IT_RXNE);
/* Read one byte from the receive data register */
jshPushIOCharEvent(device, (char)USART_ReceiveData(USART));
}
/* If overrun condition occurs, clear the ORE flag and recover communication */
if (USART_GetFlagStatus(USART, USART_FLAG_ORE) != RESET)
{
(void)USART_ReceiveData(USART);
}
if(USART_GetITStatus(USART, USART_IT_TXE) != RESET) {
/* If we have other data to send, send it */
int c = jshGetCharToTransmit(device);
if (c >= 0) {
USART_SendData(USART, (uint16_t)c);
} else
USART_ITConfig(USART, USART_IT_TXE, DISABLE);
}
}
void USART1_IRQHandler(void) {