TT12-MCU/applications/RS_485.c
CSSC-WORK\murmur 3169b24a24 修复isInByte数据类型错误导致索引不能大于255的bug
修复过长BIN类型数据使用trDataTolog写数据异常的bug
修改系统时钟为50->100MHz,msg_pool大小为256B->4k,串口buffer为4->40k
更新版本号为2.6d
2023-10-27 10:15:22 +08:00

321 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <rtthread.h>
#include <dfs_file.h>
#include <usrcfg.h>
#include <board.h>
#define LOG_TAG "RS485"
#define LOG_LVL LOG_LVL_DBG
#include <ulog.h>
#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[1024 + 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,);