O Libcanard é uma implementação compacta da pilha de protocolos cyfal/pode em C99/C11 para sistemas incorporados em tempo real de alta integridade.
O Cyphal é um padrão de barramento de dados leve aberto projetado para comunicação intravehicular confiável em aplicações aeroespaciais e robóticas via canculador, Ethernet e outros transportes robustos.
Leia os documentos em libcanard/canard.h
.
Encontre exemplos, entradas, tutoriais no fórum ciflal.
Se você deseja contribuir, leia CONTRIBUTING.md
.
A biblioteca foi projetada para ser utilizável pronta para uso com qualquer plataforma convencional de 8/16/32/64 bits, incluindo plataformas baremetais profundamente incorporadas, desde que exista um compilador compatível com padrão disponível. A camada de IO de mídia específica da plataforma (Driver) deve ser fornecida pelo aplicativo:
+---------------------------------+
| Application |
+-------+-----------------+-------+
| |
+-------+-------+ +-------+-------+
| Libcanard | | Media layer |
+---------------+ +-------+-------+
|
+-------+-------+
| Hardware |
+---------------+
A equipe de desenvolvimento OPENCYPHAL mantém uma coleção de vários componentes específicos da plataforma em um repositório separado em https://github.com/opencyphal/platform_specific_components. Os usuários são incentivados a pesquisar esse repositório de drivers, exemplos e outras peças que podem ser reutilizadas no aplicativo de destino para acelerar o design da camada de IO de mídia (driver) para o aplicativo.
O exemplo aumenta a documentação, mas não a substitui.
A biblioteca requer um alocador de memória dinâmica determinística de complexidade constante. Poderíamos usar a pilha C padrão, mas a maioria das implementações não é de complexidade constante, então, vamos supor que estamos usando o O1Heap. Vamos precisar de embalagens básicas:
static void * memAllocate ( CanardInstance * const canard , const size_t amount )
{
( void ) canard ;
return o1heapAllocate ( my_allocator , amount );
}
static void memFree ( CanardInstance * const canard , void * const pointer )
{
( void ) canard ;
o1heapFree ( my_allocator , pointer );
}
Init uma instância da biblioteca:
CanardInstance canard = canardInit ( & memAllocate , & memFree );
canard . node_id = 42 ; // Defaults to anonymous; can be set up later at any point.
Para poder enviar transferências pela rede, precisaremos de uma fila de transmissão por redundante pode interface:
CanardTxQueue queue = canardTxInit ( 100 , // Limit the size of the queue at 100 frames.
CANARD_MTU_CAN_FD ); // Set MTU = 64 bytes. There is also CANARD_MTU_CAN_CLASSIC.
Publique uma mensagem (serialização da mensagem não mostrada):
static uint8_t my_message_transfer_id ; // Must be static or heap-allocated to retain state between calls.
const CanardTransferMetadata transfer_metadata = {
. priority = CanardPriorityNominal ,
. transfer_kind = CanardTransferKindMessage ,
. port_id = 1234 , // This is the subject-ID.
. remote_node_id = CANARD_NODE_ID_UNSET , // Messages cannot be unicast, so use UNSET.
. transfer_id = my_message_transfer_id ,
};
++ my_message_transfer_id ; // The transfer-ID shall be incremented after every transmission on this subject.
int32_t result = canardTxPush ( & queue , // Call this once per redundant CAN interface (queue).
& canard ,
tx_deadline_usec , // Zero if transmission deadline is not limited.
& transfer_metadata ,
47 , // Size of the message payload (see Nunavut transpiler).
"x2Dx00" "Sancho, it strikes me thou art in great fear." );
if ( result < 0 )
{
// An error has occurred: either an argument is invalid, the TX queue is full, or we've run out of memory.
// It is possible to statically prove that an out-of-memory will never occur for a given application if the
// heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
}
Use o NUNAVUT para gerar automaticamente (DE) código de serialização das definições DSDL.
Os quadros de lata gerados a partir da transferência de mensagens agora estão armazenados na queue
. Precisamos escolhê -los um por um e transmitir -os. Normalmente, o seguinte fragmento deve ser invocado periodicamente para descarregar os quadros da lata da fila de transmissão priorizada (ou vários, se interfaces de rede redundantes forem usados) no driver da lata:
for ( const CanardTxQueueItem * ti = NULL ; ( ti = canardTxPeek ( & queue )) != NULL ;) // Peek at the top of the queue.
{
if (( 0U == ti -> tx_deadline_usec ) || ( ti -> tx_deadline_usec > getCurrentMicroseconds ())) // Check the deadline.
{
if (! pleaseTransmit ( ti )) // Send the frame over this redundant CAN iface.
{
break ; // If the driver is busy, break and retry later.
}
}
// After the frame is transmitted or if it has timed out while waiting, pop it from the queue and deallocate:
canard . memory_free ( & canard , canardTxPop ( & queue , ti ));
}
A recepção de transferência é feita alimentando quadros na máquina de remontagem de transferência de qualquer uma das interfaces redundantes. Mas primeiro, precisamos assinar:
CanardRxSubscription heartbeat_subscription ;
( void ) canardRxSubscribe ( & canard , // Subscribe to messages uavcan.node.Heartbeat.
CanardTransferKindMessage ,
7509 , // The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
16 , // The extent (the maximum possible payload size) provided by Nunavut.
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC ,
& heartbeat_subscription );
CanardRxSubscription my_service_subscription ;
( void ) canardRxSubscribe ( & canard , // Subscribe to an arbitrary service response.
CanardTransferKindResponse , // Specify that we want service responses, not requests.
123 , // The Service-ID whose responses we will receive.
1024 , // The extent (see above).
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC ,
& my_service_subscription );
A "extensão" refere -se à quantidade mínima de memória necessária para manter qualquer representação serializada de qualquer versão compatível do tipo de dados; Ou, em outras palavras, é o tamanho máximo possível dos objetos recebidos. Este parâmetro é determinado pelo autor do tipo de dados no tempo de definição do tipo de dados. É normalmente maior que o tamanho máximo do objeto para permitir que o autor do tipo de dados introduza mais campos nas versões futuras do tipo; Por exemplo, MyMessage.1.0
pode ter o tamanho máximo de 100 bytes e a extensão de 200 bytes; Uma versão revisada MyMessage.1.1
pode ter o tamanho máximo entre 0 e 200 bytes. Os valores de extensão são fornecidos por tipo de dados por transcompilers DSDL, como o Nunavut.
Em Libcanard, usamos o termo "assinatura" não apenas para assuntos (mensagens), mas também para serviços, por simplicidade.
Podemos se inscrever e cancelar a inscrição em tempo de execução quantas vezes quisermos. Normalmente, no entanto, um aplicativo incorporado se inscreveria uma vez e rolaria com ele. Ok, é assim que recebemos transferências:
CanardRxTransfer transfer ;
const int8_t result = canardRxAccept ( & canard ,
rx_timestamp_usec , // When the frame was received, in microseconds.
& received_frame , // The CAN frame received from the bus.
redundant_interface_index , // If the transport is not redundant, use 0.
& transfer ,
NULL );
if ( result < 0 )
{
// An error has occurred: either an argument is invalid or we've ran out of memory.
// It is possible to statically prove that an out-of-memory will never occur for a given application if
// the heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
// Reception of an invalid frame is NOT an error.
}
else if ( result == 1 )
{
processReceivedTransfer ( redundant_interface_index , & transfer ); // A transfer has been received, process it.
canard . memory_free ( & canard , transfer . payload ); // Deallocate the dynamic memory afterwards.
}
else
{
// Nothing to do.
// The received frame is either invalid or it's a non-last frame of a multi-frame transfer.
// Reception of an invalid frame is NOT reported as an error because it is not an error.
}
Uma API simples para gerar configurações de filtro de aceitação de hardware também é fornecida. Os filtros de aceitação são gerados em um esquema estendido de máscara de ID + de 29 bits e podem ser usados para minimizar o número de transferências irrelevantes processadas em software.
// Generate an acceptance filter to receive only uavcan.node.Heartbeat.1.0 messages (fixed port-ID 7509):
CanardFilter heartbeat_config = canardMakeFilterForSubject ( 7509 );
// And to receive only uavcan.register.Access.1.0 service transfers (fixed port-ID 384):
CanardFilter register_access_config = canardMakeFilterForService ( 384 , ins . node_id );
// You can also combine the two filter configurations into one (may also accept irrelevant messages).
// This allows consolidating a large set of configurations to fit the number of hardware filters.
// For more information on the optimal subset of configurations to consolidate to minimize wasted CPU,
// see the Cyphal specification.
CanardFilter combined_config =
canardConsolidateFilters ( & heartbeat_config , & register_access_config );
configureHardwareFilters ( combined_config . extended_can_id , combined_config . extended_mask );
A especificação completa da API está disponível na documentação. Se você achar que os exemplos não são claros ou incorretos, abra um ingresso.
canardRxGetSubscription
.Atualizar a marca como o Uavcan V1 é renomeado para o Cyphal.
Melhore a conformidade com Misra, removendo o uso da matriz flexível: (#192).
Corrija problemas de dependência na Chain de ferramentas do Docker.
Não há alterações na API nesta versão além da renomeação/renomeação: CANARD_UAVCAN_SPECIFICATION_VERSION_MAJOR
-> CANARD_CYPHAL_SPECIFICATION_VERSION_MAJOR
CANARD_UAVCAN_SPECIFICATION_VERSION_MINOR
-> CANARD_CYPHAL_SPECIFICATION_VERSION_MINOR
_canard_cavl.h
(#196). Filas de transmissão dedicadas por redundante podem interagir com limites de profundidade. Agora, espera -se que o aplicativo instancie CanardTxQueue
(ou vários em caso de transporte redundante) manualmente.
Substitua as listas vinculadas O (n) com árvores AVL Fast O (log n) (a biblioteca Cavl é distribuída com Libcanard). Travessia a lista de assinaturas RX agora requer travessia recursiva da árvore.
Os ajudantes manuais de serialização do DSDL removidos; Use Nunavut em vez disso.
Substitua a computação CRC bittudosa por uma tabela estática muito mais rápida por padrão (#185). Isso pode ser desativado definindo CANARD_CRC_TABLE=0
, que deve salvar ca. 500 bytes de ROM.
Problemas corrigidos com correção de consistência na API (#175).
canardRxAccept2()
renomeado para canardRxAccept()
.
Suporte Cabeçalhos de configuração de criação via CANARD_CONFIG_HEADER
.
Adicione a API para gerar configurações de filtro de aceitação de hardware (#169).
canardRxAccept2()
, deprecie canardRxAccept()
.CanardRxSubscription
.A versão inicial.