Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据

这篇具有很好参考价值的文章主要介绍了Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从1980年代,Kvaser就开始CAN产品的研发,在相关产品开发领域有近40多年的经验,对CAN和相关总线技术有着非常深入的研究。我们将分享一些有趣的发现和一些特定情况的技术处理,欢迎关注❤️广州智维电子科技有限公司❤️!
所有人的电脑上都有Microsoft Office,平时我们用它列表和计算,除此之外,它还可以被用来进行CAN总线数据收发的工作,进而辅助你进行其他工作。本文就由Kvaser客户软件经理Dan Arvidson分享如何巧用Excel进行该操作。

我们可以利用Windows电脑上的Microsoft Office Excel进行如下操作:
◾从工作表中的任意单元格向CAN总线发送数据
◾以工作表的任意单元格从CAN总线接收数据
Kvaser CANlib可用于Visual Basic for Applications (VBA)。此多功能库支持Kvaser的所有适配器,并辅助你编写高级和创新的解决方案。

有这么多其他语言可以用,为什么还要使用VBA呢?
首先,如果你平时使用Microsoft Office,那么使用它会更容易。Excel使用广泛,通过VBA,你可应用Excel的所有功能,以及你自己的创意和创新想法。另外,使用Excel不会产生额外成本,也更方便与他人或其他公司分享数据。

本文我们会简要介绍如何在Microsoft Excel中使用CANlib、VBA向CAN总线发送/接收数据。

应用此方法,你需要:
根据本文的步骤,你需要使用Kvaser CANlib和Excel VBA。任意版本的Microsoft Office都可以,但最好是Office 2010,因为VBA 7是在2010年推出的。64位和32位Office均可使用(请参阅下文“VBA实例”中的“32和64位Microsoft Office”内容)。本文中,使用的是Office 365和Excel(版本2202 Build 16.0.14931.20116)64位。
你还需要安装“用于Windows的Kvaser驱动程序”。请咨询客服13824417328获取最新版本CANlib并依照其步骤进行安装。如果你需要进一步使用CANlib,我们建议下载Kvaser CANlib SDK,从上述同一链接即可获得。

VBA介绍
VBA(Visual Basic for Applications)是Visual Basic的一种宏语言,是在其桌面应用程序中执行通用的自动化(OLE)任务的编程语言。主要能用来扩展Windows的应用程序功能,特别是Microsoft Office软件。它也可以说是一种应用程式视觉化的 Basic 脚本。
VBA是Microsoft编程语言Visual Basic 6的一个子集,它使用同一编辑器的精简版本以及类似的调试功能。因此,如果你了解VB6,也就能了解VBA的使用方法。Visual Basic是为了简化编程而创建的,VBA编程的过程不难,使用它就类似于用英语的语句来告诉电脑该做什么。

Application.ActiveDocument.SaveAs (“New Document Name.docx”)
将活动文档另存为 “New Document Name.docx”

需要注意的是,VBA是单线程的,这意味着它将一次执行一个任务。(请参阅下文“VBA示例”中的“多任务处理”内容)

默认情况下,Office不显示“开发工具”选项卡,你必须通过如下步骤启用它:
1️⃣从“File文件”选项卡中,选择“Options选项”打开对话框。
2️⃣选择对话框左侧的“Customize Ribbon自定义功能区”。
3️⃣在对话框左侧的“Choose Commands From从中选择命令”中,选择“Common Commands常用命令”。
4️⃣在右侧的“Customize the ribbon自定义功能区”中,从下拉列表中选择“Main Tab主选项卡”,然后选择“Developer开发工具”复选框。
5️⃣选择OK。

从“开发工具”选项卡,你可以打开编辑器并创建按钮、下拉菜单等。或者,你可以在Office应用程序中使用快捷键:ALT+F11。

如果你还是不了解如何操作,可以在网上进行VBA示例搜索,即使需求不完全相同,你也能从中参考到类似的步骤。
VBA实例
第一个示例将显示当某个单元出现更改时如何作出反应,并将该值发送到CAN总线。
在VBA编辑器中,双击你需要对其作出反应的单元格所在的工作表,然后选择该工作表的“更改(Change)”步骤。
Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据每当该工作表中发生更改时,都会执行工作表更改事件,将其缩小到特定的一个单元格,我使用VBA中的Intersect()函数。此函数确定更改的单元格是否与我们指定的单元格“匹配”。为了简化此示例,我假设工作表处于活动状态,并且我们要发送的值介于0-255之间。有关canWrite中使用的参数,以及如何从CAN总线接收数据的更多信息,请见下一个示例。
在另一个示例中,我已将一个记录文件导入Excel(在此示例中,它是一个.ASC文件)。我这样做只是为了向CAN总线发送数据。
Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据
当然,你的数据可以是来自任何地方的,在VBA中打开一个文件,也可以在Excel中编写几个具体应发送的帧等。
在这里,不会深入探讨CANlib,这个示例主要是启发你可以如何利用VBA和CANlib。在本文,我将演示如何使用Kvaser CANlib从Excel中的任意单元格发送数据,以及如何以Excel中的任意单元格接收数据。
编码

CANlib API - 和句柄声明
Option Explicit 'Force explicit variable declaration so that an undeclared variable generates error.

#If VBA7 Then
Private Declare PtrSafe Sub canInitializeLibrary Lib “CANLIB32.DLL” ()
Private Declare PtrSafe Function canUnloadLibrary Lib “CANLIB32.DLL” () As Long
Private Declare PtrSafe Function canGetNumberOfChannels Lib “CANLIB32.DLL” (ByRef channelCount As Long) As Long
Private Declare PtrSafe Function canGetChannelData Lib “CANLIB32.DLL” (ByVal channel As Long, ByVal item As Long, ByRef buffer As Any, ByVal bufsize As Long) As Long
Private Declare PtrSafe Function canOpenChannel Lib “CANLIB32.DLL” (ByVal handle As Long, ByVal Flags As Long) As LongPtr
Private Declare PtrSafe Function canClose Lib “CANLIB32.DLL” (ByVal handle As LongPtr) As Long
Private Declare PtrSafe Function canBusOn Lib “CANLIB32.DLL” (ByVal handle As LongPtr) As Long
Private Declare PtrSafe Function canBusOff Lib “CANLIB32.DLL” (ByVal handle As LongPtr) As Long
Private Declare PtrSafe Function canSetBusParams Lib “CANLIB32.DLL” (ByVal handle As LongPtr, ByVal freq As Long, ByVal tseg1 As Long, ByVal tseg2 As Long, ByVal sjw As Long, ByVal noSamp As Long, ByVal syncMode As Long) As Long
Private Declare PtrSafe Function canWrite Lib “CANLIB32.DLL” (ByVal handle As LongPtr, ByVal id As Long, ByRef msg As Any, ByVal dlc As Long, ByVal flag As Long) As Long
Private Declare PtrSafe Function canReadWait Lib “CANLIB32.DLL” (ByVal handle As LongPtr, ByRef id As Long, ByRef msg As Any, ByRef dlc As Long, ByRef flag As Long, ByRef time As Long, ByRef timeout As Long) As Long
#Else
Private Declare Sub canInitializeLibrary Lib “CANLIB32.DLL” ()
Private Declare Function canUnloadLibrary Lib “CANLIB32.DLL” () As Long
Private Declare Function canGetNumberOfChannels Lib “CANLIB32.DLL” (ByRef channelCount As Long) As Long
Private Declare Function canGetChannelData Lib “CANLIB32.DLL” (ByVal channel As Long, ByVal item As Long, ByRef buffer As Any, ByVal bufsize As Long) As Long
Private Declare Function canOpenChannel Lib “CANLIB32.DLL” (ByVal handle As Long, ByVal Flags As Long) As Long
Private Declare Function canClose Lib “CANLIB32.DLL” (ByVal handle As Long) As Long
Private Declare Function canBusOn Lib “CANLIB32.DLL” (ByVal handle As Long) As Long
Private Declare Function canBusOff Lib “CANLIB32.DLL” (ByVal handle As Long) As Long
Private Declare Function canSetBusParams Lib “CANLIB32.DLL” (ByVal handle As Long, ByVal freq As Long, ByVal tseg1 As Long, ByVal tseg2 As Long, ByVal sjw As Long, ByVal noSamp As Long, ByVal syncMode As Long) As Long
Private Declare Function canWrite Lib “CANLIB32.DLL” (ByVal handle As Long, ByVal id As Long, ByRef msg As Any, ByVal dlc As Long, ByVal flag As Long) As Long
Private Declare Function canReadWait Lib “CANLIB32.DLL” (ByVal handle As Long, ByRef id As Long, ByRef msg As Any, ByRef dlc As Long, ByRef flag As Long, ByRef time As Long, ByRef timeout As Long) As Long
#End If
'Constant declarations
Private Const canOK = 0
Private Const canOPEN_ACCEPT_VIRTUAL = &H20
Private Const canBITRATE_250K = -3
Private Const canCHANNELDATA_CARD_SERIAL_NO = 7

'Declaration of CAN handles
#If VBA7 Then
Private hnd0, hnd1 As LongPtr
#Else
Private hnd0, hnd1 As Long
#End If

这些声明是必要的,以便指定哪些dll调用可用,并指出此dll的位置。通过Kvaser的安装程序安装时,CANlib32.dll位于系统路径中。这就是说你不必指定它的具体位置。
[ Public | Private ] Declare Sub name Lib “libname” [ ( [ arglist ] ) ]
[ Public | Private ] Declare Function name Lib “libname” [ ( [ arglist ] ) ] [ As type ]
使用Private表明,只有在声明它的模块中才能访问它。
Kvaser CANlib SDK目前不包含任何VB或VBA声明,因此你必须根据需要编写这些声明。有关在线Kvaser CANlib SDK,请访问咨询客服vx:13824417328
有任何问题,你可以联系我们的技术支持人员,本文结尾处提供了联系方式。

调用 CANlib API
初始化CANlib并获取可用通道数
Sub CANLib_Start()
Dim chCount, stat, i As Long
Dim buffer As String
Dim myArr(32) As Byte
Dim ws As Worksheet
canInitializeLibrary
stat = canGetNumberOfChannels(chCount)
If stat <> canOK Then GoTo ErrorHandler
在使用任何其他函数之前,必须先调用canInitializeLibrary函数。它将初始化驱动程序。
canGetNumberOfChannels, 此函数将返回电脑中可用CAN通道的数量。虚拟通道包括在此通道数中。
准备工作表以读取一些设备信息
Sheets.Add(Before:=Sheets(1)).name = “Device info” ’ Add a sheet called “Device info” to the first position
Range(“A1”).Value = “Nof channels”
Range(“B1”).Value = chCount

在这里,我们给第一个位置添加一个新的工作表,并在该工作表的单元格B1中写入可用通道数。

读取每个可用通道的一些设备信息
For i = 0 To chCount - 1
stat = canGetChannelData(i, canCHANNELDATA_CARD_SERIAL_NO, myArr(0), 32)
buffer = StrConv(myArr(), vbUnicode)
If buffer <> Empty Then
Cells(i + 2, 1).Value = “Serial”
Cells(i + 2, 2).Value = buffer
End If
Next i

在这里,我们查看每个可用的通道,并询问设备的序列号,并且我们将其写入每个通道的第二列中的新行(即B)。

打开通道,设置参数并启动总线
hnd0 = canOpenChannel(0, canOPEN_ACCEPT_VIRTUAL)
hnd1 = canOpenChannel(1, canOPEN_ACCEPT_VIRTUAL)
stat = canSetBusParams(hnd0, canBITRATE_250K, 0, 0, 0, 0, 0)
If stat <> canOK Then GoTo ErrorHandler
stat = canSetBusParams(hnd1, canBITRATE_250K, 0, 0, 0, 0, 0)
If stat <> canOK Then GoTo ErrorHandler
stat = canBusOn(hnd0)
If stat <> canOK Then GoTo ErrorHandler
stat = canBusOn(hnd1)
If stat <> canOK Then GoTo ErrorHandler

在这里,我们打开第一个和第二个通道,以获得所有其他调用所需的句柄。我们继续准备这两个打开的通道,为它们设置相同的比特率。

为读取数据准备工作表
DeleteSheet (“Read data”)
Set ws = Sheets.Add()
ws.name = “Read data”
ws.Cells(1, 1).Value = “ID”
ws.Cells(1, 2).Value = “Data1”
ws.Cells(1, 3).Value = “Data2”
ws.Cells(1, 4).Value = “Data3”
ws.Cells(1, 5).Value = “Data4”
ws.Cells(1, 6).Value = “Data5”
ws.Cells(1, 7).Value = “Data6”
ws.Cells(1, 8).Value = “Data7”
ws.Cells(1, 9).Value = “Data8”
这里我们准备一个工作表来存储读取的数据。我首先删除“读取数据(Read data)”表,如果它已经存在。然后我创建此工作表并命名,同时添加一些标题注释,以便更好地理解输出内容。

发送、接收和填充单元格
Sub CANlib_Traffic()
Dim tb As ListObject
Dim iCol, iRow As Integer
Dim sData(1 To 8), sCol As String
Dim bDataTx(1 To 8) As Byte
Dim bDataRx(1 To 8) As Byte
Dim stat, lID, lDlc, lFlags, lTime As Long
Worksheets(“Imported ASC”).Activate
Set tb = ActiveSheet.ListObjects(“TestLog”)
For iRow = 1 To tb.Range.Rows.Count
lDlc = tb.DataBodyRange.Cells(iRow, tb.ListColumns(“DLC”).Index) ’ Get how many data bytes
For iCol = 1 To lDlc
sCol = “Data” + Trim(Str(iCol)) 'Create the headline to read from
sData(iCol) = tb.DataBodyRange.Cells(iRow, tb.ListColumns(sCol).Index)
bDataTx(iCol) = CByte(“&H” & sData(iCol)) ’ Convert the Hex value to decimal
Next iCol
’ Send the byte stream of CAN data on the first channel
stat = canWrite(hnd0, CLng(iRow), bDataTx(1), lDlc, 0)
DoEvents
’ Read out the received data on the second channel
stat = canReadWait(hnd1, lID, bDataRx(1), lDlc, lFlags, lTime, 50)
If stat = canOK Then
With Worksheets(“Read data”) ’ Populate cells in Excel with read CAN data
.Cells(lID + 1, 1).Value = lID
’ .Cells(lID + 1, 2).Value = CStr(Hex(bDataRx(1))) ’ Use this if value should be in hexadecimal
.Cells(lID + 1, 2).Value = bDataRx(1)
.Cells(lID + 1, 3).Value = bDataRx(2)
.Cells(lID + 1, 4).Value = bDataRx(3)
.Cells(lID + 1, 5).Value = bDataRx(4)
.Cells(lID + 1, 6).Value = bDataRx(5)
.Cells(lID + 1, 7).Value = bDataRx(6)
.Cells(lID + 1, 8).Value = bDataRx(7)
.Cells(lID + 1, 9).Value = bDataRx(8)
End With
End If
Next iRow
MsgBox “Traffic done!”
End Sub
首先确定要读取的工作表已激活。在我的示例中,我将导入数据的工作表命名为TestLog,并将其设置为ListObject变量。我这样做是为了在获取要发送的值时更容易循环操作,这样我可以使用工作表的标题来指定我正在读取的列。第一个循环设置为读取导入数据的整个范围。我读取数据长度代码(dlc)值,以了解还要读取和稍后发送的字节数。第二个循环迭代数据字节,将它们从文本格式的十六进制转换为十进制值,并将值存储在字节数组中。

然后,通过调用CANlib函数canWrite,将CAN数据的字节数组与报文id(在本例中为行号)和数据长度代码(dlc)一起发送。

DoEvents可以更轻松地停止正在运行的宏。DoEvents函数允许中断执行代码,并允许计算机处理器同时运行其他任务。使用DoEvents会延长执行时间,但另一方面,它也会让宏停止运行。

canReadWait从接收缓冲区读取报文。如果没有可用的报文,则该函数将等待报文到达或超时。

最后,用该读取值填充之前创建的“读取数据”工作表并使用报文id指定单元格行。通过在写入单元格时使用命名工作表,我不必激活该工作表(Worksheets(“Read data”). Cells (Row, Column).Value),同时能保持保存导入数据的工作表处于活动状态。

操作后清理
Sub CANLib_Stop()
canBusOff (hnd0)
canBusOff (hnd1)
canUnloadLibrary
MsgBox “CANlib is unloaded!”
End Sub

canBusOff把指定句柄关闭。如果同一通道上没有其他句柄处于活动状态,则也将把此通道关闭。canUnloadLibrary将释放分配的内存,卸载已加载的DLL canlib32.,并取消初始化数据结构。

结果Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据
如果选择将输出数据格式化为十六进制,这样更便于比较,然后将单元格格式化为文本: ws.Columns(“A:I”).NumberFormat = “@”,当将值放入这些单元格时,需要进行如下转换:Cells(col, row).Value = CStr(Hex(MyDecValue))。
但在本次操作中,为了让后续分析更方便,我选择了十进制。(也可以使用十六进制值,但有时需要进行转换,因为VBA和Excel中的图表对象需要其值为十进制格式。)
可使用Excel图表可视化数据,例如:
Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据

图表可以在VBA代码中生成,也可以在以后使用Excel的工具栏生成。
32位与64位Microsoft Office
两个Office版本都可用,但VBA版本7中添加了一些新的64位功能。

Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据

VBA中除数据类型Byte外,没有无符号数据类型。但这并不是完全不可行 – 可以在VBA中读取无符号值,例如本例介绍了如何对2147483647以上的值使用双精度类型。
Private Const MAX_UINT32 = 4294967296#
Private Const MAX_INT32 = 2147483647
Function LongToUnsigned(ByVal Value As Long) As Double
If Value < 0 Then
LongToUnsigned = Value + MAX_UINT32
Else
LongToUnsigned = Value
End If
End Function
Function UnsignedToLong(ByVal Value As Double) As Long
If Value < 0 Or Value >= MAX_UINT32 Then Error 6
If Value <= MAX_INT32 Then
UnsignedToLong = Value
Else
UnsignedToLong = Value - MAX_UINT32
End If
End Function

多任务处理
VBA是单线程的,这意味着它将一次执行一个任务。仍然可以创建一个线程或使用回调函数,如CANlib中的kvSetNotifyCallback,但我不建议这样做。如果关闭一个线程并在主线程中等待,则创建一个线程是可以的,但创建更多线程或尝试写入主线程外的单元格,可能会导致Excel冻结并关闭。为了避免麻烦和花费额外时间,建议保持它的简单性和主线程。

对于那些有使用VBA经验的用户,有一些方法可以解决这个问题,例如从代码中的循环中启动一个新的Excel实例,并从该新实例调用主工作簿中的一个过程。
我的建议是保持在主线程里。

结论
在本文中,我们简要介绍了如何在Microsoft Excel中使用CANlib和VBA向CAN总线发送和从CAN总线接收数据。希望此示例可让你了解如何操作可行,并辅助你进一步分析数据或编写解决方案。如果您对本文有任何有问题,可以拨打下方电话13824417328,或者发送邮件至hsales@triv.cn文章来源地址https://www.toymoban.com/news/detail-472433.html

到了这里,关于Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RabbitMQ如何保证消息的发送和接收

    一、RabbitMQ如何保证消息的发送和接收 1.ConfirmCallback方法 ConfirmCallback是一个回调接口,消息发送到broker后触发回调,确认消息是否到达broker服务器,也就是只确认消息是否正确到达Exchange交换机中。 2.ReturnCallback方法 通过实现ReturnCallback接口,启动消息失败返回,此接口是在交

    2024年02月15日
    浏览(35)
  • 如何在前端实现WebSocket发送和接收TCP消息(多线程模式)

    当在前端实现WebSocket发送和接收TCP消息时,可以使用以下步骤来实现多线程模式。本文将详细介绍如何在前端实现WebSocket发送和接收TCP消息,并解释使用到的相关函数及原理。 在前端实现WebSocket发送和接收TCP消息的第一步是创建一个WebSocket连接。我们可以使用浏览器提供的

    2024年02月12日
    浏览(32)
  • 如何在前端实现WebSocket发送和接收UDP消息(多线程模式)

    本文将继续介绍如何在前端应用中利用WebSocket技术发送和接收UDP消息,并引入多线程模式来提高发送效率和性能。我们将使用JavaScript语言来编写代码,并结合WebSocket API、UDP数据包、Web Workers和UDP消息监听器来实现这一功能。 首先,我们需要在前端应用中建立一个WebSocket连接

    2024年02月12日
    浏览(33)
  • 如何在Java实现TCP方式发送和接收Socket消息(多线程模式)

    在Java编程中,使用TCP协议进行Socket通信是非常常见的场景。本文将详细介绍如何在Java中实现TCP方式发送和接收Socket消息,并且利用多线程模式来提高通信效率。 首先,我们需要创建一个Server端来处理接收到的Socket连接请求。以下是实现的步骤: 创建一个ServerSocket对象,并指

    2024年02月12日
    浏览(27)
  • 使用C#和RabbitMQ发送和接收消息

    通过NuGet安装 RabbitMQ.Client 以下是一个简单的示例代码,演示如何使用 C# 和 RabbitMQ 客户端库来发送和接收消息: durable持久化 durable 参数用于指定队列是否是持久化的。 当 durable 参数设置为 true 时,表示队列是持久化的。持久化的队列会在RabbitMQ服务器重启后仍然存在,确保

    2024年02月11日
    浏览(35)
  • C#使用RabbitMQ发送和接收消息工具类

    下面是一个简单的 C# RabbitMQ 发送和接收消息的封装工具类的示例代码: 通过NuGet安装 RabbitMQ.Client

    2024年02月11日
    浏览(46)
  • 如何使用 Lightly 进行 Python GUI 项目开发

    GUI 即图形用户界面(Graphical User Interface)的缩写,是一种使用图形交互的界面系统。这种系统为软件提供图标、菜单等视觉交互性强的部件,让用户能通过点击、拖动、下拉等方式操作电脑中的软件和应用程序。GUI 所展示的物体可以传递各式各样的信息,同时也会随着用户

    2024年02月05日
    浏览(30)
  • 实战指南:使用Spring Boot实现消息的发送和接收

    当涉及到消息发送和接收的场景时,可以使用Spring Boot和消息中间件RabbitMQ来实现。下面是一个简单的示例代码,展示了如何在Spring Boot应用程序中创建消息发送者和接收者,并发送和接收一条消息。 首先,你需要进行以下准备工作 确保你已经安装了Java和Maven,并设置好相应

    2024年02月11日
    浏览(39)
  • 使用HTTP方式发送请求及json数据的接收和解析

    目录 需求 请求端 1,添加依赖 2,请求对象 3,请求工具类 4,请求测试(事先开启接收端的服务) 接收端 数据请求模拟 本项目需要通过向对端第三方项目发送一个http的post类型的请求,并且指定了一些请求字段,数据传输采用了json,对请求头没有其他特殊要求,所以这里写

    2024年01月17日
    浏览(43)
  • springboot(java)使用javamail实现邮件的接收、转发、发送、清除

    最近在弄邮件相关的功能,被搞的头大,很多找的方法根本不知道该怎么往下走,就目前为止, 经过各种的碰壁和失败,就整理出来如何使用javamail实现邮件的接收、转发、发送、清除 不单单是分享,也为我后续查找更方便做一个记录 在正式发送邮件之前,我们应该对邮件

    2024年02月04日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包