diff --git a/Inc/mmr_can.h b/Inc/mmr_can.h index 057c48f..c41bf76 100644 --- a/Inc/mmr_can.h +++ b/Inc/mmr_can.h @@ -6,6 +6,7 @@ #include "mmr_can_includes.h" #include "mmr_can_types.h" #include "mmr_can_optimize.h" +#include "mmr_can_binary_literals.h" #ifndef MMR_CAN_RX_FIFO #define MMR_CAN_RX_FIFO CAN_RX_FIFO0 @@ -23,15 +24,11 @@ #define MMR_CAN_RX_INTERRUPT CAN_IT_RX_FIFO1_MSG_PENDING #endif -#ifndef MMR_CAN_ID -#define MMR_CAN_ID 0x103 +#ifndef MMR_CAN_MAX_DATA_LENGTH +#define MMR_CAN_MAX_DATA_LENGTH 8 #endif -#ifndef MMR_CAN_BUFFER_LEN -#define MMR_CAN_BUFFER_LEN 8 -#endif - -typedef uint8_t CanRxBuffer[MMR_CAN_BUFFER_LEN]; +typedef uint8_t CanRxBuffer[MMR_CAN_MAX_DATA_LENGTH]; typedef struct { @@ -47,7 +44,10 @@ typedef struct { * The filter bank to use * * In systems with more than one CAN interface, - * this must go from 0 to slaveBankStart. + * this must go from 0 to slaveBankStart for the master CAN + * and from slaveBankStart to 27 for the slave CAN + * + * Master CAN is usually CAN1, while slave CAN is CAN2 */ CanFilterBank bank; @@ -63,13 +63,8 @@ typedef struct { CanFilterMask idMask; } MmrCanFilterSettings; + typedef struct { - /** - * The id of the CAN interface that will receive the packet. - * - * If you are using the CANbus in loopback mode, it can be the - * id that the sender is using. - */ CanId remoteId; CanMailbox *mailbox; uint8_t *data; @@ -77,13 +72,48 @@ typedef struct { } MmrCanPacket; +/** + * @brief + * These values can be appended to the extended-id + * portion of the CAN bus message (that is, the lower 5 bits + * of the standard id) + * + * They are used to check if a message is either standalone + * or split into multiple frames + * + * Multi-frame messages have a higher priority over normal ones + */ +typedef enum { + MMR_CAN_MESSAGE_TYPE_MULTI_FRAME = B_(0010), + MMR_CAN_MESSAGE_TYPE_MULTI_FRAME_END = B_(0011), + MMR_CAN_MESSAGE_TYPE_NORMAL = B_(1000), +} MmrCanMessageType; + + +/** + * @brief + * Represents a CAN message + */ +typedef struct { + CanId senderId; + void *store; +} MmrCanMessage; + + #define MMR_CAN_FilterConfigDefault(phcan) \ MMR_CAN_FilterConfig(phcan, MMR_CAN_GetDefaultFilterSettings()) + +HalStatus MMR_CAN_BasicSetupAndStart(CanHandle *hcan); HalStatus MMR_CAN_FilterConfig(CanHandle *hcan, MmrCanFilterSettings settings); CanFilterMask MMR_CAN_AlignStandardMask(CanFilterMask baseMask); MmrCanFilterSettings MMR_CAN_GetDefaultFilterSettings(); HalStatus MMR_CAN_Send(CanHandle *hcan, MmrCanPacket packet); +HalStatus MMR_CAN_Receive(CanHandle *hcan, MmrCanMessage *result); + + +bool MMR_CAN_IsMultiFrame(CanRxHeader *header); +bool MMR_CAN_IsMultiFrameEnd(CanRxHeader *header); #endif /* INC_MMR_CAN_H_ */ diff --git a/Inc/mmr_can_binary_literals.h b/Inc/mmr_can_binary_literals.h new file mode 100644 index 0000000..8cebca3 --- /dev/null +++ b/Inc/mmr_can_binary_literals.h @@ -0,0 +1,64 @@ +#ifndef INC_MMR_CAN_BINARY_LITERALS_H_ +#define INC_MMR_CAN_BINARY_LITERALS_H_ + +/** + * @brief + * These set of macros (B_, B8_, B16_, B32_) allow one + * to convert bit literals to their hexadecimal representation + * + * They are useful when it is needed to convey exactly which bits + * are being set for a particular register, message etc... + * + * @example + * B_(1010) = 0xA + * B8_(1111, 1111) = 0xFF + * + * ----------------------------------------------------------- + * Macros taken from https://stackoverflow.com/a/7577517 and + * sligthly readapted + */ + +#define __B_0000 0 +#define __B_0001 1 +#define __B_0010 2 +#define __B_0011 3 +#define __B_0100 4 +#define __B_0101 5 +#define __B_0110 6 +#define __B_0111 7 +#define __B_1000 8 +#define __B_1001 9 +#define __B_1010 a +#define __B_1011 b +#define __B_1100 c +#define __B_1101 d +#define __B_1110 e +#define __B_1111 f + +#define __B2H(bits) __B_ ## bits +#define _B2H(bits) __B2H(bits) +#define __HEX(n) 0x ## n +#define _HEX(n) __HEX(n) +#define __CCAT(a,b) a ## b +#define _CCAT(a,b) __CCAT(a,b) + +#define B_(a) _HEX(_B2H(a)) +#define B8_(a, b) _HEX(_CCAT(_B2H(a), _B2H(b))) +#define B16_(a, b, c, d) \ + _HEX(_CCAT( \ + _CCAT(_B2H(a), _B2H(b)), \ + _CCAT(_B2H(c), _B2H(d)) \ + )) + +#define B32_(a, b, c, d, e, f, g, h) \ + _HEX(_CCAT( \ + _CCAT( \ + _CCAT(_B2H(a), _B2H(b)), \ + _CCAT(_B2H(c),_B2H(d))), \ + _CCAT( \ + _CCAT(_B2H(e), _B2H(f)), \ + _CCAT(_B2H(g), _B2H(h)) \ + ) \ + )) + +#endif /* INC_MMR_CAN_BINARY_LITERALS_H_ */ diff --git a/Inc/mmr_can_events.h b/Inc/mmr_can_events.h index 5fb1b28..b8dba1f 100644 --- a/Inc/mmr_can_events.h +++ b/Inc/mmr_can_events.h @@ -2,21 +2,19 @@ #define INC_MMR_CAN_EVENTS_H_ #include "mmr_can.h" +#include "mmr_can_util.h" -typedef struct { - CanId senderId; - uint8_t *message; -} MmrCanEvent; - -typedef void (*MmrCanEventHandler)(MmrCanEvent *event); +typedef void (*MmrCanEventHandler)(const MmrCanMessage *event); typedef struct { const MmrCanEventHandler *handlers; const size_t count; } MmrCanEventList; +#define MMR_CAN_CreateEventList(handlers) \ + (const MmrCanEventList) { handlers, sizeofarray(handlers) } -void MMR_CAN_InitRxHandlers(const MmrCanEventList *rxEvents); +HalStatus MMR_CAN_InitRxHandlers(CanHandle *hcan, const MmrCanEventList *rxEvents); #endif /* INC_MMR_CAN_EVENTS_H_ */ diff --git a/Inc/mmr_can_types.h b/Inc/mmr_can_types.h index 580e2b3..f490cb6 100644 --- a/Inc/mmr_can_types.h +++ b/Inc/mmr_can_types.h @@ -35,5 +35,6 @@ typedef HAL_StatusTypeDef HalStatus; typedef CAN_HandleTypeDef CanHandle; typedef CAN_RxHeaderTypeDef CanRxHeader; typedef CAN_TxHeaderTypeDef CanTxHeader; +typedef CAN_FilterTypeDef CanFilter; #endif /* INC_MMR_CAN_TYPES_H_ */ diff --git a/Inc/mmr_can_util.h b/Inc/mmr_can_util.h index 62cf522..5071807 100644 --- a/Inc/mmr_can_util.h +++ b/Inc/mmr_can_util.h @@ -4,14 +4,17 @@ #include #include -#define arrayLength(array) \ +#define sizeofarray(array) \ (sizeof(array) / sizeof(*(array))) #define stringArrayLength(array) \ - stringBufferLength((array), sizeof(array)) + stringBufferLength((array), sizeofarray(array)) + +#define stringBufferLength(pbuffer, maxLen) \ + strnlen((const char*)(pbuffer), maxLen) + +#define min(a, b) ((a) < (b) ? a : b); +#define mask(value, bits) (value & bits) -size_t stringBufferLength(uint8_t *buffer, size_t maxLen) { - return strnlen((const char*)buffer, maxLen); -} #endif /* INC_MMR_CAN_UTIL_H_ */ diff --git a/Src/mmr_can.c b/Src/mmr_can.c index 443fafe..4e5ed2c 100644 --- a/Src/mmr_can.c +++ b/Src/mmr_can.c @@ -1,8 +1,19 @@ #include "mmr_can.h" +#include "mmr_can_util.h" + +static uint8_t maskIdLower5Bits(CanRxHeader *header); + + +HalStatus MMR_CAN_BasicSetupAndStart(CanHandle *hcan) { + return + MMR_CAN_FilterConfigDefault(hcan) | + HAL_CAN_Start(hcan) + ; +} HalStatus MMR_CAN_FilterConfig(CanHandle *hcan, MmrCanFilterSettings settings) { - CAN_FilterTypeDef filter = { + CanFilter filter = { .FilterActivation = settings.enabled ? CAN_FILTER_ENABLE : CAN_FILTER_DISABLE, @@ -19,11 +30,13 @@ HalStatus MMR_CAN_FilterConfig(CanHandle *hcan, MmrCanFilterSettings settings) { return HAL_CAN_ConfigFilter(hcan, &filter); } + CanFilterMask MMR_CAN_AlignStandardMask(CanFilterMask baseMask) { static const uint8_t extendedMaskSurplusBytes = 5; return baseMask << extendedMaskSurplusBytes; } + MmrCanFilterSettings MMR_CAN_GetDefaultFilterSettings() { return (MmrCanFilterSettings) { .enabled = true, @@ -35,14 +48,14 @@ MmrCanFilterSettings MMR_CAN_GetDefaultFilterSettings() { } -HalStatus MMR_CAN_Send(CanHandle *hcan, MmrCanPacket packet) { - CanTxHeader header = { - .IDE = CAN_ID_STD, - .RTR = CAN_RTR_DATA, - .DLC = packet.length, - .StdId = packet.remoteId, - .TransmitGlobalTime = DISABLE, - }; - - return HAL_CAN_AddTxMessage(hcan, &header, packet.data, packet.mailbox); +bool MMR_CAN_IsMultiFrame(CanRxHeader *header) { + return maskIdLower5Bits(header) == MMR_CAN_MESSAGE_TYPE_MULTI_FRAME; +} + +bool MMR_CAN_IsMultiFrameEnd(CanRxHeader *header) { + return maskIdLower5Bits(header) == MMR_CAN_MESSAGE_TYPE_MULTI_FRAME; +} + +static always_inline uint8_t maskIdLower5Bits(CanRxHeader *header) { + return mask(header->ExtId, B8_(0001, 1111)); } diff --git a/Src/mmr_can_events.c b/Src/mmr_can_events.c index 2b530b7..74a17d9 100644 --- a/Src/mmr_can_events.c +++ b/Src/mmr_can_events.c @@ -6,30 +6,30 @@ static const MmrCanEventList *_rxEvents = NULL; static void __handleCanRxInterrupt(CanHandle *hcan); -static void __invokeAll(const MmrCanEventList *events, MmrCanEvent *event); -static void __maybeInvoke(const MmrCanEventHandler handler, MmrCanEvent *event); +static void __invokeAll(const MmrCanEventList *events, const MmrCanMessage *event); +static void __maybeInvoke(const MmrCanEventHandler handler, const MmrCanMessage *event); -void MMR_CAN_InitRxHandlers(const MmrCanEventList *rxEvents) { +HalStatus MMR_CAN_InitRxHandlers(CanHandle *hcan, const MmrCanEventList *rxEvents) { _rxEvents = rxEvents; + return HAL_CAN_ActivateNotification(hcan, MMR_CAN_RX_INTERRUPT); } static void __handleCanRxInterrupt(CanHandle *hcan) { - static CanRxHeader rxHeader = {}; - static CanRxBuffer rxData = {}; - - HAL_CAN_GetRxMessage(hcan, MMR_CAN_RX_FIFO, &rxHeader, rxData); - - MmrCanEvent event = { - .senderId = rxHeader.StdId, - .message = rxData, + static CanRxBuffer buffer = {}; + static MmrCanMessage event = { + .store = buffer, }; + MMR_CAN_Receive(hcan, &event); __invokeAll(_rxEvents, &event); } -static void __invokeAll(const MmrCanEventList *events, MmrCanEvent *event) { +static void __invokeAll( + const MmrCanEventList *events, + const MmrCanMessage *event +) { if (!events) { return; } @@ -40,7 +40,10 @@ static void __invokeAll(const MmrCanEventList *events, MmrCanEvent *event) { } } -static always_inline void __maybeInvoke(const MmrCanEventHandler handler, MmrCanEvent *event) { +static always_inline void __maybeInvoke( + const MmrCanEventHandler handler, + const MmrCanMessage *event +) { if (handler) { handler(event); } diff --git a/Src/mmr_can_receive.c b/Src/mmr_can_receive.c new file mode 100644 index 0000000..3da188e --- /dev/null +++ b/Src/mmr_can_receive.c @@ -0,0 +1,54 @@ +#include +#include "mmr_can.h" + +static HalStatus receiveOne(CanHandle *hcan, CanRxHeader *header, uint8_t *result); +static HalStatus receiveAll(CanHandle *hcan, CanRxHeader *header, uint8_t *result); +static bool headerIsMultiFrame(CanRxHeader *header, CanId targetId); + + +HalStatus MMR_CAN_Receive(CanHandle *hcan, MmrCanMessage *result) { + CanRxHeader header = {}; + uint8_t *dest = result->store; + HalStatus status = receiveOne(hcan, &header, dest); + + result->senderId = header.ExtId; + if (MMR_CAN_IsMultiFrame(&header)) { + status |= receiveAll(hcan, &header, dest); + } + + return status; +} + + +static HalStatus receiveOne( + CanHandle *hcan, + CanRxHeader *header, + uint8_t *result +) { + return HAL_CAN_GetRxMessage(hcan, MMR_CAN_RX_FIFO, header, result); +} + +static HalStatus receiveAll( + CanHandle *hcan, + CanRxHeader *header, + uint8_t *result +) { + CanId targetId = header->ExtId; + HalStatus status = HAL_OK; + do { + result += MMR_CAN_MAX_DATA_LENGTH; + status |= receiveOne(hcan, header, result); + } while ( + headerIsMultiFrame(header, targetId) && status == HAL_OK + ); + + return status; +} + + +static bool headerIsMultiFrame(CanRxHeader *header, CanId targetId) { + return + MMR_CAN_IsMultiFrame(header) && + !MMR_CAN_IsMultiFrameEnd(header) && + header->DLC >= MMR_CAN_MAX_DATA_LENGTH; +} diff --git a/Src/mmr_can_send.c b/Src/mmr_can_send.c new file mode 100644 index 0000000..7eb1eff --- /dev/null +++ b/Src/mmr_can_send.c @@ -0,0 +1,130 @@ +#include +#include "mmr_can.h" +#include "mmr_can_util.h" + + +static HalStatus sendNormal(CanHandle *hcan, CanTxHeader *header, MmrCanPacket *packet); +static HalStatus sendMulti(CanHandle *hcan, CanTxHeader *header, MmrCanPacket *packet); +static HalStatus sendSingleMultiFrame(CanHandle *hcan, CanTxHeader *header, MmrCanPacket *packet, uint8_t *offset); +static uint8_t computeFramesToSend(MmrCanPacket *packet); +static uint8_t computeNextMessageLength(MmrCanPacket *packet, uint8_t offset); + + +HalStatus MMR_CAN_Send(CanHandle *hcan, MmrCanPacket packet) { + CanTxHeader header = { + .IDE = CAN_ID_EXT, + .RTR = CAN_RTR_DATA, + .DLC = packet.length, + .ExtId = packet.remoteId << 5, + .TransmitGlobalTime = DISABLE, + }; + + return packet.length <= MMR_CAN_MAX_DATA_LENGTH + ? sendNormal(hcan, &header, &packet) + : sendMulti(hcan, &header, &packet); +} + + +static HalStatus sendNormal( + CanHandle *hcan, + CanTxHeader *header, + MmrCanPacket *packet +) { + header->ExtId |= MMR_CAN_MESSAGE_TYPE_NORMAL; + return + HAL_CAN_AddTxMessage(hcan, header, packet->data, packet->mailbox); +} + + +static HalStatus sendMulti( + CanHandle *hcan, + CanTxHeader *header, + MmrCanPacket *packet +) { + HalStatus status = HAL_OK; + uint8_t offset = 0; + uint8_t framesToSend = computeFramesToSend(packet); + + header->ExtId |= MMR_CAN_MESSAGE_TYPE_MULTI_FRAME; + do { + bool isLastFrame = framesToSend <= 1; + if (isLastFrame) { + header->ExtId |= MMR_CAN_MESSAGE_TYPE_MULTI_FRAME_END; + } + + status |= + sendSingleMultiFrame(hcan, header, packet, &offset); + } + while (framesToSend-- > 1 && status == HAL_OK); + + return status; +} + + +static HalStatus sendSingleMultiFrame( + CanHandle *hcan, + CanTxHeader *header, + MmrCanPacket *packet, + uint8_t *offset +) { + uint8_t *dataStart = packet->data + (*offset); + uint8_t length = computeNextMessageLength(packet, *offset); + + header->DLC = length; + *offset += length; + + return HAL_CAN_AddTxMessage(hcan, header, dataStart, packet->mailbox); +} + + +/** + * @brief + * Computes the frames that will need to be sent for a packet. + * + * For example, if a packet has 15 bytes, the function will return + * 2, as one packet is needed for the first 8 bytes and another is + * needed for the remaining 7. + * + * @example + * The computation is pretty simple + * Given + * length = 17 (bytes) + * We'll have + * framesToSend = 17 / 8 = 2 (frames) + * remainder = 17 % 8 = 1 (bytes) + * maybeOneForRemainder = 1 > 0 = 1 (frame) + * ------------------------------------------- + * result = 2 + 1 = 3 (frames) + */ +static uint8_t computeFramesToSend(MmrCanPacket *packet) { + uint8_t length = packet->length; + uint8_t framesToSend = length / MMR_CAN_MAX_DATA_LENGTH; + uint8_t remainder = length % MMR_CAN_MAX_DATA_LENGTH; + uint8_t maybeOneForRemainder = remainder > 0; + + return framesToSend + maybeOneForRemainder; +} + + +/** + * @brief + * Returns the length for the next message, either + * 8 bytes or a lower value, keeping the offset into count. + * + * @example + * Given + * - a 17 bytes packet + * - an offset starting at 0 and supposedly incrementing + * of 8 after each call + * + * Three subsequent calls to this function will return + * First call -> min(17 - 0, 8) = min(17, 8) = 8 (bytes) + * Second call -> min(17 - 8, 8) = min(9, 8) = 8 (bytes) + * Third call -> min(17 - 16, 8) = min(1, 8) = 1 (byte) + */ +static uint8_t computeNextMessageLength(MmrCanPacket *packet, uint8_t offset) { + uint8_t remainingBytes = packet->length - offset; + return min( + remainingBytes, MMR_CAN_MAX_DATA_LENGTH + ); +}