diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst index 8f417f735b..46e6d08cf1 100644 --- a/docs/library/pyb.CAN.rst +++ b/docs/library/pyb.CAN.rst @@ -126,7 +126,7 @@ Methods Return value: buffer of data bytes. -.. method:: can.send(send, addr, \*, timeout=5000) +.. method:: can.send(send, addr, \*, timeout=0) Send a message on the bus: @@ -134,6 +134,12 @@ Methods - ``addr`` is the address to send to - ``timeout`` is the timeout in milliseconds to wait for the send. + If timeout is 0 the message is placed in a buffer in one of three hardware + buffers and the method returns immediately. If all three buffers are in use + an exception is thrown. If timeout is not 0, the method waits until the + message is transmitted. If the message can't be transmitted within the + specified time an exception is thrown. + Return value: ``None``. .. method:: can.rxcallback(fifo, fun) diff --git a/stmhal/can.c b/stmhal/can.c index 02afbbf828..f60f79532d 100644 --- a/stmhal/can.c +++ b/stmhal/can.c @@ -170,6 +170,96 @@ STATIC void can_clearfilter(uint32_t f) { HAL_CAN_ConfigFilter(NULL, &filter); } +// We have our own version of CAN transmit so we can handle Timeout=0 correctly. +STATIC HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout) { + uint32_t transmitmailbox; + uint32_t tickstart; + uint32_t rqcpflag; + uint32_t txokflag; + + // Check the parameters + assert_param(IS_CAN_IDTYPE(hcan->pTxMsg->IDE)); + assert_param(IS_CAN_RTR(hcan->pTxMsg->RTR)); + assert_param(IS_CAN_DLC(hcan->pTxMsg->DLC)); + + // Select one empty transmit mailbox + if ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) { + transmitmailbox = CAN_TXMAILBOX_0; + rqcpflag = CAN_FLAG_RQCP0; + txokflag = CAN_FLAG_TXOK0; + } else if ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) { + transmitmailbox = CAN_TXMAILBOX_1; + rqcpflag = CAN_FLAG_RQCP1; + txokflag = CAN_FLAG_TXOK1; + } else if ((hcan->Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2) { + transmitmailbox = CAN_TXMAILBOX_2; + rqcpflag = CAN_FLAG_RQCP2; + txokflag = CAN_FLAG_TXOK2; + } else { + transmitmailbox = CAN_TXSTATUS_NOMAILBOX; + } + + if (transmitmailbox != CAN_TXSTATUS_NOMAILBOX) { + // Set up the Id + hcan->Instance->sTxMailBox[transmitmailbox].TIR &= CAN_TI0R_TXRQ; + if (hcan->pTxMsg->IDE == CAN_ID_STD) { + assert_param(IS_CAN_STDID(hcan->pTxMsg->StdId)); + hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->StdId << 21) | \ + hcan->pTxMsg->RTR); + } else { + assert_param(IS_CAN_EXTID(hcan->pTxMsg->ExtId)); + hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->ExtId << 3) | \ + hcan->pTxMsg->IDE | \ + hcan->pTxMsg->RTR); + } + + // Set up the DLC + hcan->pTxMsg->DLC &= (uint8_t)0x0000000F; + hcan->Instance->sTxMailBox[transmitmailbox].TDTR &= (uint32_t)0xFFFFFFF0; + hcan->Instance->sTxMailBox[transmitmailbox].TDTR |= hcan->pTxMsg->DLC; + + // Set up the data field + hcan->Instance->sTxMailBox[transmitmailbox].TDLR = (((uint32_t)hcan->pTxMsg->Data[3] << 24) | + ((uint32_t)hcan->pTxMsg->Data[2] << 16) | + ((uint32_t)hcan->pTxMsg->Data[1] << 8) | + ((uint32_t)hcan->pTxMsg->Data[0])); + hcan->Instance->sTxMailBox[transmitmailbox].TDHR = (((uint32_t)hcan->pTxMsg->Data[7] << 24) | + ((uint32_t)hcan->pTxMsg->Data[6] << 16) | + ((uint32_t)hcan->pTxMsg->Data[5] << 8) | + ((uint32_t)hcan->pTxMsg->Data[4])); + // Request transmission + hcan->Instance->sTxMailBox[transmitmailbox].TIR |= CAN_TI0R_TXRQ; + + if (Timeout == 0) { + return HAL_OK; + } + + // Get tick + tickstart = HAL_GetTick(); + // Check End of transmission flag + while (!(__HAL_CAN_TRANSMIT_STATUS(hcan, transmitmailbox))) { + // Check for the Timeout + if (Timeout != HAL_MAX_DELAY) { + if ((HAL_GetTick() - tickstart) > Timeout) { + // When the timeout expires, we try to abort the transmission of the packet + __HAL_CAN_CANCEL_TRANSMIT(hcan, transmitmailbox); + while (!__HAL_CAN_GET_FLAG(hcan, rqcpflag)) { + } + if (__HAL_CAN_GET_FLAG(hcan, txokflag)) { + // The abort attempt failed and the message was sent properly + return HAL_OK; + } else { + return HAL_TIMEOUT; + } + } + } + } + return HAL_OK; + } else { + return HAL_BUSY; + } +} + /******************************************************************************/ // Micro Python bindings @@ -348,7 +438,7 @@ STATIC mp_obj_t pyb_can_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ static const mp_arg_t allowed_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; // parse args @@ -380,7 +470,7 @@ STATIC mp_obj_t pyb_can_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ tx_msg.Data[i] = ((byte*)bufinfo.buf)[i]; // Data is uint32_t but holds only 1 byte } self->can.pTxMsg = &tx_msg; - HAL_StatusTypeDef status = HAL_CAN_Transmit(&self->can, args[2].u_int); + HAL_StatusTypeDef status = CAN_Transmit(&self->can, args[2].u_int); if (status != HAL_OK) { mp_hal_raise(status); diff --git a/tests/pyb/can.py b/tests/pyb/can.py index 132da23069..5fd4d39d05 100644 --- a/tests/pyb/can.py +++ b/tests/pyb/can.py @@ -1,4 +1,5 @@ from pyb import CAN +import pyb CAN.initfilterbanks(14) can = CAN(1) @@ -11,19 +12,19 @@ print(can.any(0)) # Catch all filter can.setfilter(0, CAN.MASK16, 0, (0, 0, 0, 0)) -can.send('abcd', 123) +can.send('abcd', 123, timeout=5000) print(can.any(0)) print(can.recv(0)) -can.send('abcd', -1) +can.send('abcd', -1, timeout=5000) print(can.recv(0)) -can.send('abcd', 0x7FF + 1) +can.send('abcd', 0x7FF + 1, timeout=5000) print(can.recv(0)) # Test too long message try: - can.send('abcdefghi', 0x7FF) + can.send('abcdefghi', 0x7FF, timeout=5000) except ValueError: print('passed') else: @@ -39,7 +40,7 @@ can.setfilter(0, CAN.MASK32, 0, (0, 0)) print(can) try: - can.send('abcde', 0x7FF + 1) + can.send('abcde', 0x7FF + 1, timeout=5000) except ValueError: print('failed') else: @@ -95,17 +96,17 @@ def cb1a(bus, reason): can.rxcallback(0, cb0) can.rxcallback(1, cb1) -can.send('11111111',1) -can.send('22222222',2) -can.send('33333333',3) +can.send('11111111',1, timeout=5000) +can.send('22222222',2, timeout=5000) +can.send('33333333',3, timeout=5000) can.rxcallback(0, cb0a) -can.send('44444444',4) +can.send('44444444',4, timeout=5000) -can.send('55555555',5) -can.send('66666666',6) -can.send('77777777',7) +can.send('55555555',5, timeout=5000) +can.send('66666666',6, timeout=5000) +can.send('77777777',7, timeout=5000) can.rxcallback(1, cb1a) -can.send('88888888',8) +can.send('88888888',8, timeout=5000) print(can.recv(0)) print(can.recv(0)) @@ -114,9 +115,39 @@ print(can.recv(1)) print(can.recv(1)) print(can.recv(1)) -can.send('11111111',1) -can.send('55555555',5) +can.send('11111111',1, timeout=5000) +can.send('55555555',5, timeout=5000) print(can.recv(0)) print(can.recv(1)) +del can + +# Testing asyncronous send +can = CAN(1, CAN.LOOPBACK) +can.setfilter(0, CAN.MASK16, 0, (0, 0, 0, 0)) + +while can.any(0): + can.recv(0) + +can.send('abcde', 1, timeout=0) +print(can.any(0)) +while not can.any(0): + pass + +print(can.recv(0)) + +try: + can.send('abcde', 2, timeout=0) + can.send('abcde', 3, timeout=0) + can.send('abcde', 4, timeout=0) + can.send('abcde', 5, timeout=0) +except OSError as e: + if str(e) == '16': + print('passed') + else: + print('failed') + +pyb.delay(500) +while can.any(0): + print(can.recv(0)) diff --git a/tests/pyb/can.py.exp b/tests/pyb/can.py.exp index b0ede7b9f4..845f6d5ba1 100644 --- a/tests/pyb/can.py.exp +++ b/tests/pyb/can.py.exp @@ -32,3 +32,9 @@ cb1a pending (1, 0, 0, b'11111111') (5, 0, 0, b'55555555') +False +(1, 0, 0, b'abcde') +passed +(2, 0, 0, b'abcde') +(3, 0, 0, b'abcde') +(4, 0, 0, b'abcde')