STM配置CA1和CAN2通信

今天内容:

1.配置CAN1和CAN2环回模式下接收数据;

2.配置CAN1和CAN2正常模式下进行相互通信

这里我不再给大家继续讲CAN通信基础原理了,直接上手教大家使用STM32CubeMX配置环回模式下的收发数据,以及配置STM32F405RGT6的CAN1和CAN相互通信;

一、配置CAN1和CAN2环回模式下下发送和接收数据

步骤1:配置时钟

步骤2:CAN基础配置(配置波特率500K),

这里讲解下如何计算传输比特率,首先确定自己的CAN所在总线最大频率,我使用的是STM32F405RGT6,总线原理图如下:

可以看到CAN0和CAN1是挂载到APB1上面的,主频是42MHz,那么配置传输波特率为(142/(4+2+1)/12 = 0.5Mhz)

这里工程配置基本结束

步骤3:开启CAN1和配置CAN过滤器(这里扩展ID和标准ID为0表示不过滤ID)

/开启CAN1
HAL_CAN_Start(&hcan1);
//配置CAN1过滤器
bsp_can1_filter_config();

//配置CAN过滤器
uint8_t bsp_can1_filter_config(void)
{
  //初始化筛选器
    CAN_FilterTypeDef filter = {0};
    //筛选器编号
    filter.FilterBank = 0;
    //存储FIFO    FIFO0
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    //筛选标准ID高位
    filter.FilterIdHigh = 0;
    //筛选标准ID低位
    filter.FilterIdLow = 0;
    //标准ID掩码高位
    filter.FilterMaskIdHigh = 0;
    //标准ID掩码低位
    filter.FilterMaskIdLow = 0;
    //是否使能当前筛选器
    filter.FilterActivation = ENABLE;
    //筛选器模式     掩码模式
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    //筛选器长度     32位
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    //配置筛选器
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    //过滤器配置失败
    if(HAL_CAN_ConfigFilter(&hcan1, &filter) != HAL_OK)
    {
      my_printf("CAN1 ConfigFilter Fail!!\r\n");
    }
    else
    {
      my_printf("CAN1 ConfigFilter SUCCESS!!\r\n");
    }
    return 1;
}

步骤4:编写发送和接收函数

这里需要说一下,环回模式是直接将数据发送到接收的FIFO里面,外部引脚没有任何物理连接作用。

//编写CAN发送函数
void bsp_can1_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len)
{
  //初始化发送结构体
    CAN_TxHeaderTypeDef send_msg_hdr = {0};
    uint8_t index = 0;
    uint32_t msg_box = 0;
    uint8_t send_buf[8] = {0};
    //标识符ID
    send_msg_hdr.StdId = basic_id;
    //扩展标识符ID
    send_msg_hdr.ExtId = 0x1839f101;
    //扩展标志
    //send_msg_hdr.IDE = CAN_ID_EXT;
    send_msg_hdr.IDE = CAN_ID_STD;
    //远程控制标志
    send_msg_hdr.RTR = CAN_RTR_DATA;
    //有效数据位长度
    send_msg_hdr.DLC = data_len;
    //是否使能捕获时间戳计数器
    send_msg_hdr.TransmitGlobalTime = DISABLE;
    for(index = 0; index < data_len; index++)
        {
          send_buf[index] = data[index];
        }
    if((HAL_CAN_AddTxMessage(&hcan1,&send_msg_hdr,send_buf,&msg_box))!= HAL_OK)
    {
      my_printf("CAN1 Send data Err!!\r\n");
    }
    else
    {
      my_printf("CAN1 Send data Success!!\r\n");
    }
}
//接收数据
void Can1RecvMsg()
{
  //配置CAN过滤器
  bsp_can1_filter_config();
  //配置32位列表模式
  //bsp_can1_filter_config_list();
  //配置16位掩码模式
  //bsp_can1_filter_config_16();
  //16位列表模式
  //bsp_can1_filter_config_16_list();
  //初始话CAN接收结构体
  CAN_RxHeaderTypeDef  rceStu = {0};
  uint8_t data[8] = {0};
  //判断FIFO中是否有数据
  if(HAL_CAN_GetRxFifoFillLevel(&hcan1,CAN_RX_FIFO0) != 0)
  {
    my_printf("CAN1 have data!!\r\n");
    //接收CAN数据信息
    HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rceStu, data);
    my_printf("rceStu.DLC: %d\r\n",rceStu.DLC);
    my_printf("rceStu.ExtId: %d\r\n",rceStu.ExtId);
    my_printf("rceStu.StdId: %x\r\n",rceStu.StdId);
    my_printf("rceStu.Timestamp: %d\r\n",rceStu.Timestamp);
    for(uint8_t i = 0;i<rceStu.DLC;i++)
    {
      my_printf(" %x",data[i]);
    }
     my_printf("\r\n");
  }
  else
  {
    my_printf("No CAN1 INFO!\r\n");
  }
}

二、配置CAN1和CAN2正常模式下相互发送和接收数据

我是用的板子是STM32F405RGT6的,只有STM32F405RGT6自带的控制器,很遗憾没有集成收发器,所以需要额外准备两个CAN的收发器,

这里给大家看看我的板子:

然后我使用的是收发器是TJA1050

接线:需要将两个收发器的高-高、低-低,RX-RX,TX-TX,GND接地,需要注意的是一定要外接5V的电源,我的STM32F405RGT6板子很坑,上面标注的5V实际没有5V只有2.几V,所以导致我最开始用的时候一直找不到原因,一直无法收发数据(重新写了好几遍测试代码都没用,又重新查看数据手册看看CAN1和CAN2是不是有啥显示条件不能通信,最后实在没办法只能将所有可能因素全查找一遍,主要是STM32F405RGT6它3.3V的电源是正常的,就没怀疑过板子问题)。

接线图

工程配置:

can1配置:

can2配置:

还是不对ID进行过过滤(下期再给大家分享如何对16位和32位ID进行列表模式和掩码模式进行配置),这里我把我写的.c和.h分享给大家。

CANTest.c

#include "CanTest.h"

//标准CAN ID
uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,
                          0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; 

//扩展ID
uint32_t ExtIdArray[10] = {0x1839f101,0x1839f102,0x1839f111,0x1839f107,0x1839f1f1,
                           0x183Af101,0x183Af102,0x183Af103,0x183Af104,0x183Af105};


/*CAN使用中断接收处理*/
//使能CAN中断
//HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);


void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    uint16_t i = 0;
    uint8_t buf[8] = {0};
    CAN_RxHeaderTypeDef rxHeader = {0};
    if(hcan->Instance == CAN1)
    {
	my_printf("Recv via STM32F429 Interrupt\r\n");
	HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);
	my_printf("Recv Data: ");
	for(i = 0; i < rxHeader.DLC; i++)
               {
                 my_printf("%x ",buf[i]);
               }
	my_printf("\r\n");
    }
}

//配置CAN过滤器
uint8_t bsp_can1_filter_config(void)
{
  //初始化筛选器
    CAN_FilterTypeDef filter = {0};
    //筛选器编号
    filter.FilterBank = 0;
    //存储FIFO    FIFO0
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    //筛选标准ID高位
    //filter.FilterIdHigh = 0x702 << 5;
    filter.FilterIdHigh = 0;
    //扩展ID高位
    //filter.FilterIdHigh = (0x1839f101 >> 13) & 0xFFFF;
    //筛选标准ID低位
    filter.FilterIdLow = 0;
    //扩展ID低位
    //filter.FilterIdLow = ((0x1839f101 << 3)| CAN_ID_EXT) & 0xFFFF;
    //筛选ID掩码高位          存放寄存器的高16位
    //filter.FilterMaskIdHigh = 0xFFFFFFFF;
    //计算掩码
    //uint16_t  msk = (0x701 ^ (~0x702));
    //uint16_t  msk = (0x1839f101 ^ (~0x1839f102));
    //标准ID掩码高位
    //filter.FilterMaskIdHigh = msk << 5;
    filter.FilterMaskIdHigh = 0;
    //扩展ID掩码低位
    //filter.FilterMaskIdHigh = (msk >> 16) & 0xFFFF;
    //标准ID掩码低位
    filter.FilterMaskIdLow = 0;
    //扩展ID掩码低位
    //filter.FilterMaskIdLow = msk & 0xFFFF;
    //是否使能当前筛选器
    filter.FilterActivation = ENABLE;
    //筛选器模式     掩码模式
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    //筛选器长度     32位
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    //配置筛选器
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    //过滤器配置失败
    if(HAL_CAN_ConfigFilter(&hcan1, &filter) != HAL_OK)
    {
      my_printf("CAN1 ConfigFilter Fail!!\r\n");
    }
    else
    {
      my_printf("CAN1 ConfigFilter SUCCESS!!\r\n");
    }
    return 1;
}

//列表模式过滤器
uint8_t bsp_can1_filter_config_list(void)
{
    //初始化筛选器
    CAN_FilterTypeDef filter = {0};
    //筛选器编号
    filter.FilterBank = 0;
    //存储FIFO    FIFO0
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    filter.FilterIdHigh = 0x723<<5;
    filter.FilterIdLow = 0;
    
    filter.FilterMaskIdHigh = 0x724<<5;
    filter.FilterMaskIdLow = 0;
    
    //是否使能当前筛选器
    filter.FilterActivation = ENABLE;
    //筛选器模式     掩码模式
    filter.FilterMode = CAN_FILTERMODE_IDLIST;
    //筛选器长度     32位
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    //配置筛选器
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    //过滤器配置失败
    if(HAL_CAN_ConfigFilter(&hcan1, &filter) != HAL_OK)
    {
      my_printf("CAN1 ConfigFilter Fail!!\r\n");
    }
    return 1;
}

//16位掩码过滤器
uint8_t bsp_can1_filter_config_16(void)
{
    //初始化筛选器
    CAN_FilterTypeDef filter = {0};
    //筛选器编号
    filter.FilterBank = 0;
    //存储FIFO    FIFO0
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    //ID组1
    uint16_t  msk = (0x721 ^ (~0x722));
    //掩码
    filter.FilterIdLow = (0x723 << 5) & 0xFFFF;
    //ID
    filter.FilterMaskIdLow = (msk << 5) & 0xFFFF;
    
    //ID组2
    uint16_t  msk1 = (0x741 ^ (~0x742));
    filter.FilterIdHigh = (0x741 << 5) & 0xFFFF;
    filter.FilterMaskIdHigh = (msk1 << 5) & 0xFFFF;
    
    //是否使能当前筛选器
    filter.FilterActivation = ENABLE;
    //筛选器模式     掩码模式
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    //筛选器长度     16位
    filter.FilterScale = CAN_FILTERSCALE_16BIT;
    //配置筛选器
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    //过滤器配置失败
    if(HAL_CAN_ConfigFilter(&hcan1, &filter) != HAL_OK)
    {
      my_printf("CAN1 ConfigFilter Fail!!\r\n");
    }
    return 1;
}

uint8_t bsp_can1_filter_config_16_list(void)
{
  //初始化筛选器
    CAN_FilterTypeDef filter = {0};
    //筛选器编号
    filter.FilterBank = 0;
    //存储FIFO    FIFO0
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    //ID1
    filter.FilterIdLow = (0x729 << 5) & 0xFFFF;
    //ID2
    filter.FilterMaskIdLow = (0x741 << 5) & 0xFFFF;
    //ID3
    filter.FilterIdHigh = (0x755 << 5) & 0xFFFF;
    //ID4
    filter.FilterMaskIdHigh = (0x769 << 5) & 0xFFFF;
    //是否使能当前筛选器
    filter.FilterActivation = ENABLE;
    //筛选器模式     掩码模式
    filter.FilterMode = CAN_FILTERMODE_IDLIST;
    //筛选器长度     16位
    filter.FilterScale = CAN_FILTERSCALE_16BIT;
    //配置筛选器
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    //过滤器配置失败
    if(HAL_CAN_ConfigFilter(&hcan1, &filter) != HAL_OK)
    {
      my_printf("CAN1 ConfigFilter Fail!!\r\n");
    }
    return 1;
}


//配置CAN过滤器
uint8_t bsp_can2_filter_config(void)
{
  //初始化筛选器
    CAN_FilterTypeDef filter = {0};
    //筛选器编号
    filter.FilterBank = 14;
    //存储FIFO    FIFO0
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    //筛选标准ID高位
    //filter.FilterIdHigh = 0x702 << 5;
    filter.FilterIdHigh = 0;
    //扩展ID高位
    //filter.FilterIdHigh = (0x1839f101 >> 13) & 0xFFFF;
    //筛选标准ID低位
    filter.FilterIdLow = 0;
    //扩展ID低位
    //filter.FilterIdLow = ((0x1839f101 << 3)| CAN_ID_EXT) & 0xFFFF;
    //筛选ID掩码高位          存放寄存器的高16位
    //filter.FilterMaskIdHigh = 0xFFFFFFFF;
    //计算掩码
    //uint16_t  msk = (0x701 ^ (~0x702));
    //uint16_t  msk = (0x1839f101 ^ (~0x1839f102));
    //标准ID掩码高位
    //filter.FilterMaskIdHigh = msk << 5;
    filter.FilterMaskIdHigh = 0;
    //扩展ID掩码低位
    //filter.FilterMaskIdHigh = (msk >> 16) & 0xFFFF;
    //标准ID掩码低位
    filter.FilterMaskIdLow = 0;
    //扩展ID掩码低位
    //filter.FilterMaskIdLow = msk & 0xFFFF;
    //是否使能当前筛选器
    filter.FilterActivation = ENABLE;
    //筛选器模式     掩码模式
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    //筛选器长度     32位
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    //配置筛选器
    HAL_CAN_ConfigFilter(&hcan2, &filter);
    //过滤器配置失败
    if(HAL_CAN_ConfigFilter(&hcan2, &filter) != HAL_OK)
    {
      my_printf("CAN2 ConfigFilter Fail!!\r\n");
    }
    return 1;
}

//编写CAN发送函数
void bsp_can1_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len)
{
  //初始化发送结构体
    CAN_TxHeaderTypeDef send_msg_hdr = {0};
    uint8_t index = 0;
    uint32_t msg_box = 0;
    uint8_t send_buf[8] = {0};
    //标识符ID
    send_msg_hdr.StdId = basic_id;
    //扩展标识符ID
    send_msg_hdr.ExtId = 0x1839f101;
    //扩展标志
    //send_msg_hdr.IDE = CAN_ID_EXT;
    send_msg_hdr.IDE = CAN_ID_STD;
    //远程控制标志
    send_msg_hdr.RTR = CAN_RTR_DATA;
    //有效数据位长度
    send_msg_hdr.DLC = data_len;
    //是否使能捕获时间戳计数器
    send_msg_hdr.TransmitGlobalTime = DISABLE;
    for(index = 0; index < data_len; index++)
        {
          send_buf[index] = data[index];
        }
    if((HAL_CAN_AddTxMessage(&hcan1,&send_msg_hdr,send_buf,&msg_box))!= HAL_OK)
    {
      my_printf("CAN1 Send data Err!!\r\n");
    }
    else
    {
      my_printf("CAN1 Send data Success!!\r\n");
    }
}

//编写CAN发送函数
void bsp_can2_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len)
{
  //初始化发送结构体
    CAN_TxHeaderTypeDef send_msg_hdr = {0};
    uint8_t index = 0;
    uint32_t msg_box = 1;
    uint8_t send_buf[8] = {0};
    //标识符ID
    send_msg_hdr.StdId = basic_id;
    //扩展标识符ID
    send_msg_hdr.ExtId = 0x1839f101;
    //扩展标志
    //send_msg_hdr.IDE = CAN_ID_EXT;
    send_msg_hdr.IDE = CAN_ID_STD;
    //远程控制标志
    send_msg_hdr.RTR = CAN_RTR_DATA;
    //有效数据位长度
    send_msg_hdr.DLC = data_len;
    //是否使能捕获时间戳计数器
    send_msg_hdr.TransmitGlobalTime = DISABLE;
    for(index = 0; index < data_len; index++)
        {
          send_buf[index] = data[index] + 1;
        }
    if((HAL_CAN_AddTxMessage(&hcan2,&send_msg_hdr,send_buf,&msg_box))!= HAL_OK)
    {
      my_printf("CAN2 Send data Err!!\r\n");
    }
    else
    {
      my_printf("CAN2 Send data Success!!\r\n");
    }
}

//接收数据
void Can1RecvMsg()
{
  //配置CAN过滤器
  bsp_can1_filter_config();
  //配置32位列表模式
  //bsp_can1_filter_config_list();
  //配置16位掩码模式
  //bsp_can1_filter_config_16();
  //16位列表模式
  //bsp_can1_filter_config_16_list();
  //初始话CAN接收结构体
  CAN_RxHeaderTypeDef  rceStu = {0};
  uint8_t data[8] = {0};
  //判断FIFO中是否有数据
  if(HAL_CAN_GetRxFifoFillLevel(&hcan1,CAN_RX_FIFO0) != 0)
  {
    my_printf("CAN1 have data!!\r\n");
    //接收CAN数据信息
    HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rceStu, data);
    my_printf("rceStu.DLC: %d\r\n",rceStu.DLC);
    my_printf("rceStu.ExtId: %d\r\n",rceStu.ExtId);
    my_printf("rceStu.StdId: %x\r\n",rceStu.StdId);
    my_printf("rceStu.Timestamp: %d\r\n",rceStu.Timestamp);
    for(uint8_t i = 0;i<rceStu.DLC;i++)
    {
      my_printf(" %x",data[i]);
    }
     my_printf("\r\n");
  }
  else
  {
    my_printf("No CAN1 INFO!\r\n");
  }
}

//接收数据
void Can2RecvMsg()
{
  //配置CAN过滤器
  bsp_can2_filter_config();
  //初始话CAN接收结构体
  CAN_RxHeaderTypeDef  rceStu = {0};
  uint8_t data[8] = {0};
  //判断FIFO中是否有数据
  if(HAL_CAN_GetRxFifoFillLevel(&hcan2,CAN_RX_FIFO0) != 0)
  {
    my_printf("CAN2 have data!!\r\n");
    //接收CAN数据信息
    HAL_CAN_GetRxMessage(&hcan2, CAN_RX_FIFO0, &rceStu, data);
    //打印有效数据长度
    my_printf("rceStu.DLC: %d\r\n",rceStu.DLC);
    //打印扩展ID
    my_printf("rceStu.ExtId: %d\r\n",rceStu.ExtId);
    //打印标准ID
    my_printf("rceStu.StdId: %x\r\n",rceStu.StdId);
    //打印接收时间
    my_printf("rceStu.Timestamp: %d\r\n",rceStu.Timestamp);
    for(uint8_t i = 0;i<rceStu.DLC;i++)
    {
      my_printf(" %x",data[i]);
    }
     my_printf("\r\n");
  }
  else
  {
    my_printf("No CAN2 INFO!\r\n");
  }
}

//CAN1}CAN2通信测试
void Can1AndCan2CommunicationTest()
{
  //CAN发送数据
    uint8_t data[10] = {0x66,0x64,0x55,0x33,0x66,0x33,0x44,0x55};
    //发送CAN数据信息
    bsp_can1_send_msg(CAN_ID_STD,0x729,2,data,8);
    //发送CAN数据信息
    bsp_can2_send_msg(CAN_ID_STD,0x723,2,data,8);
    //CAN1接收数据
    Can1RecvMsg();
    //CAN2接收数据
    Can2RecvMsg();
}

CANTest.h

#ifndef CANTEST_H_
#define CANTEST_H_
#include "can.h"
#include "comm.h"

#define MY_CAN_ID       0x7B1

//32位掩码模式
uint8_t bsp_can1_filter_config(void);

//32位掩码模式
uint8_t bsp_can2_filter_config(void);

//32位列表模式
uint8_t bsp_can1_filter_config_list(void);

//16位掩码模式
uint8_t bsp_can1_filter_config_16(void);

//16位列表模式
uint8_t bsp_can1_filter_config_16_list(void);

void bsp_can1_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len);

void bsp_can2_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len);

void Can1RecvMsg();

void Can2RecvMsg();

void Can1AndCan2CommunicationTest();

#endif


main.c

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_CAN1_Init();
  MX_CAN2_Init();
  MX_TIM3_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
  //开启CAN1
  HAL_CAN_Start(&hcan1);
  //开启CAN2
  HAL_CAN_Start(&hcan2);
  //配置CAN1过滤器
  bsp_can1_filter_config();
  //配置CAN2过滤器
  bsp_can2_filter_config();
  
  //Dht11重置处理
  //Dht11InitHandle();
  //OLED初始化
  //OLED_Init();
  //清屏操作
  //OLED_FullyClear();
  //固定位显示
  //FixedDisplay();
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    //led显示模块
    //LedModuleHandle();
    
    //CAN1\CAN2通信测试
    Can1AndCan2CommunicationTest();
    
    //Dht11模块处理
    //Dht11ModuleHandle();
    
    //OLED模块处理
    //TemperatureAndHumidityTest();
    
    //延时2S
    HAL_Delay(2000);
  }
  /* USER CODE END 3 */
}

下面展示串口打印接收的数据:

  • 36
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值