Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
CAN总线
国民技术_N32G45x
RT-Thread Studio
[ N32G457 ] rtthread上使用can设备驱动收发数据
发布于 2022-01-15 22:31:58 浏览:2175
订阅该版
[tocm] rtthread最新的bsp代码里面已经有了N32G457的can驱动,我们可以通过git拉取最新的代码使用。 使用rtthread device的api就可以操作can设备。 ### mdk或者gcc编译方式 **第1步,获取bsp代码** 使用git工具(git 命令行或者图形化工具TortoiseGit)从github或者gitee **第2步,分离打包出bsp和rtthread内核代码** 进入到rtthread的bsp源码**/bsp/n32g452xx** 目录下。(n32g452 和n32g457 是统一系列芯片,库函数是可以通用的) 分离打包的目的是为了后面代码的修改不会影响到这个git目录的数据,方便下次拉去新的版本,同时可以将bsp目录的驱动和rtthread复制到一个单独的路径,可以放在任何位置使用。 将**n32g452xx-mini-system**目录复制到一个新的目录n32g45x-test,然后进入n32g45x-test打开env目录。 在env中执行 ``` scons --dist -s ``` 开始分离出打包bsp和rtthread内核代码到dist目录中,分离打包的代码可以复制到任何目录使用,因为内核代码和bsp已经放到一起去了。 **分离打包的代码可能存在Kconfig路径错误或者SConscript路径错误,只要修改一下即可。** ![20220115214621.jpg](https://oss-club.rt-thread.org/uploads/20220115/4257a07f7dcaaf5bd44b0bfbc79b35e0.jpg) 。。。 **第3步,配置rtthread内核和bsp驱动** 进入打包好的bsp目录,使用env工具 输入命令进入Kconfig配置 ``` menuconfig ``` 选择 → Hardware Drivers Config → On-chip Peripheral Drivers 然后打开CAN1 和CAN2的驱动 ![QQ截图20220115215437.jpg](https://oss-club.rt-thread.org/uploads/20220115/ae36e6cfb899b132b19f6cd17e24621c.jpg.webp) 打开CAN1 和CAN2的驱动 ![QQ截图20220115215445.jpg](https://oss-club.rt-thread.org/uploads/20220115/2e01d00bd6147fc6048ea95cfb511f92.jpg.webp) 打开CAN1 和CAN2的驱动 **第4步,修改can复用引脚,添加测试代码** 然后找到bsp目录 board/msp 下面的 **n32_msp.c** 文件, 修改can1和can2的引脚位置,代码如下 ``` #ifdef BSP_USING_CAN void n32_msp_can_init(void *Instance) { GPIO_InitType GPIO_InitCtlStruct; CAN_Module *CANx = (CAN_Module *)Instance; GPIO_InitStruct(&GPIO_InitCtlStruct); GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_50MHz; #ifdef BSP_USING_CAN1 if (CAN1 == CANx) { RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN1, ENABLE); RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE); RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO | RCC_APB2_PERIPH_GPIOD | RCC_APB2_PERIPH_GPIOB, ENABLE); // GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP; // GPIO_InitCtlStruct.Pin = GPIO_PIN_12; // GPIO_InitPeripheral(GPIOA, &GPIO_InitCtlStruct); // GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // GPIO_InitCtlStruct.Pin = GPIO_PIN_11; // GPIO_InitPeripheral(GPIOA, &GPIO_InitCtlStruct); /* Configure CAN1 RX pin */ GPIO_InitCtlStruct.Pin = GPIO_PIN_8; GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitPeripheral(GPIOD, &GPIO_InitCtlStruct); /* Configure CAN1 TX pin */ GPIO_InitCtlStruct.Pin = GPIO_PIN_9; GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitPeripheral(GPIOD, &GPIO_InitCtlStruct); /* Remap CAN1 and CAN2 GPIOs */ GPIO_ConfigPinRemap(GPIO_RMP1_CAN1, ENABLE); } #endif #ifdef BSP_USING_CAN2 if (CAN2 == CANx) { RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN2, ENABLE); RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE); RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE); // //GPIO_PinsRemapConfig(AFIO_MAP6_CAN2_0001, ENABLE); // GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP; // GPIO_InitCtlStruct.Pin = GPIO_PIN_6; // GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct); // GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // GPIO_InitCtlStruct.Pin = GPIO_PIN_5; // GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct); /* Configure CAN2 RX pin */ GPIO_InitCtlStruct.Pin = GPIO_PIN_12; GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct); /* Configure CAN2 TX pin */ GPIO_InitCtlStruct.Pin = GPIO_PIN_13; GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct); //GPIO_ConfigPinRemap(GPIO_RMP1_CAN1, ENABLE); } #endif } #endif /* BSP_USING_CAN */ ``` 这段代码将以前的引脚注释掉了,重映射使用了新的CAN引脚。 CAN1 TX PD9 CAN1 RX PD8 CAN2 TX PB13 CAN2 RX PB12 按这个线序接上CAN收发器 JTA1050之类的模块,然后把CAN1 CAN2的H L接到同一总线上,因为CAN至少需要两个设备(需要另外一个设备回复ACK) ![IMG_20220115_220842.jpg](https://oss-club.rt-thread.org/uploads/20220115/cb0bf695edad98497628c09d80006672.jpg.webp) 这样接线,或者使用USB转CAN工具也可以。 然后添加测试代码 ``` /* * Change Logs: * Date Author Notes * 2022-01-15 chenbin */ #include "stdint.h" #include "stdio.h" #include "string.h" #include "rtthread.h" #include "rtdevice.h" #define DBG_TAG "can1" #define DBG_LVL DBG_LOG #include
#define CAN1_DEVICE_NAME "can1" static rt_device_t can1_dev; /* CAN 设备句柄 */ static struct rt_semaphore can1_rx_sem; /* 用于接收消息的信号量 */ /* 接收数据回调函数 */ static rt_err_t can1_rx_call(rt_device_t dev, rt_size_t size) { /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */ rt_sem_release(&can1_rx_sem); return RT_EOK; } void can1_send_0x123(void) { struct rt_can_msg msg = {0}; msg.id = 0x123; /* ID 为 0x123 */ msg.ide = RT_CAN_STDID; /* 标准格式 */ msg.rtr = RT_CAN_DTR; /* 数据帧 */ msg.len = 8; /* 数据长度为 8 */ /* 待发送的 8 字节数据 */ msg.data[0] = 0x00; msg.data[1] = 0x11; msg.data[2] = 0x22; msg.data[3] = 0x33; msg.data[4] = 0x44; msg.data[5] = 0x55; msg.data[6] = 0x66; msg.data[7] = 0x77; /* 发送一帧 CAN 数据 */ int size = rt_device_write(can1_dev, 0, &msg, sizeof(msg)); if (size < 0) { rt_kprintf("can1 dev write data failed rc:%d\n",size); } } void can1_send_0x456_RTR(void) { struct rt_can_msg msg = {0}; msg.id = 0x456; /* ID 为 0x456 */ msg.ide = RT_CAN_STDID; /* 标准格式 */ msg.rtr = RT_CAN_RTR; /* 远程帧 */ int size = rt_device_write(can1_dev, 0, &msg, sizeof(msg)); if (size < 0) { rt_kprintf("can1 dev write data failed rc:%d\n",size); } } static void can1_thread(void *param) { int rc = 0; can1_dev = rt_device_find(CAN1_DEVICE_NAME); if (!can1_dev) { rt_kprintf("find %s failed!\n", CAN1_DEVICE_NAME); return ; }else { rt_kprintf("find %s ok!\n", CAN1_DEVICE_NAME); } rt_sem_init(&can1_rx_sem, "can1_rx_sem", 0, RT_IPC_FLAG_FIFO); /* 以中断接收及发送方式打开 CAN 设备 */ rc = rt_device_open(can1_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); RT_ASSERT(rc == RT_EOK); /* 设置接收回调函数 */ rt_device_set_rx_indicate(can1_dev, can1_rx_call); rt_device_control(can1_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud); rt_device_control(can1_dev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL); #ifdef RT_CAN_USING_HDR struct rt_can_filter_item items[5] = { RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */ RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */ RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 1, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 为 - 1 */ {0x555, 0, 0, 1, 0x7ff, 7,} /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */ }; struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */ /* 设置硬件过滤表 */ res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); RT_ASSERT(res == RT_EOK); #endif struct rt_can_msg rxmsg = {0}; while(1) { /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */ rxmsg.hdr = -1; /* 阻塞等待接收信号量 */ rt_sem_take(&can1_rx_sem, RT_WAITING_FOREVER); /* 从 CAN 读取一帧数据 */ rt_device_read(can1_dev, 0, &rxmsg, sizeof(rxmsg)); rt_kprintf("CAN1 RX\n"); /* 打印数据 ID 及内容 */ if(rxmsg.rtr == RT_CAN_RTR) //远程帧 { if(rxmsg.ide == RT_CAN_EXTID)//扩展帧 { rt_kprintf("ID:0x%08X RTR len:%d \n", rxmsg.id, rxmsg.len); }else { rt_kprintf("ID:0x%04X RTR len:%d \n", rxmsg.id, rxmsg.len); } if(rxmsg.id == 0x123) { can1_send_0x123(); } }else //数据帧 { if(rxmsg.ide == RT_CAN_EXTID)//扩展帧 { rt_kprintf("ID:0x%08X len:%d ", rxmsg.id, rxmsg.len); for (int i = 0; i < rxmsg.len; i++) { rt_kprintf(" %02X", rxmsg.data[i]); } rt_kprintf("\n"); }else { rt_kprintf("ID:0x%04X len:%d ", rxmsg.id, rxmsg.len); for (int i = 0; i < rxmsg.len; i++) { rt_kprintf(" %02X", rxmsg.data[i]); } rt_kprintf("\n"); } } } } int can1_test_init(void) { static int can1_init = 0; rt_thread_t tid; if(can1_init > 0) { return 0; } can1_init = 1; tid = rt_thread_create("can1",can1_thread, NULL,2048,20, 10); if (tid != RT_NULL) rt_thread_startup(tid); return 0; } void can1_test(uint8_t argc, char **argv) { rt_kprintf("can1 test\n"); can1_send_0x123(); } MSH_CMD_EXPORT(can1_test, can1 test); void can1_test1(uint8_t argc, char **argv) { rt_kprintf("can1 test1\n"); can1_send_0x456_RTR(); } MSH_CMD_EXPORT(can1_test1, can1 test1); void can1_debug(uint8_t argc, char **argv) { if(argc > 1) { int val = atoi(argv[1]); rt_kprintf("can1 debug set %d\n",val); } } MSH_CMD_EXPORT(can1_debug, can1 debug); ``` can2的测试代码 ``` /* * Change Logs: * Date Author Notes * 2022-01-15 chenbin */ #include "stdint.h" #include "stdio.h" #include "string.h" #include "rtthread.h" #include "rtdevice.h" #define DBG_TAG "can2" #define DBG_LVL DBG_LOG #include
#define CAN2_DEVICE_NAME "can2" static rt_device_t can2_dev; /* CAN 设备句柄 */ static struct rt_semaphore can2_rx_sem; /* 用于接收消息的信号量 */ /* 接收数据回调函数 */ static rt_err_t can2_rx_call(rt_device_t dev, rt_size_t size) { /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */ rt_sem_release(&can2_rx_sem); return RT_EOK; } void can2_send_0x456(void) { struct rt_can_msg msg = {0}; msg.id = 0x456; /* ID 为 0x456 */ msg.ide = RT_CAN_STDID; /* 标准格式 */ msg.rtr = RT_CAN_DTR; /* 数据帧 */ msg.len = 8; /* 数据长度为 8 */ /* 待发送的 8 字节数据 */ msg.data[0] = 0x00; msg.data[1] = 0x11; msg.data[2] = 0x22; msg.data[3] = 0x33; msg.data[4] = 0x44; msg.data[5] = 0x55; msg.data[6] = 0x66; msg.data[7] = 0x77; /* 发送一帧 CAN 数据 */ int size = rt_device_write(can2_dev, 0, &msg, sizeof(msg)); if (size < 0) { rt_kprintf("can2 dev write data failed rc:%d\n",size); } } void can2_send_0x123_RTR(void) { struct rt_can_msg msg = {0}; msg.id = 0x123; /* ID 为 0x123 */ msg.ide = RT_CAN_STDID; /* 标准格式 */ msg.rtr = RT_CAN_RTR; /* 远程帧 */ int size = rt_device_write(can2_dev, 0, &msg, sizeof(msg)); if (size < 0) { rt_kprintf("can2 dev write data failed rc:%d\n",size); } } static void can2_thread(void *param) { int rc = 0; can2_dev = rt_device_find(CAN2_DEVICE_NAME); if (!can2_dev) { rt_kprintf("find %s failed!\n", CAN2_DEVICE_NAME); return ; }else { rt_kprintf("find %s ok!\n", CAN2_DEVICE_NAME); } rt_sem_init(&can2_rx_sem, "can2_rx_sem", 0, RT_IPC_FLAG_FIFO); /* 以中断接收及发送方式打开 CAN 设备 */ rc = rt_device_open(can2_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); RT_ASSERT(rc == RT_EOK); /* 设置接收回调函数 */ rt_device_set_rx_indicate(can2_dev, can2_rx_call); rt_device_control(can2_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud); rt_device_control(can2_dev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL); #ifdef RT_CAN_USING_HDR struct rt_can_filter_item items[5] = { RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */ RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 1, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */ RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 1, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 为 - 1 */ {0x555, 0, 0, 1, 0x7ff, 7,} /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */ }; struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */ /* 设置硬件过滤表 */ res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); RT_ASSERT(res == RT_EOK); #endif struct rt_can_msg rxmsg = {0}; while(1) { /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */ rxmsg.hdr = -1; /* 阻塞等待接收信号量 */ rt_sem_take(&can2_rx_sem, RT_WAITING_FOREVER); /* 从 CAN 读取一帧数据 */ rt_device_read(can2_dev, 0, &rxmsg, sizeof(rxmsg)); rt_kprintf("CAN2 RX\n"); /* 打印数据 ID 及内容 */ if(rxmsg.rtr == RT_CAN_RTR) //远程帧 { if(rxmsg.ide == RT_CAN_EXTID)//扩展帧 { rt_kprintf("ID:0x%08X RTR len:%d \n", rxmsg.id, rxmsg.len); }else { rt_kprintf("ID:0x%04X RTR len:%d \n", rxmsg.id, rxmsg.len); } if(rxmsg.id == 0x456) { can2_send_0x456(); } }else //数据帧 { if(rxmsg.ide == RT_CAN_EXTID)//扩展帧 { rt_kprintf("ID:0x%08X len:%d ", rxmsg.id, rxmsg.len); for (int i = 0; i < rxmsg.len; i++) { rt_kprintf(" %02X", rxmsg.data[i]); } rt_kprintf("\n"); }else { rt_kprintf("ID:0x%04X len:%d ", rxmsg.id, rxmsg.len); for (int i = 0; i < rxmsg.len; i++) { rt_kprintf(" %02X", rxmsg.data[i]); } rt_kprintf("\n"); } } } } int can2_test_init(void) { static int can2_init = 0; rt_thread_t tid; if(can2_init > 0) { return 0; } can2_init = 1; tid = rt_thread_create("can2",can2_thread, NULL,2048,20, 10); if (tid != RT_NULL) rt_thread_startup(tid); return 0; } void can2_test(uint8_t argc, char **argv) { rt_kprintf("can2 test\n"); can2_send_0x456(); } MSH_CMD_EXPORT(can2_test, can2 test); void can2_test1(uint8_t argc, char **argv) { rt_kprintf("can2 test1\n"); can2_send_0x123_RTR(); } MSH_CMD_EXPORT(can2_test1, can2 test1); void can2_debug(uint8_t argc, char **argv) { if(argc > 1) { int val = atoi(argv[1]); rt_kprintf("can2 debug set %d\n",val); } } MSH_CMD_EXPORT(can2_debug, can2 debug); ``` 最后在main函数中启动这两个测试的线程 ``` #include
#include
#include
int can1_test_init(void); int can2_test_init(void); /* defined the LED1 pin: PB5 */ #define LED1_PIN 57 int main(void) { uint32_t Speed = 200; /* set LED1 pin mode to output */ rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); can1_test_init(); can2_test_init(); while (1) { rt_pin_write(LED1_PIN, PIN_LOW); rt_thread_mdelay(Speed); rt_pin_write(LED1_PIN, PIN_HIGH); rt_thread_mdelay(Speed); } } ``` **第5步,生成mdk工程** 在env中使用命令 ``` scons --target=mdk5 -s ``` 即可生成工程,然后打开mdk开始编译,下载 ![QQ截图20220115221948.jpg](https://oss-club.rt-thread.org/uploads/20220115/cca3d44a4276ecf072c885caf5b759c6.jpg) 编译 ![QQ截图20220115222130.jpg](https://oss-club.rt-thread.org/uploads/20220115/6ab432d68aea10b43f9b55b6f256565f.jpg.webp) 下载 ![QQ截图20220115222201.jpg](https://oss-club.rt-thread.org/uploads/20220115/0ce8c3d62a7fb23bfa76b8953d4c5401.jpg.webp) 下载 **第6步,测试** 运行效果 ![QQ截图20220115222349.jpg](https://oss-club.rt-thread.org/uploads/20220115/72a74bc8d90a11b5c0763649682f3d0c.jpg) **测试CAN1发送数据帧和远程帧** ![20220115223006.jpg](https://oss-club.rt-thread.org/uploads/20220115/1406b124e64a1c96c742bb7064925f7e.jpg) **测试CAN2发送数据帧和远程帧** ![QQ截图20220115222712.jpg](https://oss-club.rt-thread.org/uploads/20220115/508416d252f093dc173d6bf610e42d58.jpg) 好了数据通信正常,使用rt studio操作也是类似的
4
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
文武斌
这家伙很懒,什么也没写!
文章
6
回答
74
被采纳
2
关注TA
发私信
相关文章
1
我也来传一个CANOpen移植,RTT+STM32F107+CanOpenNode
2
谁有STM32裸跑的CANopen程序啊???
3
CAN驱动程序框架
4
CAN驱动接口如何规范一下
5
RTT无法进入线程.Cannot access Memory
6
编译提示arm-none-eabi/bin/ld: cannot find crt0.o: No such file o
7
rtt 2.1.0 正式版 mdk4 bsp/stm32 编译canapp.c错误
8
STM32F10XCAN驱动使用的问题
9
2.1版本stm32f10x分支bxcan驱动波特率设置的bug
10
rtthread2.1.0下,找不到can1设备
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
ART-Pi
FinSH
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
cubemx
PWM
flash
packages_软件包
freemodbus
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
rt_mq_消息队列_msg_queue
keil_MDK
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
4
个答案
3
次被采纳
张世争
8
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
KunYi
5
个答案
1
次被采纳
rv666
3
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
4
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部