/****************************************************************************** 版权所有: 文件名称: can_app.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2013-02-28 功能说明: can传输应用层 其它说明: 修改记录: */ /*------------------------------- 头文件 -------------------------------------- */ #include "head.h" #ifdef BSP_CAN_ENABLE /*------------------------------- 宏定义 -------------------------------------- */ /*------------------------------ 类型结构 ------------------------------------- */ /*------------------------------ 全局变量 ------------------------------------- */ int g_sb_monitor; /*------------------------------ 函数声明 ------------------------------------- */ int can_app_callback(u32 can_bus, u8 *buf); /*------------------------------ 外部函数 ------------------------------------- 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性. */ int can_app_init(void) { can_regester_recv_callback(can_app_callback); return 0; } int can_app_exit(void) { can_regester_recv_callback(NULL); return 0; } int can_app_callback(u32 can_bus, u8 *buf) { struct can_frame_head *cfh; // 检查参数 if (can_bus >= CAN_BUS_NUM) { return -1; } if (buf == NULL) { return -2; } cfh = (struct can_frame_head *)buf; switch (cfh->type) { case CAN_FRAME_TYPE_DI_INIT: case CAN_FRAME_TYPE_DI_ON: case CAN_FRAME_TYPE_DI_OFF: case CAN_FRAME_TYPE_DI_INIT1: case CAN_FRAME_TYPE_DI_ON1: case CAN_FRAME_TYPE_DI_OFF1: dido_di_update(buf); break; case CAN_FRAME_TYPE_SB_INFO: #if defined(CAN_SLAVE_BOARD) equ_board_info_update(can_bus, buf); #endif break; default: return 0; break; } return 1; } u8 can_app_checksum(u8 *buf, u8 len) { u8 i, sum; sum = 0; for (i = 0; i < len; i++) { sum += *buf++; } return sum; } int can_app_timing(void) { static long g_timing_sec = 0; int i, ret; u8 *buf; struct timespec ts; clk_time_get(&ts); // 对时帧必须在内部对时秒脉冲发出10ms后再发送,每秒发送一次。 if (ts.tv_sec == g_timing_sec || ts.tv_nsec < 10 * NSEC_PER_MS) { return 0; } // 帧头 buf = can_request_tx_buf(CAN_FRAME_TYPE_TIMING); buf[0] = CAN_FRAME_TYPE_TIMING; buf[1] = CAN_BUS_ADDR_BCAST; buf[2] = 0; buf[3] = sizeof(int); // long -> int 8byte -> 4 byte, 暂时按回4字节传输,待确认 EWen // 得到时间 memcpy(buf + 4, &ts.tv_sec, sizeof(int)); // noted by sunxi: 这里需要注意大小端问题 // 发送 for (i = 0; i < CAN_BUS_NUM; i++) { ret = can_send(i, buf); if (ret < 0) { // rt_printf("can_app_timing:ret=%d\r\n",ret); } } g_timing_sec = ts.tv_sec; return 0; } int can_app_reset(int is_watchdog) { int i; u8 *buf; // 帧头 buf = can_request_tx_buf(CAN_FRAME_TYPE_RESET); buf[0] = CAN_FRAME_TYPE_RESET; buf[1] = CAN_BUS_ADDR_BCAST; buf[2] = 0; buf[3] = 1; buf[4] = (u8)is_watchdog; // 发送 for (i = 0; i < CAN_BUS_NUM; i++) { can_send(i, buf); } // 子板复位时更新时间,避免产生子板通讯异常错误。 for (i = 0; i < EQU_SLOT_NUM_MAX; i++) { g_board_info[i].us0 = ustimer_get_origin(); } return 0; } int can_app_sb_monitor(void) { g_sb_monitor = 1; return 0; } int can_app_sb_monitor_send(void) { u8 buf[4]; buf[0] = CAN_FRAME_TYPE_SB_MONITOR; buf[1] = CAN_BUS_ADDR_BCAST; buf[2] = 0; buf[3] = 0; // 发送 can_send(0, buf); can_send(1, buf); return 0; } // can子板查询 oujie add 20200420 int can_app_sb_discover(void) { u8 buf[4]; u32 slot; slot = equ_get_slot_by_type(BOARD_TYPE_METERING); if (slot > 0) { buf[0] = CAN_FRAME_TYPE_DISCOVER; buf[1] = CAN_BUS_ADDR_BCAST; buf[2] = 0; buf[3] = 0; #ifdef CAN_BOARD_DEBUG rt_printf("查询子板\r\n"); #endif // 发送 can_send(0, buf); // can_send(1,buf); } return 0; } #if 1 // 分配can子板卡槽号 oujie add 20200420 int can_app_sb_give_slot(u32 no, u8 *buf) { u8 send_buf[32]; char id_string[64] = {0}; struct can_board_res *can_brd; Hex2Str(&buf[5], id_string, CHIP_ID_SIZE * 4); #ifdef CAN_BOARD_DEBUG rt_printf("子板id字符串 = %s\r\n", id_string); rt_printf("获取卡槽号\r\n"); #endif can_brd = equ_get_can_slot(buf[4], buf[5], &buf[6], sizeof(can_brd->chip_id)); if (can_brd != NULL) { can_brd->type = buf[5]; send_buf[0] = CAN_FRAME_TYPE_GIVE_CAN_SLOT; send_buf[1] = CAN_BUS_ADDR_BCAST; send_buf[2] = 0; send_buf[3] = sizeof(can_brd->chip_id) + 1; send_buf[4] = can_brd->slot; memcpy(&send_buf[5], can_brd->chip_id, sizeof(can_brd->chip_id)); #ifdef CAN_BOARD_DEBUG rt_printf("CAN发送卡槽号 slot = %d\r\n", can_brd->slot); rt_printf("CAN卡类型 type = %d\r\n", can_brd->type); #endif // 发送 can_send(no, send_buf); } else { #ifdef CAN_BOARD_DEBUG rt_printf("卡槽号已存在,不再发送\r\n"); #endif } return 0; } #endif struct sb_monitor { // 系统 u32 slot; u32 type; u32 start_sec; // 中断时间 u32 us_gps_min; u32 us_gps_max; u32 us_100us_min; u32 us_100us_max; u32 us_can_min; u32 us_can_max; // 开出 u32 do_delay_min; u32 do_delay_max; // CAN总线 u32 can_rx_dropped; u32 can_tx_dropped; u32 can_hw_bus_errors; u32 can_hw_errors; u32 can_hw_overrun; u32 can_overrun; }; extern struct rt_stat g_stat_do_delay; int can_app_sb_monitor_recv(u8 *buf) { struct sb_monitor sm; struct timespec ts; memcpy(&sm, &buf[4], sizeof(sm)); rt_printf("\r\n子板监视信息[slot=%02d,type=%02d]:\r\n", sm.slot, sm.type); ts.tv_sec = sm.start_sec; ts.tv_nsec = 0; rt_printf("上电时间: "); rt_printf_time2(ts); rt_printf(" GPS中断时间:\tmin=%d,\tmax=%d\r\n", sm.us_gps_min, sm.us_gps_max); rt_printf("100us中断时间:\tmin=%d,\tmax=%d\r\n", sm.us_100us_min, sm.us_100us_max); rt_printf(" CAN中断时间:\tmin=%d,\tmax=%d\r\n", sm.us_can_min, sm.us_can_max); rt_printf(" 开出延时时间:\tmin=%d,\tmax=%d\r\n", sm.do_delay_min, sm.do_delay_max); rt_stat_in(&g_stat_do_delay, sm.do_delay_max); rt_printf("CAN总线接收丢包:%d\r\n", sm.can_rx_dropped); rt_printf("CAN总线发送丢包:%d\r\n", sm.can_tx_dropped); rt_printf("CAN总线总线错误:%d\r\n", sm.can_hw_bus_errors); rt_printf("CAN总线硬件错误:%d\r\n", sm.can_hw_errors); rt_printf("CAN总线硬件溢出:%d\r\n", sm.can_hw_overrun); rt_printf("CAN总线接收溢出:%d\r\n", sm.can_overrun); return 0; } int can_app_sb_discover_recv(u8 *buf) { struct can_board_res *can_brd; #ifdef CAN_BOARD_DEBUG uint8_t i; #endif memcpy((u8 *)can_board[0].chip_id, &buf[6], sizeof(can_brd->chip_id)); #ifdef CAN_BOARD_DEBUG rt_printf("\r\n接收到子板chip_id: "); for (i = 0; i < sizeof(can_brd->chip_id); i++) { rt_printf("%x ", buf[6 + i]); } rt_printf("\r\n"); #endif return 0; } int can_app_sb_slot_recv(u8 *buf) { #ifdef CAN_BOARD_DEBUG rt_printf("接收到子板分配卡槽的应答\r\n"); #endif uint8_t i; for (i = 0; i < CAN_BOARD_NUM; i++) { if (can_board[i].file_have) { if ((memcmp((u8 *)can_board[i].chip_id, &buf[5], sizeof(can_board[i].chip_id)) == 0) && (can_board[i].slot == buf[4])) { #ifdef CAN_BOARD_DEBUG rt_printf("分配子板号完成\r\n"); #endif can_board[i].given_slot = 1; } } } return 0; } int errcount_crc = 0; int errcount_sb = 0; int errcount_no = 0; int errcount_sb_no = 0; int errcount_check = 0; static u32 g_no_send[CAN_BUS_NUM]; static u32 g_no_recv[EQU_SLOT_NUM_MAX]; int can_app_test_send(void) { u8 *buf_tx; int i, can, no, len, ret; len = 200; buf_tx = can_request_tx_buf(CAN_FRAME_TYPE_TEST); for (can = 0; can < CAN_BUS_NUM; can++) { no = g_no_send[can]; buf_tx[0] = CAN_FRAME_TYPE_TEST; buf_tx[1] = CAN_BUS_ADDR_BCAST; buf_tx[2] = 0; buf_tx[3] = len; buf_tx[4] = 0; buf_tx[5] = no; buf_tx[6] = no >> 8; buf_tx[7] = no >> 16; buf_tx[8] = no >> 24; for (i = 0; i < (len - 6); i++) { buf_tx[i + 9] = i; } buf_tx[len + 3] = can_app_checksum(buf_tx + 4, len - 1); // 发送 ret = can_send(can, buf_tx); if (ret < 0) { if (ret != -7) { rt_printf("can_app_test error:ret=%d\r\n", ret); } } else { g_no_send[can]++; } } return 0; } int can_app_test_recv(u8 *buf) { u8 len, crc, crc_check; int i, no, slot; slot = buf[2]; len = buf[3]; crc = buf[len + 3]; crc_check = can_app_checksum(buf + 4, len - 1); no = buf[5] | ((int)buf[6]) << 8 | ((int)buf[7]) << 16 | ((int)buf[8]) << 24; // 检查CRC if (crc != crc_check) { errcount_crc++; rt_printf("can_app_test_recv:crc error!slot=%d,type=%d,no=%d, crc=%x,crc_check=%x,errcount_crc=%d\r\n", slot, buf[4], no, crc, crc_check, errcount_crc); print_msg("RX_CAN:", buf, buf[3] + 4); return -1; } // 检查子板接收错误 if (buf[4] != 0) { errcount_sb++; rt_printf("can_app_test_recv:sb error! slot=%d,type=%d,no=%d, errcount_sb=%d\r\n", slot, buf[4], no, errcount_sb); print_msg("RX_CAN:", buf, buf[3] + 4); return -2; } // 检查内容 for (i = 0; i < (len - 6); i++) { if (buf[i + 9] != i) { break; } } if (i != (len - 6)) { errcount_check++; rt_printf("can_app_test_recv:content error!slot=%d,no=%d,errcount_check=%d\r\n", slot, no, errcount_check); print_msg("RX_CAN:", buf, buf[3] + 4); return -4; } // 检查序号 if (no != g_no_recv[slot]) { errcount_sb_no++; rt_printf("can_app_test_recv:no error!slot=%d,no=%d,g_no_recv[slot]=%d,errcount_sb_no=%d\r\n", slot, no, g_no_recv[slot], errcount_sb_no); print_msg("RX_CAN:", buf, buf[3] + 4); return -5; } // 检查ok,序号加一 g_no_recv[slot]++; return 0; } int can_task(void) { int i; u8 *buf; struct can_frame_head *cfh; static unsigned char send_discover = 0; #ifdef CAN_SLAVE_BOARD static unsigned long us0 = 0; static unsigned long us1 = 0; static unsigned char send_discover = 0; #endif // static unsigned long us1 = 0; u8 can_no = 0; if (g_sb_monitor) { can_app_sb_monitor_send(); g_sb_monitor = 0; } buf = can_request_tx_buf(CAN_FRAME_TYPE_PROGRAM_UPDATE); // 最多一次接收8帧 for (i = 0; i < 128; i++) { if (can_recv(0, buf, CAN_FRAME_LEN_MAX) <= 0) { if (can_recv(1, buf, CAN_FRAME_LEN_MAX) <= 0) { prog_recv_timeout(); break; } else can_no = 1; } else can_no = 0; // 检查帧内容 cfh = (struct can_frame_head *)buf; #if 0 if(cfh->src >= EQU_SLOT_NUM_MAX) { continue; } #endif if (cfh->src >= (EQU_SLOT_NUM_MAX + CAN_BOARD_NUM)) { continue; } // 处理不同类型的帧 switch (cfh->type) { case CAN_FRAME_TYPE_PROGRAM_UPDATE: prog_recv(buf); break; case CAN_FRAME_TYPE_SB_MONITOR: can_app_sb_monitor_recv(buf); break; case CAN_FRAME_TYPE_TEST: if (can_app_test_recv(buf) != 0) { g_test_on = 0; g_print_can = 0; } break; case CAN_FRAME_TYPE_DISCOVER: can_app_sb_discover_recv(buf); can_app_sb_give_slot(can_no, buf); send_discover = 0; can_brd_error_clear(buf[4]); break; case CAN_FRAME_TYPE_GIVE_CAN_SLOT: // 收到子板应答 can_app_sb_slot_recv(buf); break; default: break; } } // 子板程序更新发送 prog_send(); #ifdef CAN_SLAVE_BOARD if (!is_prog_down()) { if (us0 == 0) us0 = ustimer_get_origin(); if (ustimer_delay_origin2(&us0, 3 * USTIMER_SEC)) { us0 = 0; can_app_sb_discover(); send_discover++; us1 = ustimer_get_origin(); } if (send_discover >= 2) { if (ustimer_delay_origin2(&us1, 2 * USTIMER_SEC)) { send_discover = 0; us1 = ustimer_get_origin(); can_brd_error_set(); // rt_printf("can_brd_error_set\r\n"); } } } #endif #if defined(METERING_ENERGY) && defined(CAN_SLAVE_BOARD) if (!is_prog_down() && !send_discover) can_metering_send_app(); #endif if (g_test_on) { // can_app_test_send(); } return 0; } int can_app_test_printf(void) { int i; for (i = 0; i < CAN_BUS_NUM; i++) { rt_printf("slot%02d:%d.\r\n", i, g_no_send[i]); } rt_printf("g_no_recv:\r\n"); for (i = 0; i < EQU_SLOT_NUM_MAX; i++) { rt_printf("slot%02d:%d.\r\n", i, g_no_recv[i]); } return 0; } #endif /*------------------------------ 内部函数 ------------------------------------- 内部函数以下划线‘_’开头,不需要检查参数的合法性. */ /*------------------------------ 测试函数 ------------------------------------- 一个实体文件必须带一个本模块的测试函数来进行单元测试,如果的确不方便在本模块中 进行单元测试,必须在此注明实际的测试位置(例如在哪个实体文件中使用哪个测试函数). */ /*------------------------------ 文件结束 ------------------------------------- */