第一次写文章,也是帮助自己梳理一下项目的框架吧,如果有什么不准确的地方也欢迎指出。
背景
实现自动驾驶赛车MCU之间的稳定通信,其中NXP S32K148将作为主要的MCU来检测赛车状态以及发布命令,通信协议将使用CAN,上位机Jetson Xavier与S32K148的通信目前将基于UART,测试无误后尝试接入CAN网络。
通信协议
主要的通信协议,未来还会加入RES(remote emergency signal)、心跳检测等。
FreeRtos 下的串口收发
串口配置:
波特率为115200
配置完毕后
首先老套路,开发板初始化
CLOCK_DRV_Init(&clockMan1_InitConfig0);
PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);
初始化完后设置freeRtos的串口收发任务,并开启scheduler
UART_task_setup();
vTaskStartScheduler();
void UART_init(){
LPUART_DRV_Init(INST_LPUART1, &lpuart1_State, &lpuart1_InitConfig0);
LPUART_DRV_InstallRxCallback(INST_LPUART1,uart_callback,NULL);
}
static void UART_recieve_task(void *pvParameters){
//防止warning
(void) pvParameters;
/*模仿的例子*/
uint32_t bytesRemaining;
status_t status;
const TickType_t UART_task_delay = pdMS_TO_TICKS(50UL);
LPUART_DRV_SendData(INST_LPUART1,"UART init",10);
while(1){
LPUART_DRV_ReceiveData(INST_LPUART1,uart_rec_buf,1U);
while(LPUART_DRV_GetReceiveStatus(INST_LPUART1,&bytesRemaining) == STATUS_BUSY);
status = LPUART_DRV_GetReceiveStatus(INST_LPUART1, &bytesRemaining);
if(status != STATUS_SUCCESS){
LPUART_DRV_SendDataBlocking(INST_LPUART1, (uint8_t *)errorMsg, strlen(errorMsg), TIMEOUT);
break;
}
bufferIdx++;
uart_rec_buf[bufferIdx] = 0U;
LPUART_DRV_SendData(INST_LPUART1, uart_rec_buf, bufferIdx);
bufferIdx = 0U;
LPUART_DRV_SendData(INST_LPUART1,"this is test\n",14);
vTaskDelay(UART_task_delay);
}
}
void UART_task_setup(){
UART_init();
xTaskCreate(UART_recieve_task,
"UART",
configMINIMAL_STACK_SIZE,
NULL,
UART_RECEIVE_TASK_PRIORITY,
NULL);
}
此处没有用Blocking函数,因为发现Block函数会导致freeRtos调度卡死,原因尚不明确,不过看到有人用polling函数,或许值得一试。
中断回调函数
static void uart_callback(void *driverState, uart_event_t event, void *userData){
(void) userData;
(void) driverState;
if(event == UART_EVENT_RX_FULL){
/* The reception stops when newline is received or the buffer is full */
if ((uart_rec_buf[bufferIdx] != '\n') && (bufferIdx != (BUFFER_SIZE - 2U))){
/* Update the buffer index and the rx buffer */
bufferIdx++;
LPUART_DRV_SetRxBuffer(INST_LPUART1, &uart_rec_buf[bufferIdx], 1U);
}
}
}
结果成功在有换行符或者buffer快满时时发送存储的数据
FreeRTOS下 FlexCAN FIFO DMA接收
发现这些方面网上资料对这方面的解释都很模糊,大部分都只包括发送,所以只好自己学的不断测试,毕竟第一次做这方面
首先引脚配置,我只用到了CAN0口,使能FIFO,接收模式DMA,总体很简单。CAN2.0最多1MB/s,通常设置一半500kB/s防止负载过大造成传输错误
初始化,记得初始化下DMA,目前只用监听RES系统发送的起始信号以及控制器的循环播报的信号,所以id table只有两个
static void flexcan_init(){
FLEXCAN_DRV_Init(INST_CANCOM0, &canCom0_State, &canCom0_InitConfig0);
for(int i=0; i<2; i++){
id_filter_table[i].isRemoteFrame = false; /*!< Remote frame*/
id_filter_table[i].isExtendedFrame = false; /*!< Extended frame*/
}
id_filter_table[0].id = 0x18C;
id_filter_table[1].id = 0x70C;
FLEXCAN_DRV_ConfigRxFifo(INST_CANCOM0,FLEXCAN_RX_FIFO_ID_FORMAT_A,id_filter_table);
FLEXCAN_DRV_InstallEventCallback(INST_CANCOM0,can_callback,NULL);
EDMA_DRV_Init(&dmaController1_State, &dmaController1_InitConfig0, edmaChnStateArray, edmaChnConfigArray, EDMA_CONFIGURED_CHANNELS_COUNT);
}
然后在相应的监听任务内循环监听就行,FIFO内有数据后DMA会自动存入对应的buffer再触发中断的,目前没感受到这样的好处,可能由于项目不算大,对于can来说还是游刃有余的。目前想不出还可以干什么,等和别的组员进一步交流后再决定。
while(1){
status = FLEXCAN_DRV_RxFifo(INST_CANCOM0,&recvMsg0);
if(status == STATUS_SUCCESS){
}
vTaskDelay(flexcan_task_delay);
}
中断回调函数,收到对应的串口打印出来
void can_callback(uint8_t instance, flexcan_event_type_t eventType,
uint32_t buffIdx, flexcan_state_t *flexcanState) {
(void) buffIdx;
(void) flexcanState;
if(eventType == FLEXCAN_EVENT_DMA_COMPLETE){
if(recvMsg0.msgId == 0x18C){
if(recvMsg0.data[1] == 0x00){
/*emergency*/
LPUART_DRV_SendData(INST_LPUART1,"emergency\n",12);
}else if(recvMsg0.data[1] == 0x03){
/*start up*/
LPUART_DRV_SendData(INST_LPUART1,"start up\n",10);
}else if(recvMsg0.data[1] == 0x01){
/*ready*/
LPUART_DRV_SendData(INST_LPUART1,"ready\n",7);
}
}else if(recvMsg0.msgId == 0x70C){
LPUART_DRV_SendData(INST_LPUART1,"init\n",6);
FLEXCAN_DRV_Send(INST_CANCOM0, 9U, &data_info, start_up_message.msg_id, start_up_message.mb_data);
}
FLEXCAN_DRV_RxFifo(INST_CANCOM0,&recvMsg0);
}
}
最终测试收发都很及时感觉还不错,高频率发送串口和CAN也没有报错,最终和视觉小组合并后再观测数据总线的承载能力吧,下一步就是等开会了。吐槽下,不得不说老外办事真的是慢啊