-
|
Dear Rokath, I have been using trice now for a while and it is really nice! I want to integrate trice with our CAN bus system. We have defined a custom message for this. However, I am unable to find the location of the so to say "dispatch" function of trice. I want to add functionality so that the trice send is not only sent via UART, but also via the CAN bus. For this, I need the raw trice id and any variables inserted into the Could you give me some pointers on where to find this? Thanks in advance! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
Hi Eric, sorry for the late reply — I only just saw your question. For your use case, please have a look at Trice Auxiliary. It is intended for cases where Trice data should be routed through a custom transport instead of, or in addition to, the standard output paths such as UART. In short, Trice itself first encodes the Trice ID and its arguments into the internal Trice buffer. The transport-specific part is then responsible for moving those encoded bytes out. So for CAN, you usually do not need to extract the raw Trice ID and arguments separately from the macro call; instead, you can forward the already encoded Trice data through your CAN message format. Depending on how you want to frame the CAN packets, you may need to split the Trice byte stream into CAN-sized chunks on the target side and reassemble or feed them into the normal Trice decoder on the host side. Please check the Trice Auxiliary mechanism first — that should be the right hook for adding your CAN transport cleanly without modifying the Trice macros themselves. Best regards -- Edit / additional idea for a CAN-specific variant: For a CAN transport there is also a more direct option, especially when you use classic CAN with 8 data bytes only. You could introduce your own Trice tag, for example "can:", and reserve a dedicated ID range for it. Trice already supports this without changing the Trice tool source:
or with decimal values, for example:
The important point is to choose an ID range that also fits into your CAN identifier space. For classic 11-bit CAN IDs, keep the range inside "0x000...0x7ff". For CAN extended IDs you would have more freedom, but for the simple case I would start with 11-bit IDs. Then write CAN-specific Trices like this: trice8("can:MotorState %u %u\n", state, error);
trice16("can:Adc %u %u %u %u\n", a, b, c, d);After "trice insert", these "can:" Trices will get IDs from the CAN-reserved range. In your project-specific "triceConfig.h", you can then route this ID range to your Auxiliary output path. The relevant manual sections are:
For the Auxiliary part, look at "trice/src/triceAuxiliary.c". The user hook is the function pointer used by the Auxiliary write function, for example the 8-bit path: Write8AuxiliaryFn_t UserNonBlockingDirectWrite8AuxiliaryFn = (void*)0;
void TriceNonBlockingDirectWrite8Auxiliary(const uint8_t* enc, size_t encLen) {
if (UserNonBlockingDirectWrite8AuxiliaryFn != (void*)0) {
UserNonBlockingDirectWrite8AuxiliaryFn(enc, encLen);
}
}So your application can assign its own non-blocking CAN writer to "UserNonBlockingDirectWrite8AuxiliaryFn". But maybe the Deferred option is better. For the simple CAN mapping, I would use only Trices without target timestamps, i.e. the normal lowercase "trice(...)" form. According to chapter 19 Binary Encoding, such a Trice starts with a 16-bit value containing:
For a no-timestamp Trice, the selector is "01". So the raw 16-bit value is not directly the CAN ID. Your CAN writer should mask it: uint16_t raw = enc[0] | ((uint16_t)enc[1] << 8);
uint16_t triceID = raw & 0x3fff;
uint8_t selector = raw >> 14;Then, for the "can:" ID range only, you can use: can_id = triceID;The next 16-bit word is the "NC" field. In the usual short form, it contains the payload byte count and the 8-bit Trice cycle counter. For your CAN use case you can ignore the cycle counter, because CAN itself already transports single frames independently and you are intentionally mapping one selected Trice to one CAN message. A rough sketch for the Auxiliary writer could look like this: static void MyCanAuxiliaryWrite(const uint8_t* enc, size_t encLen) {
if (encLen < 4) {
return;
}
uint16_t raw = enc[0] | ((uint16_t)enc[1] << 8);
uint8_t selector = raw >> 14;
uint16_t triceID = raw & 0x3fff;
// Only no-timestamp Trices are mapped directly to CAN here.
if (selector != 1) {
return;
}
// Only the reserved can: ID range is routed to CAN.
if (triceID < TRICE_CAN_MIN_ID || triceID > TRICE_CAN_MAX_ID) {
return;
}
uint16_t nc = enc[2] | ((uint16_t)enc[3] << 8);
// Usual short-count form: z == 0.
// Bits 8..14 contain payload byte count, bits 0..7 contain cycle counter.
if (nc & 0x8000) {
return; // long payload form: not used for simple classic CAN mapping
}
uint8_t payloadLen = (uint8_t)((nc >> 8) & 0x7f);
// Classic CAN: max 8 payload bytes.
if (payloadLen > 8) {
return;
}
if ((size_t)(4 + payloadLen) > encLen) {
return;
}
const uint8_t* payload = enc + 4;
CAN_Send(triceID, payload, payloadLen);
}With this approach, a selected "can:" Trice becomes almost a CAN message definition: trice8("can:MotorState %u %u\n", state, error);After ID insertion, the Trice ID is also the CAN ID, and the encoded Trice payload becomes the CAN payload. You can also force specific IDs: trice8(iD(123), "can:MotorState %u %u\n", state, error);The trice insert will simply add the ID 123 to the til.json file. The restrictions should be documented clearly:
If the goal is instead to transport the complete Trice byte stream over CAN and decode it with the normal "trice log" tool, then do not use the Trice ID as CAN ID. In that case CAN should only be a packetized byte-stream transport, and the receiver has to reassemble the Trice stream before feeding it into the normal decoder. The ID-as-CAN-ID idea is a special optimized mode for selected "can:" Trices. Edit 2 / fixed CAN IDs are also possible: Another simple option is to assign the CAN ID directly in the source code, as long as that Trice ID is still unused. For example, if CAN ID trice(iD(123), "can:MotorState %u %u\n", state, error);or, for several explicitly chosen CAN IDs: trice8(iD(120), "can:MotorState %u %u\n", state, error);
trice16(iD(121), "can:AdcRaw %u %u\n", adc0, adc1);
trice8(iD(122), "can:Temperature %d\n", temperature);
trice32(iD(123), "can:ErrorState %u\n", error);The So for a CAN mapping you can either:
The second approach is useful when the CAN ID is part of an existing communication specification and must not change. Of course, the ID must still be unique in the Trice ID list. If the same ID is already used by another Trice, choose another one or resolve the duplicate first. For classic 11-bit CAN, the explicitly chosen Trice IDs should also stay within Together with the Auxiliary CAN writer described above, this means: trice(iD(123), "can:MotorState %u %u\n", state, error);can directly become a CAN frame with: CAN ID = 123
Payload = encoded Trice payload bytesprovided that this special You can even use the trice stamp option, when your |
Beta Was this translation helpful? Give feedback.
Hi Eric,
sorry for the late reply — I only just saw your question.
For your use case, please have a look at Trice Auxiliary. It is intended for cases where Trice data should be routed through a custom transport instead of, or in addition to, the standard output paths such as UART.
In short, Trice itself first encodes the Trice ID and its arguments into the internal Trice buffer. The transport-specific part is then responsible for moving those encoded bytes out. So for CAN, you usually do not need to extract the raw Trice ID and arguments separately from the macro call; instead, you can forward the already encoded Trice data through your CAN message format.
Depending on how you want to fra…