diff --git a/.clang-format b/.clang-format deleted file mode 100644 index a6e0111..0000000 --- a/.clang-format +++ /dev/null @@ -1,211 +0,0 @@ -Language: Cpp -AccessModifierOffset: -1 -AlignAfterOpenBracket: AlwaysBreak # BlockIndent -AlignArrayOfStructures: None -AlignConsecutiveMacros: None -AlignConsecutiveAssignments: None -AlignConsecutiveBitFields: None -AlignConsecutiveDeclarations: None -AlignEscapedNewlines: Left -AlignOperands: AlignAfterOperator -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortEnumsOnASingleLine: false -AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortLambdasOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: Never -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: Yes -AttributeMacros: - - __capability -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: MultiLine - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: true - BeforeElse: true - BeforeLambdaBody: false - BeforeWhile: true - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeConceptDeclarations: true -BreakBeforeBraces: Custom -BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeColon -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeColon -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' -QualifierAlignment: Left -CompactNamespaces: false -ConstructorInitializerIndentWidth: 2 -ContinuationIndentWidth: 2 -Cpp11BracedListStyle: true -DeriveLineEnding: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -PackConstructorInitializers: NextLine -BasedOnStyle: '' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -AllowAllConstructorInitializersOnNextLine: true -FixNamespaceComments: true -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IfMacros: - - KJ_IF_MAYBE -IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^' - Priority: 2 - SortPriority: 0 - CaseSensitive: false - - Regex: '^<.*\.h>' - Priority: 1 - SortPriority: 0 - CaseSensitive: false - - Regex: '^<.*' - Priority: 2 - SortPriority: 0 - CaseSensitive: false - - Regex: '.*' - Priority: 3 - SortPriority: 0 - CaseSensitive: false -IncludeIsMainRegex: '([-_](test|unittest))?$' -IncludeIsMainSourceRegex: '' -IndentAccessModifiers: false -IndentCaseLabels: true -IndentCaseBlocks: false -IndentGotoLabels: false -IndentPPDirectives: None -IndentExternBlock: Indent -IndentRequires: false -IndentWidth: 2 -IndentWrappedFunctionNames: false -InsertTrailingCommas: Wrapped -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false -LambdaBodyIndentation: Signature -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 2 -NamespaceIndentation: None -ObjCBinPackProtocolList: Never -ObjCBlockIndentWidth: 2 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 1 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 200 -PenaltyIndentedWhitespace: 0 -PointerAlignment: Right -PPIndentWidth: -1 -RawStringFormats: - - Language: Cpp - Delimiters: - - cc - - CC - - cpp - - Cpp - - CPP - - 'c++' - - 'C++' - CanonicalDelimiter: '' - BasedOnStyle: google - - Language: TextProto - Delimiters: - - pb - - PB - - proto - - PROTO - EnclosingFunctions: - - EqualsProto - - EquivToProto - - PARSE_PARTIAL_TEXT_PROTO - - PARSE_TEST_PROTO - - PARSE_TEXT_PROTO - - ParseTextOrDie - - ParseTextProtoOrDie - - ParseTestProto - - ParsePartialTestProto - CanonicalDelimiter: pb - BasedOnStyle: google -ReferenceAlignment: Pointer -ReflowComments: true -ShortNamespaceLines: 1 -SortIncludes: CaseSensitive -SortJavaStaticImport: Before -SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: true -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceAroundPointerQualifiers: Default -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: Never -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -BitFieldColonSpacing: Both -Standard: Auto -StatementAttributeLikeMacros: - - Q_EMIT -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 8 -UseCRLF: false -UseTab: Never -WhitespaceSensitiveMacros: - - STRINGIZE - - PP_STRINGIZE - - BOOST_PP_STRINGIZE - - NS_SWIFT_NAME - - CF_SWIFT_NAME diff --git a/.github/workflows/cpp-linting.yml b/.github/workflows/cpp-linting.yml deleted file mode 100644 index 27a3a9f..0000000 --- a/.github/workflows/cpp-linting.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: cpp-linting -on: - pull_request: - -jobs: - cpp-linter: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: shenxianpeng/cpp-linter-action@master - id: linter - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - style: file - - - name: Result - if: steps.linter.outputs.checks-failed > 0 - run: exit 1 \ No newline at end of file diff --git a/Inc/mmr_can.h b/Inc/mmr_can.h index 6304c4c..945d31d 100644 --- a/Inc/mmr_can.h +++ b/Inc/mmr_can.h @@ -1,3 +1,9 @@ +/** + * @file mmr_can.h + * @brief + * Main header for the mmr_can library. + */ + #ifndef INC_MMR_CAN_H_ #define INC_MMR_CAN_H_ @@ -8,6 +14,7 @@ #include "mmr_can_types.h" #include "mmr_can_optimize.h" #include "mmr_can_binary_literals.h" +#include "mmr_can_scs.h" #ifndef MMR_CAN_RX_FIFO #define MMR_CAN_RX_FIFO CAN_RX_FIFO0 @@ -29,6 +36,13 @@ #define MMR_CAN_MAX_DATA_LENGTH 8 #endif + +typedef uint32_t (*MmrCanTickProvider)(); + +/** + * @brief + * A buffer large enough to hold a CAN payload. + */ typedef uint8_t CanRxBuffer[MMR_CAN_MAX_DATA_LENGTH]; @@ -65,9 +79,42 @@ typedef struct { } MmrCanFilterSettings; +/** + * @brief + * A packet that can be sent over a CAN + * network. + * + * @example + * typedef struct { + * int x; + * int y; + * } Point; + * + * HalStatus send(Point point) { + * MmrCanPacket packet = { + * .header = { + * .priority = MMR_CAN_MESSAGE_PRIORITY_NORMAL, + * .messageId = MMR_CAN_EXAMPLES_POINT, + * .senderId = 0xXXX, + * }, + * .data = (uint8_t*)&point, + * .length = sizeof(point), + * }; + * + * return MMR_CAN_Send(&hcan, packet); + * } + * + * int main() { + * Point p = {10, 20}; + * if (send(p) != HAL_OK) { + * Error_Handler(); + * } + * + * // 'p' has been sent. + * } + */ typedef struct { MmrCanHeader header; - CanMailbox *mailbox; uint8_t *data; uint8_t length; } MmrCanPacket; @@ -75,7 +122,37 @@ typedef struct { /** * @brief - * Represents a CAN message + * A message received over a CAN network. + * + * @example + * typedef struct { + * int x; + * int y; + * } Point; + * + * HalStatus receive(Point *result) { + * MmrCanMessage message = { + * .store = result, + * }; + * + * HalStatus result = MMR_CAN_Receive(&hcan, &message); + * bool isAPoint = message.header.messageId == MMR_CAN_EXAMPLES_POINT; + * if (!isAPoint) { + * return HAL_ERROR; + * } + * + * return result; + * } + * + * int main() { + * Point p = {}; + * if (receive(&p) != HAL_OK) { + * Error_Handler(); + * } + * + * // here 'p' has been populated and can + * // be used. + * } */ typedef struct { MmrCanHeader header; @@ -87,12 +164,76 @@ typedef struct { MMR_CAN_FilterConfig(phcan, MMR_CAN_GetDefaultFilterSettings()) +/** + * @brief + * A function that provides the current tick. + * Used to track the delay between message + * and acknowledgment. + */ +extern MmrCanTickProvider __mmr_can_tickProvider; + + +/** + * @brief + * Sets the tick provider. + * + * @param tickProvider + * The function to use when fetching the current tick. + */ +void MMR_CAN_SetTickProvider(MmrCanTickProvider tickProvider); + +/** + * @brief + * Returns the current tick by calling the + * configured tick provider. + */ +uint32_t MMR_CAN_GetCurrentTick(); + +/** + * @brief + * Configures the filters with the default configuration + * and starts the can interface. + */ HalStatus MMR_CAN_BasicSetupAndStart(CanHandle *hcan); + +/** + * @brief + * Configures the filters. + */ HalStatus MMR_CAN_FilterConfig(CanHandle *hcan, MmrCanFilterSettings settings); -CanFilterMask MMR_CAN_AlignStandardMask(CanFilterMask baseMask); + +/** + * @brief + * Provides the default configuration for + * the filters. + */ MmrCanFilterSettings MMR_CAN_GetDefaultFilterSettings(); +/** + * @brief + * Sends a can packet over the network. + * Based on the data length, the packet may be split + * into multiple frames. + * + * It is not recommended to send more than 8 bytes, as the + * 'multiple frames' feature has not been fully implemented yet. + */ HalStatus MMR_CAN_Send(CanHandle *hcan, MmrCanPacket packet); + +/** + * @brief + * Sends a can packet over the network as is, without + * changing the data that is provided. + * + * This can prevent the sudden change of the header's message type when + * using MMR_CAN_Send. + */ +HalStatus MMR_CAN_SendNoTamper(CanHandle *hcan, MmrCanPacket packet); + +/** + * @brief + * Receives a can message from the network. + */ HalStatus MMR_CAN_Receive(CanHandle *hcan, MmrCanMessage *result); #endif /* INC_MMR_CAN_H_ */ diff --git a/Inc/mmr_can_events.h b/Inc/mmr_can_events.h index b8dba1f..6b4a6d2 100644 --- a/Inc/mmr_can_events.h +++ b/Inc/mmr_can_events.h @@ -1,3 +1,14 @@ +/** + * @file mmr_can_events.h + * @brief + * This header provides a set of utilities for working + * with interrupts. + * + * The recommended way of readings the CAN bus is + * via polling, altought interrupt may be used for + * monitoring critical messages. + */ + #ifndef INC_MMR_CAN_EVENTS_H_ #define INC_MMR_CAN_EVENTS_H_ @@ -14,6 +25,14 @@ typedef struct { #define MMR_CAN_CreateEventList(handlers) \ (const MmrCanEventList) { handlers, sizeofarray(handlers) } + +/** + * @brief + * Activates the CAN rx interrupts + * + * When one is fired, the callbacks provided inside the + * MmrCanEventList will be invoked + */ HalStatus MMR_CAN_InitRxHandlers(CanHandle *hcan, const MmrCanEventList *rxEvents); diff --git a/Inc/mmr_can_header.h b/Inc/mmr_can_header.h index 7a5cf88..4bf9698 100644 --- a/Inc/mmr_can_header.h +++ b/Inc/mmr_can_header.h @@ -1,3 +1,13 @@ +/** + * @file mmr_can_header.h + * @brief + * This file defines the header used for the can message, + * along with its utilities. + * + * With header is intended the ExtendedId portion + * of the can message. + */ + #ifndef INC_MMR_CAN_HEADER_H_ #define INC_MMR_CAN_HEADER_H_ @@ -12,17 +22,17 @@ * 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 + * They are used to check if a message is either standalone, an + * acknowledgement or split into multiple frames * - * When the priority and id fields are the same, multi-frame - * messages have a higher priority over normal ones + * Constants with lower values have an higher priority. */ typedef enum { - MMR_CAN_MESSAGE_ACK = B_(0001), + MMR_CAN_MESSAGE_TYPE_SCS = B_(0000), + MMR_CAN_MESSAGE_TYPE_ACK = B_(0001), MMR_CAN_MESSAGE_TYPE_MULTI_FRAME = B_(0010), MMR_CAN_MESSAGE_TYPE_MULTI_FRAME_END = B_(0011), - MMR_CAN_MESSAGE_TYPE_NORMAL = B_(1000), + MMR_CAN_MESSAGE_TYPE_NORMAL = B_(0100), } MmrCanMessageType; @@ -40,16 +50,36 @@ typedef enum { */ typedef struct { MmrCanMessagePriority priority : 3; - MmrCanMessageId messageId : 10; - uint32_t senderId : 12; - MmrCanMessageType messageType : 4; + uint16_t messageId : 10; + uint16_t senderId : 10; + uint8_t seqNumber : 3; + MmrCanMessageType messageType : 3; } MmrCanHeader; -uint32_t *MMR_CAN_HeaderToBits(MmrCanHeader *header); -MmrCanHeader *MMR_CAN_HeaderFromBits(uint32_t *bits); +/** + * @brief + * Serializes an MmrCanHeader to bits. + * That is, a 32bits integer with the first + * 3 bits set to zero and the remaining 29 containing the + * extended id + */ +uint32_t MMR_CAN_HeaderToBits(MmrCanHeader header); -bool MMR_CAN_IsMultiFrame(MmrCanHeader *header); -bool MMR_CAN_IsMultiFrameEnd(MmrCanHeader *header); +/** + * @brief + * Deserializes a 32bits integer to an MmrCanHeader. + * The 3 left-most bits must be of padding. + */ +MmrCanHeader MMR_CAN_HeaderFromBits(uint32_t bits); + + +/** + * @brief + * Tells wether the given header represents an SCS. + */ +bool MMR_CAN_IsHeaderScs(MmrCanHeader header); +bool MMR_CAN_IsMultiFrame(MmrCanHeader header); +bool MMR_CAN_IsMultiFrameEnd(MmrCanHeader header); #endif /* INC_MMR_CAN_HEADER_H_ */ diff --git a/Inc/mmr_can_includes.h b/Inc/mmr_can_includes.h index b502eb7..233f6bc 100644 --- a/Inc/mmr_can_includes.h +++ b/Inc/mmr_can_includes.h @@ -1,7 +1,17 @@ +/** + * @file mmr_can_includes.h + * @brief + * This header contains the include macros + * related to the external can_bus drivers. + * + * These may be changed based on the board that is + * being used. + */ + #ifndef INC_MMR_CAN_INCLUDES_H_ #define INC_MMR_CAN_INCLUDES_H_ -#include "main.h" +#include "stm32f3xx_hal.h" #ifndef CAN #define CAN diff --git a/Inc/mmr_can_message_id.h b/Inc/mmr_can_message_id.h index 68d1082..26844f9 100644 --- a/Inc/mmr_can_message_id.h +++ b/Inc/mmr_can_message_id.h @@ -1,6 +1,21 @@ +/** + * @file mmr_can_message_id.h + * @brief + * This header contains the message id declarations. + * + * Message ids identify a message, allowing the receiver + * to take appropriate action when parsing one. + * + * For example, a can packet with message id set to + * MMR_CAN_MESSAGE_ID_POINT might be interpreted as + * a message carrying a struct Point { int x; int y; };, + * and thus deserialized accordingly. + */ + #ifndef INC_MMR_CAN_MESSAGE_ID_H_ #define INC_MMR_CAN_MESSAGE_ID_H_ +#include #include #include "mmr_can_binary_literals.h" @@ -23,11 +38,28 @@ typedef enum { } MmrCanMessageIdType; +/** + * @brief + * Returns the 3 bits representing + * the MmrCanMessageIdType. + */ uint8_t MMR_CAN_GetMessageIdType(MmrCanMessageId msgId); + +/** + * @brief + * Returns the 7 bits representing + * the message id's subtype. + */ uint8_t MMR_CAN_GetMessageIdSubtype(MmrCanMessageId msgId); +/** + * @brief + * Tells wether the provided message + * is of the given id type. + * + * E.g. if a message is an SCS. + */ bool MMR_CAN_IsMessageIdOfType(MmrCanMessageId msgId, MmrCanMessageIdType type); -bool MMR_CAN_IsMessageIdSCS(MmrCanMessageId msgId); enum MmrCanMessageId { @@ -49,7 +81,7 @@ enum MmrCanMessageId { MMR_CAN_MESSAGE_ID_SCS_AS_READY, MMR_CAN_MESSAGE_ID_SCS_AS_DRIVING, MMR_CAN_MESSAGE_ID_SCS_AS_OFF, - + MMR_CAN_MESSAGE_ID_SCS_AM_MANUAL_DRIVING, MMR_CAN_MESSAGE_ID_SCS_AM_ACCELERATION, MMR_CAN_MESSAGE_ID_SCS_AM_SKIDPAD, diff --git a/Inc/mmr_can_optimize.h b/Inc/mmr_can_optimize.h index a3094b2..10c5841 100644 --- a/Inc/mmr_can_optimize.h +++ b/Inc/mmr_can_optimize.h @@ -1,7 +1,23 @@ +/** + * @file mmr_can_optimize.h + * @brief + * Low level optimization utilities. + */ + #ifndef INC_MMR_CAN_OPTIMIZE_H_ #define INC_MMR_CAN_OPTIMIZE_H_ #ifdef __GNUC__ +/** + * @brief + * Tells the compiler that the given method + * must always be inlined. + * + * @example + * static always_inline int min(int a, int b) { + * return a < b ? a : b; + * } + */ #define always_inline inline __attribute__((always_inline)) #else #define always_inline inline diff --git a/Inc/mmr_can_queue.h b/Inc/mmr_can_queue.h deleted file mode 100644 index 11f3b0a..0000000 --- a/Inc/mmr_can_queue.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef INC_MMR_CAN_QUEUE_H_ -#define INC_MMR_CAN_QUEUE_H_ - -#include -#include "mmr_can.h" - -#define MMR_CAN_QUEUE_SIZE 10 - -typedef struct { - MmrCanMessage messages[MMR_CAN_QUEUE_SIZE]; - size_t count; -} MmrCanQueue; - -#endif /* INC_MMR_CAN_QUEUE_H_ */ diff --git a/Inc/mmr_can_scs.h b/Inc/mmr_can_scs.h new file mode 100644 index 0000000..778ca88 --- /dev/null +++ b/Inc/mmr_can_scs.h @@ -0,0 +1,155 @@ +/** + * @file mmr_can_scs_manager.h + * @brief + * Provides utilities for managing the scs messages, + * such as transmission, retransmission and timeout error. + */ + +#ifndef INC_MMR_CAN_SCS_H_ +#define INC_MMR_CAN_SCS_H_ + +#include "mmr_can.h" + + +/** + * @brief + * The maximum number of scs messages that + * can be tracked at any given time. + */ +#ifndef MMR_CAN_SCS_ENTRIES_COUNT +#define MMR_CAN_SCS_ENTRIES_COUNT 5 +#endif + + +/** + * @brief + * Maximum timeout before first retransmission, + * in milliseconds + */ +#ifndef MMR_CAN_MAX_TIMEOUT +#define MMR_CAN_MAX_TIMEOUT 500 +#endif + + +/** + * @brief + * It depends on how many bits the board devotes to the timer + * Check the datasheet + */ +typedef uint32_t TimerRange; + + +/** + * @brief + * Represents the base-struct to manage a single RTR + * and allows to interface with the associated SCS's timer + */ +typedef struct { + /** + * @brief + * The header used to index this entry. + */ + MmrCanHeader header; + + /** + * @brief + * The time at which this message was sent, + * represented as milliseconds since the board + * was turned on. + */ + TimerRange counter; + + /** + * @brief + * Number of retransmissions for this message. + * + * == 0 -> No retransmission occurred. + * >= 1 -> The scs was retransmitted. + */ + int rtr; +} MmrCanScsEntry; + + +/** + * @brief + * The results of an scsCheck operation. + */ +typedef enum { + /** + * @brief No timeout error. + */ + MMR_CAN_SCS_CHECK_OK, + + /** + * @brief The message should be retransmitted. + */ + MMR_CAN_SCS_CHECK_RTR, + + /** + * @brief + * The message has timed out and has already been + * retransmitted, fail. + */ + MMR_CAN_SCS_CHECK_ERROR, +} MmrCanScsCheckResult; + + +/** + * @brief + * Tries to handle an acknowledgment for a particular scs. + * + * This function MUST be called every time a + * message is received, as it might potentially be an + * ACK. + * + * @param header The header to check. + * @return true The message was an ACK and was cleared accordingly. + * @return false The message wasn't an ACK. + */ +bool MMR_CAN_MaybeHandleAck(MmrCanHeader header); + +/** + * @brief + * Checks the array with the stored scs messages and + * retransmits the message if no ack was received. + * + * This function MUST be called at every loop. + * + * @return HalStatus + * The result of the operation. + * HAL_ERROR should immediately be handled as a safe state. + */ +HalStatus MMR_CAN_HandleNextScs(CanHandle *hcan); + +/** + * @brief + * Send an acknowledgment packet based on the + * given scs header. + * + * @param originalHeader The scs header to acknowledge. + */ +HalStatus MMR_CAN_SendAck( + CanHandle *hcan, + MmrCanHeader originalHeader +); + +/** + * @brief + * Sends an Scs message. + * + * @param scsId The MMR_CAN_MESSAGE_ID_SCS_xx id. + * @param senderId The id of this board. + */ +HalStatus MMR_CAN_SendScs( + CanHandle *hcan, + MmrCanMessageId scsId, + CanId senderId +); + + +MmrCanScsEntry* MMR_CAN_GetNextScsEntry(); +MmrCanScsEntry* MMR_CAN_PutScsEntry(MmrCanHeader header); +MmrCanScsEntry* MMR_CAN_ClearScsEntry(MmrCanHeader header); +MmrCanScsEntry* MMR_CAN_FindScsEntry(MmrCanHeader header); + +#endif // !INC_MMR_CAN_SCS_H_ diff --git a/Inc/mmr_can_types.h b/Inc/mmr_can_types.h index f490cb6..657e379 100644 --- a/Inc/mmr_can_types.h +++ b/Inc/mmr_can_types.h @@ -1,3 +1,9 @@ +/** + * @file mmr_can_types.h + * @brief + * Basic type definitions for the can. + */ + #ifndef INC_MMR_CAN_TYPES_H_ #define INC_MMR_CAN_TYPES_H_ @@ -8,6 +14,7 @@ typedef uint32_t CanId; typedef uint32_t CanMailbox; /** + * @brief * A filter mask for the CANbus. * It acts like a subnet mask, filtering the ids that * do not match it. @@ -20,12 +27,14 @@ typedef uint32_t CanMailbox; typedef uint32_t CanFilterMask; /** + * @brief * Stores a value from CAN_filter_FIFO * That is, CAN_FILTER_FIFOx */ typedef uint8_t CanFilterFifo; -/** +/** + * @brief * Represents a filter bank. * The values must be in the range [0, 27] */ diff --git a/Inc/mmr_can_util.h b/Inc/mmr_can_util.h index 06929e9..ef043da 100644 --- a/Inc/mmr_can_util.h +++ b/Inc/mmr_can_util.h @@ -1,19 +1,61 @@ +/** + * @file mmr_can_util.h + * @brief + * Utility functions and macros. + */ + #ifndef INC_MMR_CAN_UTIL_H_ #define INC_MMR_CAN_UTIL_H_ #include #include +/** + * @brief + * Returns the size of the given array. + * + * This only works on static arrays declared + * within the current scope, that is: + * int main() { + * int arr[] = {1, 2, 3}; + * int len = sizeofarray(arr); + * } + */ #define sizeofarray(array) \ (sizeof(array) / sizeof(*(array))) +/** + * @brief + * Returns the length of a statically, non const ptr, declared + * string. + * That is: + * int main() { + * char str[] = "abc"; + * int len = stringArrayLength(str); + * } + */ #define stringArrayLength(array) \ stringBufferLength((array), sizeofarray(array)) +/** + * @brief + * Returns the length of a string buffer. + * Its format must be in bytes, so it could be: + * const char* + * char* + * uint8_t* + * etc... + */ #define stringBufferLength(pbuffer, maxLen) \ strnlen((const char*)(pbuffer), maxLen) -#define min(a, b) ((a) < (b) ? a : b); +/** + * @brief + * Returns the minimum between the + * two given values. + * E.g. min(1, 2) == 1 // true + */ +#define min(a, b) ((a) < (b) ? a : b) #define mask(value, bits) (value & bits) #define convertTo(resultType, lvalue) (*interpretAs(resultType*, &(lvalue))) #define interpretAs(resultType, lvalue) ((resultType)(lvalue)) @@ -26,10 +68,10 @@ * Either * - Error: the computation resulted in error * - Pending: the computation is still undergoing - * - Completed: the computation has completed succesfully + * - Completed: the computation has completed successfully * and its results can be read * - * Asynchronous logig can be easily implemented using State Machines + * Asynchronous logic can be easily implemented using State Machines */ typedef enum { MMR_ASYNC_RESULT_ERROR, diff --git a/Src/mmr_can.c b/Src/mmr_can.c index 6d4af6b..4d9686e 100644 --- a/Src/mmr_can.c +++ b/Src/mmr_can.c @@ -2,12 +2,19 @@ #include "mmr_can_util.h" -/** - * @brief - * Initializes the filter using the values - * from MMR_CAN_GetDefaultFilterSettings, and then - * starts the CAN - */ +MmrCanTickProvider __mmr_can_tickProvider; + + +void MMR_CAN_SetTickProvider(MmrCanTickProvider tickProvider) { + __mmr_can_tickProvider = tickProvider; +} + + +uint32_t MMR_CAN_GetCurrentTick() { + return __mmr_can_tickProvider(); +} + + HalStatus MMR_CAN_BasicSetupAndStart(CanHandle *hcan) { return MMR_CAN_FilterConfigDefault(hcan) | diff --git a/Src/mmr_can_events.c b/Src/mmr_can_events.c index 5e0a3f9..1fe9e09 100644 --- a/Src/mmr_can_events.c +++ b/Src/mmr_can_events.c @@ -10,13 +10,7 @@ static void __invokeAll(const MmrCanEventList *events, const MmrCanMessage *even static void __maybeInvoke(const MmrCanEventHandler handler, const MmrCanMessage *event); -/** - * @brief - * Activates the CAN rx interrupts - * - * When one is fired, the callbacks provided inside the - * MmrCanEventList will be invoked - */ + HalStatus MMR_CAN_InitRxHandlers(CanHandle *hcan, const MmrCanEventList *rxEvents) { _rxEvents = rxEvents; return HAL_CAN_ActivateNotification(hcan, MMR_CAN_RX_INTERRUPT); diff --git a/Src/mmr_can_header.c b/Src/mmr_can_header.c index 8ceb707..bf05a3e 100644 --- a/Src/mmr_can_header.c +++ b/Src/mmr_can_header.c @@ -2,19 +2,34 @@ #include "mmr_can_util.h" #include "mmr_can_optimize.h" -uint32_t *MMR_CAN_HeaderToBits(MmrCanHeader *header) { - return (uint32_t*)header; +uint32_t MMR_CAN_HeaderToBits(MmrCanHeader header) { + return 0 + | ((uint32_t)header.priority << 26) + | ((uint32_t)header.messageId << 23) + | ((uint32_t)header.senderId << 13) + | ((uint32_t)header.seqNumber << 3) + | ((uint32_t)header.messageType); } -MmrCanHeader *MMR_CAN_HeaderFromBits(uint32_t *bits) { - return (MmrCanHeader*)bits; +MmrCanHeader MMR_CAN_HeaderFromBits(uint32_t bits) { + return (MmrCanHeader) { + .priority = bits >> 26, + .messageId = bits >> 23, + .senderId = bits >> 13, + .seqNumber = bits >> 3, + .messageType = bits, + }; } -bool MMR_CAN_IsMultiFrame(MmrCanHeader *header) { - return header->messageType == MMR_CAN_MESSAGE_TYPE_MULTI_FRAME; +bool MMR_CAN_IsHeaderScs(MmrCanHeader header) { + return header.messageType == MMR_CAN_MESSAGE_TYPE_SCS; } -bool MMR_CAN_IsMultiFrameEnd(MmrCanHeader *header) { - return header->messageType == MMR_CAN_MESSAGE_TYPE_MULTI_FRAME_END; +bool MMR_CAN_IsMultiFrame(MmrCanHeader header) { + return header.messageType == MMR_CAN_MESSAGE_TYPE_MULTI_FRAME; +} + +bool MMR_CAN_IsMultiFrameEnd(MmrCanHeader header) { + return header.messageType == MMR_CAN_MESSAGE_TYPE_MULTI_FRAME_END; } diff --git a/Src/mmr_can_message_id.c b/Src/mmr_can_message_id.c index d224d92..eeb4c0f 100644 --- a/Src/mmr_can_message_id.c +++ b/Src/mmr_can_message_id.c @@ -1,11 +1,7 @@ +#include #include "mmr_can_message_id.h" -bool MMR_CAN_IsMessageIdSCS(MmrCanMessageId msgId) { - return MMR_CAN_IsMessageIdOfType(msgId, MMR_CAN_MESSAGE_ID_TYPE_SCS); -} - - bool MMR_CAN_IsMessageIdOfType( MmrCanMessageId msgId, MmrCanMessageIdType type diff --git a/Src/mmr_can_receive.c b/Src/mmr_can_receive.c index 3627f2f..a3639ff 100644 --- a/Src/mmr_can_receive.c +++ b/Src/mmr_can_receive.c @@ -17,7 +17,7 @@ typedef struct { static HalStatus receiveOne(ReceptionParams *rp); static HalStatus receiveAll(ReceptionParams *rp); -static bool headerIsMultiFrame(MmrCanHeader *header, CanId targetId); +static bool headerIsMultiFrame(MmrCanHeader header, CanId targetId); /** @@ -41,7 +41,7 @@ HalStatus MMR_CAN_Receive(CanHandle *hcan, MmrCanMessage *result) { } result->header = rp.headers.mmr; - if (MMR_CAN_IsMultiFrame(&rp.headers.mmr)) { + if (MMR_CAN_IsMultiFrame(rp.headers.mmr)) { status |= receiveAll(&rp); } @@ -55,8 +55,9 @@ static HalStatus receiveAll(ReceptionParams *rp) { do { rp->result += MMR_CAN_MAX_DATA_LENGTH; status |= receiveOne(rp); - } while ( - headerIsMultiFrame(&rp->headers.mmr, targetId) && status == HAL_OK + } + while ( + headerIsMultiFrame(rp->headers.mmr, targetId) && status == HAL_OK ); return status; @@ -64,17 +65,21 @@ static HalStatus receiveAll(ReceptionParams *rp) { static HalStatus receiveOne(ReceptionParams *rp) { - HalStatus status = - HAL_CAN_GetRxMessage(rp->handle, rp->fifo, &(rp->headers.rx), rp->result); + HalStatus status = HAL_CAN_GetRxMessage( + rp->handle, + rp->fifo, + &rp->headers.rx, + rp->result + ); - rp->headers.mmr = convertTo(MmrCanHeader, rp->headers.rx.ExtId); + rp->headers.mmr = MMR_CAN_HeaderFromBits(rp->headers.rx.ExtId); return status; } -static always_inline bool headerIsMultiFrame(MmrCanHeader *header, CanId targetId) { +static bool headerIsMultiFrame(MmrCanHeader header, CanId targetId) { return MMR_CAN_IsMultiFrame(header) && !MMR_CAN_IsMultiFrameEnd(header) && - header->senderId == targetId; + header.senderId == targetId; } diff --git a/Src/mmr_can_scs_entries.c b/Src/mmr_can_scs_entries.c new file mode 100644 index 0000000..f3c9a86 --- /dev/null +++ b/Src/mmr_can_scs_entries.c @@ -0,0 +1,60 @@ +#include "mmr_can_scs.h" + +#define EMPTY_HEADER ((MmrCanHeader){}) +#define EMPTY_ENTRY ((MmrCanScsEntry){}) + +static MmrCanScsEntry __scsEntries[MMR_CAN_SCS_ENTRIES_COUNT]; + + +MmrCanScsEntry* MMR_CAN_GetNextScsEntry() { + static int counter = 0; + + MmrCanScsEntry *entry = __scsEntries + counter; + counter++; + counter %= MMR_CAN_SCS_ENTRIES_COUNT; + + return entry; +} + + +MmrCanScsEntry* MMR_CAN_PutScsEntry(MmrCanHeader header) { + MmrCanScsEntry *entry = MMR_CAN_FindScsEntry(EMPTY_HEADER); + if (entry == NULL) { + return NULL; + } + + *entry = (MmrCanScsEntry){ + .header = header, + .counter = MMR_CAN_GetCurrentTick(), + }; + + return entry; +} + + +MmrCanScsEntry* MMR_CAN_ClearScsEntry(MmrCanHeader header) { + MmrCanScsEntry *entry = MMR_CAN_FindScsEntry(header); + if (entry == NULL) { + return NULL; + } + + *entry = EMPTY_ENTRY; + return entry; +} + + +MmrCanScsEntry* MMR_CAN_FindScsEntry(MmrCanHeader header) { + uint32_t target = MMR_CAN_HeaderToBits(header); + uint32_t i = 0; + + for (; i < MMR_CAN_SCS_ENTRIES_COUNT; i++) { + MmrCanScsEntry *entry = __scsEntries + i; + uint32_t curr = MMR_CAN_HeaderToBits(entry->header); + + if (curr == target) { + return entry; + } + } + + return NULL; +} diff --git a/Src/mmr_can_scs_manager.c b/Src/mmr_can_scs_manager.c new file mode 100644 index 0000000..30cea5d --- /dev/null +++ b/Src/mmr_can_scs_manager.c @@ -0,0 +1,96 @@ +#include "mmr_can_scs.h" + +static void maybeIncrementRtr(MmrCanScsEntry *entry); +static HalStatus sendScs(CanHandle *hcan, MmrCanHeader header); +static MmrCanScsCheckResult checkScs(MmrCanScsEntry *entry); + + +bool MMR_CAN_MaybeHandleAck(MmrCanHeader header) { + if (header.messageType != MMR_CAN_MESSAGE_TYPE_ACK) { + return false; + } + + header.messageType = MMR_CAN_MESSAGE_TYPE_SCS; + MmrCanScsEntry *entry = MMR_CAN_FindScsEntry(header); + if (entry == NULL) { + return false; + } + + MMR_CAN_ClearScsEntry(header); + return true; +} + + +HalStatus MMR_CAN_SendAck( + CanHandle *hcan, + MmrCanHeader originalHeader +) { + originalHeader.messageType = MMR_CAN_MESSAGE_TYPE_ACK; + return sendScs(hcan, originalHeader); +} + + +HalStatus MMR_CAN_SendScs( + CanHandle *hcan, + MmrCanMessageId scsId, + CanId senderId +) { + MmrCanHeader header = { + .senderId = senderId, + .messageId = scsId, + .priority = MMR_CAN_MESSAGE_PRIORITY_HIGH, + .messageType = MMR_CAN_MESSAGE_TYPE_SCS, + }; + + if (MMR_CAN_PutScsEntry(header) == NULL) { + return HAL_ERROR; + } + + return sendScs(hcan, header); +} + + +HalStatus MMR_CAN_HandleNextScs(CanHandle *hcan) { + MmrCanScsEntry *entry = MMR_CAN_GetNextScsEntry(); + + if (MMR_CAN_HeaderToBits(entry->header) == 0) { + return HAL_OK; + } + + switch (checkScs(entry)) { + case MMR_CAN_SCS_CHECK_ERROR: return HAL_ERROR; + case MMR_CAN_SCS_CHECK_OK: return HAL_OK; + default: break; + } + + entry->counter = MMR_CAN_GetCurrentTick(); + return sendScs(hcan, entry->header); +} + + +static HalStatus sendScs(CanHandle *hcan, MmrCanHeader header) { + MmrCanPacket packet = { + .header = header, + .length = 0, + }; + + return MMR_CAN_SendNoTamper(hcan, packet); +} + + +static MmrCanScsCheckResult checkScs(MmrCanScsEntry *entry) { + maybeIncrementRtr(entry); + return + entry->rtr == 1 ? MMR_CAN_SCS_CHECK_RTR : + entry->rtr > 1 ? MMR_CAN_SCS_CHECK_ERROR : + MMR_CAN_SCS_CHECK_OK; +} + +static void maybeIncrementRtr(MmrCanScsEntry *entry) { + TimerRange now = MMR_CAN_GetCurrentTick(); + TimerRange delay = now - entry->counter; + + if (delay >= MMR_CAN_MAX_TIMEOUT) { + entry->rtr++; + } +} diff --git a/Src/mmr_can_send.c b/Src/mmr_can_send.c index 4e02a7d..f92599d 100644 --- a/Src/mmr_can_send.c +++ b/Src/mmr_can_send.c @@ -14,6 +14,7 @@ typedef struct { } TransmissionParams; +static TransmissionParams buildParams(CanHandle *hcan, MmrCanPacket packet); static HalStatus send(TransmissionParams *tp); static HalStatus sendNormal(TransmissionParams *tp); static HalStatus sendMulti(TransmissionParams *tp); @@ -24,10 +25,33 @@ static void setMessageType(TransmissionParams *header, MmrCanMessageType type); static void syncHeaders(TransmissionParams *tp); +static CanMailbox __mailbox; + + HalStatus MMR_CAN_Send(CanHandle *hcan, MmrCanPacket packet) { - TransmissionParams tp = { + TransmissionParams tp = + buildParams(hcan, packet); + + syncHeaders(&tp); + return packet.length <= MMR_CAN_MAX_DATA_LENGTH + ? sendNormal(&tp) + : sendMulti(&tp); +} + + +HalStatus MMR_CAN_SendNoTamper(CanHandle *hcan, MmrCanPacket packet) { + TransmissionParams tp = + buildParams(hcan, packet); + + return send(&tp); +} + + +static TransmissionParams buildParams(CanHandle *hcan, MmrCanPacket packet) { + return (TransmissionParams) { .handle = hcan, .packet = &packet, + .headers.mmr = packet.header, .headers.tx = { .IDE = CAN_ID_EXT, .RTR = CAN_RTR_DATA, @@ -35,11 +59,6 @@ HalStatus MMR_CAN_Send(CanHandle *hcan, MmrCanPacket packet) { .TransmitGlobalTime = DISABLE, }, }; - syncHeaders(&tp); - - return packet.length <= MMR_CAN_MAX_DATA_LENGTH - ? sendNormal(&tp) - : sendMulti(&tp); } @@ -72,11 +91,13 @@ static HalStatus sendSingleMultiFrame(TransmissionParams *tp) { uint8_t length = computeNextMessageLength(tp->packet); - tp->packet->length -= length; - tp->packet->data += length; tp->headers.tx.DLC = length; + tp->packet->length -= length; - return send(tp); + HalStatus result = send(tp); + + tp->packet->data += length; + return result; } @@ -87,13 +108,13 @@ static HalStatus send(TransmissionParams *tp) { tp->handle, &tp->headers.tx, tp->packet->data, - tp->packet->mailbox + &__mailbox ); } static void syncHeaders(TransmissionParams *tp) { - tp->headers.tx.ExtId = *MMR_CAN_HeaderToBits(&tp->headers.mmr); + tp->headers.tx.ExtId = MMR_CAN_HeaderToBits(tp->headers.mmr); }