/* * Copyright (c) 2006-2022, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2019-04-16 misonyo first implementation. */ /* * 程序清单:这是一个串口设备 DMA 接收使用例程 * 例程导出了 uart_dma_sample 命令到控制终端 * 命令调用格式:uart_dma_sample uart3 * 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备 * 程序功能:通过串口输出字符串"hello RT-Thread!",并通过串口输出接收到的数据,然后打印接收到的数据。 */ #include #include #include #include #define LOG_TAG "RS485" #define LOG_LVL LOG_LVL_DBG #include #define TR485 #define SAMPLE_UART_NAME "uart6" /* 串口设备名称 */ static rt_timer_t timer485=RT_NULL; static rt_mutex_t sendcmd=RT_NULL; #ifndef MIN_FRAME_LEN #define MIN_FRAME_LEN 10 #endif /* 串口接收消息结构*/ struct rx_msg { rt_device_t dev; rt_size_t size; }; /* 串口设备句柄 */ static rt_device_t serial485; /* 消息队列控制块 */ static struct rt_messagequeue rx485_mq; //static char rx_buffer[RT_SERIAL_RB_BUFSZ/4 + 1]; /* 接收数据回调函数 */ static rt_err_t uart485_input(rt_device_t dev, rt_size_t size) { struct rx_msg msg; rt_err_t result; static rt_size_t cachecnt=0; cachecnt += size; if (cachecnt < 63) { // LOG_W("not enough data[%d byte(s)], cached and waiting...",size);//处理半包 // LOG_D("%d byte(s)",size); return -1; } msg.dev = dev; msg.size = cachecnt; result = rt_mq_send(&rx485_mq, &msg, sizeof(msg)); if ( result == -RT_EFULL) { /* 消息队列满 */ LOG_W("485 message queue full!"); } cachecnt = 0; return result; } typedef struct{ uint8_t norVol[2]; uint8_t cnt[2]; uint8_t level[2]; uint8_t Ah[2]; uint8_t current[2]; uint8_t incu[2]; uint8_t temp1[2]; uint8_t temp2[2]; uint8_t temp3[2]; uint8_t vol1[2]; uint8_t vol2[2]; uint8_t vol3[2]; uint8_t vol4[2]; uint8_t vol5[2]; uint8_t vol6[2]; uint8_t vol7[2]; uint8_t vol8[2]; }BATT_INFO; static BATT_INFO batt; static int readyToSend=1; static void timer485_cb() { // rt_mutex_release(sendcmd); readyToSend = 1; } static int b2v(uint8_t *din) { int rst = (din[0] << 8) + din[1]; return rst; } void parseBattInfo(uint8_t *din, size_t len) { // LOG_HEX("batt",16,din,len); uint8_t head[]={0x01,0x03,0x3a}; uint8_t index[10]; size_t rst = isInByte(din, len, head, sizeof(head), index); if (!rst) { LOG_W("response from battery is not correct."); batt.level[1]=0x65;//设一个异常值便于观测101% return; } rt_memcpy(&batt, din+index[0]+sizeof(head), sizeof(batt));//简化,只取第一组数据 // LOG_D("batt=%X,%X,%d\%",batt.level[0],batt.level[1],b2v(batt.level)); } static void dumpBattInfo() { rt_kprintf("实际电压 %d\n",b2v(batt.norVol)); rt_kprintf("电芯数量 %d\n",b2v(batt.cnt)); rt_kprintf("电量 %d\n",b2v(batt.level)); rt_kprintf("容量 %d\n",b2v(batt.Ah)); rt_kprintf("输出电流 %d\n",b2v(batt.current)); rt_kprintf("充电电流 %d\n",b2v(batt.incu)); rt_kprintf("温度1 %d\n",b2v(batt.temp1)); rt_kprintf("温度2 %d\n",b2v(batt.temp2)); rt_kprintf("温度3 %d\n",b2v(batt.temp3)); rt_kprintf("电芯 1电压 %d\n",b2v(batt.vol1)); rt_kprintf("电芯 2电压 %d\n",b2v(batt.vol2)); rt_kprintf("电芯 3电压 %d\n",b2v(batt.vol3)); rt_kprintf("电芯 4电压 %d\n",b2v(batt.vol4)); rt_kprintf("电芯 5电压 %d\n",b2v(batt.vol5)); rt_kprintf("电芯 6电压 %d\n",b2v(batt.vol6)); rt_kprintf("电芯 7电压 %d\n",b2v(batt.vol7)); rt_kprintf("电芯 8电压 %d\n",b2v(batt.vol8)); } /** * 获取电池电量 * @return */ uint8_t getPowerLevel(void) { return b2v(batt.level); } /** * 获取电池信息,目前仅返回电量和板卡温度 * @param dout 返回结果数组 * @return 数组长度 */ uint8_t getBattInfo(uint8_t *dout) { size_t p=0; memcpy(dout+p,batt.level,2); p += 2; memcpy(dout+p,batt.temp3,2); p += 2; return p; } /* * 获取电池RAW信息 */ uint8_t getBattRAWInfo(uint8_t *dout) { uint8_t len = sizeof(batt); memcpy(dout,&batt,len); return len; } static void T485_thread_entry(void *parameter) { while (1) { //发送指令 if (readyToSend) { readyToSend = 0; uint8_t cmd[] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x1d, 0x85, 0xc3 }; //send cmd rt_pin_write(TR485_RE, PIN_HIGH); rt_device_write(serial485, 0, cmd, sizeof(cmd)); rt_pin_write(TR485_RE, PIN_LOW); // LOG_D("send 485"); } rt_thread_mdelay(10*1000); } } static void serial485_thread_entry(void *parameter) { struct rx_msg msg; rt_err_t result; rt_uint32_t rx_length=0;//单次收到的数据长度 static char rx_buffer[RT_SERIAL_RB_BUFSZ/4 + 1]; while (1) { rt_memset(&msg, 0, sizeof(msg)); /* 从消息队列中读取消息*/ result = rt_mq_recv(&rx485_mq, &msg, sizeof(msg), RT_WAITING_FOREVER); if (result == RT_EOK) { /* 从串口读取数据*/ rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size); if (!rx_length) { LOG_W("read from RS485 error"); continue; } LOG_I("%d Bytes received from RS485",rx_length); LOG_HEX("485rx:",16,rx_buffer,rx_length);//print what received. parseBattInfo(rx_buffer, rx_length); } // rt_thread_mdelay(1*60*1000); } } static int uart485_dma_sample(int argc, char *argv[]) { rt_err_t ret = RT_EOK; char uart_name[RT_NAME_MAX]; static char msg_pool[1024]; // char str[] = {0x01,0x03,0x00,0x00,0x00,0x1d,0x85,0xc3}; if (argc == 2) { rt_strncpy(uart_name, argv[1], RT_NAME_MAX); } else { rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX); } /* 查找串口设备 */ serial485 = rt_device_find(uart_name); if (!serial485) { LOG_E("find %s failed!", uart_name); return RT_ERROR; } /* 初始化消息队列 */ rt_mq_init(&rx485_mq, "rx485_mq", msg_pool, /* 存放消息的缓冲区 */ sizeof(struct rx_msg), /* 一条消息的最大长度 */ sizeof(msg_pool), /* 存放消息的缓冲区大小 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */ struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初 始 化 配 置 参 数 */ /* step2: 修 改 串 口 配 置 参 数 */ config.baud_rate = BAUD_RATE_9600; //修 改 波 特 率 为 9600 config.data_bits = DATA_BITS_8; //数 据 位 8 config.stop_bits = STOP_BITS_1; //停 止 位 1 config.bufsz = 128; //修 改 缓 冲 区 buff size 为 128 config.parity = PARITY_NONE; //无 奇 偶 校 验 位 /* step3: 控 制 串 口 设 备。 通 过 控 制 接 口 传 入 命 令 控 制 字, 与 控 制 参 数 */ rt_device_control(serial485, RT_DEVICE_CTRL_CONFIG, &config); /* 以 DMA 接收及轮询发送方式打开串口设备 */ rt_device_open(serial485, RT_DEVICE_FLAG_DMA_RX); /* 设置接收回调函数 */ rt_device_set_rx_indicate(serial485, uart485_input); /* 发送字符串 */ /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("RS485", serial485_thread_entry, RT_NULL, 1024*5, 24, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { ret = RT_ERROR; } /* 创建 serial 线程 */ thread = rt_thread_create("T485", T485_thread_entry, RT_NULL, 1024*5, 25+1, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { ret = RT_ERROR; } timer485 = rt_timer_create("batt", timer485_cb, RT_NULL, rt_tick_from_millisecond(1*60*1000), RT_TIMER_FLAG_PERIODIC); /* 启动定时器1 */ if (timer485 != RT_NULL) rt_timer_start(timer485); return ret; } /* 导出到 msh 命令列表中 */ //MSH_CMD_EXPORT(uart485_dma_sample,uart485_dma_sample); INIT_APP_EXPORT(uart485_dma_sample); MSH_CMD_EXPORT(dumpBattInfo,解析电池信息);