stm32f407单片机上通过HAL库实现can总线数据的收发

news2024/11/28 10:42:40

  最近在使用can总线,由于这个以前接触的比较少,所以调试代码的时候直接是下载的正点原子的例程,在这个基础上修改调试的。现在将调试中遇到的问题,总结一下,避免以后踩坑。目前写了一个查询方式的,一个中断方式的。项目代码下载地址:
https://download.csdn.net/download/qq_20222919/87793221

查询方式

  首先说查询模式,查询模式直接使用原子的例程就可以使用。初始化代码如下:

CAN_HandleTypeDef   g_canx_handler;     /* CANx句柄 */
CAN_TxHeaderTypeDef g_canx_txheader;    /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader;    /* 接收参数句柄 */

uint8_t can_init( uint32_t tsjw, uint32_t tbs2, uint32_t tbs1, uint16_t brp, uint32_t mode )
{
    g_canx_handler.Instance = CAN1;
    g_canx_handler.Init.Prescaler = brp;                /* 分频系数(Fdiv)为brp+1 */
    g_canx_handler.Init.Mode = mode;                    /* 模式设置 */
    g_canx_handler.Init.SyncJumpWidth = tsjw;           /* 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ */
    g_canx_handler.Init.TimeSeg1 = tbs1;                /* tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ */
    g_canx_handler.Init.TimeSeg2 = tbs2;                /* tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ */
    g_canx_handler.Init.TimeTriggeredMode = DISABLE;    /* 非时间触发通信模式 */
    g_canx_handler.Init.AutoBusOff = DISABLE;           /* 软件自动离线管理 */
    g_canx_handler.Init.AutoWakeUp = DISABLE;           /* 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) */
     //报文自动传送开启后,当数据发送失败时,can芯片会自动重发数据,直到数据发送成功,会造成程序假死状态。
    g_canx_handler.Init.AutoRetransmission = DISABLE;    /* 禁止报文自动传送 */
    g_canx_handler.Init.ReceiveFifoLocked = DISABLE;    /* 报文不锁定,数据溢出后新的数据覆盖旧的,如果使能锁定,数据溢出后新的数据会被丢掉 */
    g_canx_handler.Init.TransmitFifoPriority = DISABLE; /* 优先级由报文标识符决定 */
    if ( HAL_CAN_Init( &g_canx_handler ) != HAL_OK )
    {
        return 1;
    }
    CAN_FilterTypeDef sFilterConfig;
    /*配置CAN过滤器*/
    sFilterConfig.FilterBank = 0;                             /* 过滤器0 */
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000;                      /* 32位ID */
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;                  /* 32位MASK */
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;    /* 过滤器0关联到FIFO0 */
    sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;       /* 激活滤波器0 */
    sFilterConfig.SlaveStartFilterBank = 14;

    /* 过滤器配置 */
    if ( HAL_CAN_ConfigFilter( &g_canx_handler, &sFilterConfig ) != HAL_OK )
    {
        return 2;
    }
    /* 启动CAN外围设备 */
    if ( HAL_CAN_Start( &g_canx_handler ) != HAL_OK )
    {
        return 3;
    }
    return 0;
}
void HAL_CAN_MspInit( CAN_HandleTypeDef *hcan )
{
    if ( CAN1 == hcan->Instance )
    {
        CAN_RX_GPIO_CLK_ENABLE();       /* CAN_RX脚时钟使能 */
        CAN_TX_GPIO_CLK_ENABLE();       /* CAN_TX脚时钟使能 */
        __HAL_RCC_CAN1_CLK_ENABLE();    /* 使能CAN1时钟 */
        GPIO_InitTypeDef gpio_init_struct;
        gpio_init_struct.Pin = CAN_TX_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        gpio_init_struct.Alternate = GPIO_AF9_CAN1;
        HAL_GPIO_Init( CAN_TX_GPIO_PORT, &gpio_init_struct ); /* CAN_TX脚 模式设置 */
        gpio_init_struct.Pin = CAN_RX_GPIO_PIN;
        HAL_GPIO_Init( CAN_RX_GPIO_PORT, &gpio_init_struct ); /* CAN_RX脚 必须设置成输入模式 */
    }
}

   接收函数使用查询的方式,在接收函数值会不停的查询FIFO中数据的长度,如果数据长度不为0,说明就接收到了数据。在主函数中不停地读取接收函数返回的数据长度,通过数据长度判断是否接收到了数据。

uint8_t can_receive_msg( uint32_t id, uint8_t *buf )
{
    if ( HAL_CAN_GetRxFifoFillLevel( &g_canx_handler, CAN_RX_FIFO0 ) == 0 ) /* 没有接收到数据 */
    {
        return 0;
    }
    if ( HAL_CAN_GetRxMessage( &g_canx_handler, CAN_RX_FIFO0, &g_canx_rxheader, buf ) != HAL_OK ) /* 读取数据 */
    {
        return 0;
    } 
    return g_canx_rxheader.DLC;
}

   发送函数

uint8_t can_send_msg( uint32_t id, uint8_t *msg, uint8_t len )
{
    uint32_t TxMailbox = CAN_TX_MAILBOX0;
    g_canx_txheader.StdId = id;         /* 标准标识符 */
    g_canx_txheader.ExtId = id;         /* 扩展标识符(29位) */
    // g_canx_txheader.IDE = CAN_ID_STD;   /* 使用标准帧 */
    g_canx_txheader.IDE = CAN_ID_EXT; /* 使用扩展帧 */
    g_canx_txheader.RTR = CAN_RTR_DATA; /* 数据帧 */
    g_canx_txheader.DLC = len;
    if ( HAL_CAN_AddTxMessage( &g_canx_handler, &g_canx_txheader, msg, &TxMailbox ) != HAL_OK ) /* 发送消息 */
    {
        return 1;
    }
    while (HAL_CAN_GetTxMailboxesFreeLevel(&g_canx_handler) != 3); /* 等待发送完成,所有邮箱为空 */
    return 0;
}

   在主函数中不停的查询接收数据长度,如果接收到了数据,就把接收到的数据发送出去。

int main( void )
{  
    uint8_t i = 0, t = 0;
    uint8_t cnt = 0;
    uint8_t canbuf[8];
    uint8_t rxlen = 0;
    uint8_t res;
    uint8_t mode = 1; /* CAN工作模式: 0,普通模式; 1,环回模式 */
    uint16_t count = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init( 336, 10, 2, 7 );   /* 设置时钟,168Mhz */
    delay_init( 168 );                      /* 延时初始化 */
    usart_init( 115200 );                   /* 串口初始化为115200 */
 
    // can_init(CAN_SJW_1TQ, CAN_BS2_6TQ, CAN_BS1_7TQ, 6, CAN_MODE_LOOPBACK);  /* CAN初始化, 环回模式, 波特率500Kbps  采样点位置占57% */
    can_init( CAN_SJW_1TQ, CAN_BS2_6TQ, CAN_BS1_7TQ, 24, CAN_MODE_NORMAL ); /* CAN初始化, 正常模式, 波特率125Kbps  采样点位置占 57.1% */

    RE_DE( 1 );						  /* 485 设置为发送模式 */
    printf( "stm32f407 can test! \r\n" );
    while ( 1 )
    {
        rxlen = can_receive_msg( 0x12, canbuf ); /* CAN ID = 0x12, 接收数据查询 */
        if ( rxlen ) /* 接收到有数据 */
        {            
            printf( "接收到数据 %d : ID:0x%08X  data: ", count++, g_canx_rxheader.ExtId );
            for ( i = 0; i < 8; i++ )
            {
                printf( "%02X ", canbuf[i] );   /* 输出接收到的数据 */
            }
            printf( "\r\n" );
            can_send_msg( 0x02A300F0 + t, canbuf, 8 ); /* ID = 0x12, 发送8个字节 */
        }            
        t++;
        delay_ms( 10 );
        if ( t == 200 )
        {
            t = 0;
            cnt++;
            for ( i = 0; i < 8; i++ )
            {
                canbuf[i] = cnt + i;    /* 填充发送缓冲区 */
            }
//   res = can_send_msg(0x12, canbuf, 8);    /* ID = 0x12, 发送8个字节 */
        }
    }
}

  这里需要注意一个小问题,在初始化的时候,有一个报文自动传送的的属性g_canx_handler.Init.AutoRetransmission,原子在这里设置的是ENABLE。
在这里插入图片描述
  经过测试后发现,如果这个属性设置为ENABLE,那么发送数据的过程中,如果硬件电路上出现了故障,比如单片机can口上的连接线松动了,或者是连接线断了。can控制发送数据时就会失败,此时控制器就会一直尝试着继续发送,can口上就会一直有高低电平变化,此时代码就会卡在等待数据发送完成这条语句位置处。

while (HAL_CAN_GetTxMailboxesFreeLevel(&g_canx_handler) != 3);

在这里插入图片描述
  此时程序就会处于假死状态,如果发送函数后面还要执行其他代码的话,就执行不了了,除非can控制器将数据成功发送出去。

  如果不想要这种一直等待发送成功的方式,就将自动传送的属性设置为DISABLE,这样发送数据失败后,还会继续执行下面的代码。程序不会形成假死状态。

g_canx_handler.Init.AutoRetransmission = DISABLE;

中断方式

  中断模式原子的代码中虽然有一个宏定义可以开启中断,但是宏定义开启后,中断的功能依然使用不了。
在这里插入图片描述
  相当于使用中断接收的功能没有实现,于是就自己边查资料边测试,摸索着将中断功能实现了。
  首先进行初始化。

CAN_HandleTypeDef   g_canx_handler;     /* CANx句柄 */
CAN_TxHeaderTypeDef g_canx_txheader;    /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader;    /* 接收参数句柄 */

uint8_t can_init( uint32_t tsjw, uint32_t tbs2, uint32_t tbs1, uint16_t brp, uint32_t mode )
{
    g_canx_handler.Instance = CAN1;
    g_canx_handler.Init.Prescaler = brp;                /* 分频系数(Fdiv)为brp+1 */
    g_canx_handler.Init.Mode = mode;                    /* 模式设置 */
    g_canx_handler.Init.SyncJumpWidth = tsjw;           /* 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ */
    g_canx_handler.Init.TimeSeg1 = tbs1;                /* tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ */
    g_canx_handler.Init.TimeSeg2 = tbs2;                /* tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ */
    g_canx_handler.Init.TimeTriggeredMode = DISABLE;    /* 非时间触发通信模式 */
    g_canx_handler.Init.AutoBusOff = DISABLE;           /* 软件自动离线管理 */
    g_canx_handler.Init.AutoWakeUp = DISABLE;           /* 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) */
    //报文自动传送开启后,当数据发送失败时,can芯片会自动重发数据,直到数据发送成功,会造成程序假死状态。
    g_canx_handler.Init.AutoRetransmission = DISABLE;    /* 禁止报文自动传送 */
    g_canx_handler.Init.ReceiveFifoLocked = DISABLE;    /* 报文不锁锁定,FIFO装满后新的覆盖旧的,如果设置报文锁定后,FIFO装满后新的就会被丢弃*/
    g_canx_handler.Init.TransmitFifoPriority = DISABLE; /* 优先级由报文标识符决定 */
    if ( HAL_CAN_Init( &g_canx_handler ) != HAL_OK )
    {
        return 1;
    }

    /* 使用中断接收 */
    __HAL_CAN_ENABLE_IT( &g_canx_handler, CAN_IT_RX_FIFO0_MSG_PENDING ); /* FIFO0消息挂号中断允许 */
    HAL_NVIC_EnableIRQ( CAN1_RX0_IRQn );                        /* 使能CAN中断 */
    HAL_NVIC_SetPriority( CAN1_RX0_IRQn, 7, 0 );                /* 抢占优先级7,子优先级0 */

    /* HAL_CAN_ActivateNotification() 函数中会调用 __HAL_CAN_ENABLE_IT(hcan, ActiveITs);来开启指定的中断,
    所以如果使用了__HAL_CAN_ENABLE_IT()函数开启了中断,就不需要使用HAL_CAN_ActivateNotification()这个函数激活中断   */
    /* 如果不使用 __HAL_CAN_ENABLE_IT()函数  也可以单独使用HAL_CAN_ActivateNotification()函数  */                                                         /* 启动CAN1 */
//    HAL_CAN_ActivateNotification(&g_canx_handler,CAN_IT_RX_FIFO0_MSG_PENDING);    /* 启动CAN接收中断-FIFO0接收新消息*/

    CAN_FilterTypeDef sFilterConfig;
    /*配置CAN过滤器*/
    sFilterConfig.FilterBank = 0;                             /* 过滤器0 */
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000;                      /* 32位ID */
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;                  /* 32位MASK */
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;    /* 过滤器0关联到FIFO0 */
    sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;       /* 激活滤波器0 */
    sFilterConfig.SlaveStartFilterBank = 14;

    /* 过滤器配置 */
    if ( HAL_CAN_ConfigFilter( &g_canx_handler, &sFilterConfig ) != HAL_OK )
    {
        return 2;
    }
    /* 启动CAN外围设备 */
    if ( HAL_CAN_Start( &g_canx_handler ) != HAL_OK )
    {
        return 3;
    }
    return 0;
}

  中断模式初始化比查询模式初始化多了下面三行代码。

  __HAL_CAN_ENABLE_IT( &g_canx_handler, CAN_IT_RX_FIFO0_MSG_PENDING ); /* FIFO0消息挂号中断允许 */
    HAL_NVIC_EnableIRQ( CAN1_RX0_IRQn );     /* 使能CAN中断 */
    HAL_NVIC_SetPriority( CAN1_RX0_IRQn, 7, 0 );  /* 抢占优先级7,子优先级0 */

   首先使用 __HAL_CAN_ENABLE_IT函数设置中断源,接收中断有下面几种方式。

/* Receive Interrupts */
#define CAN_IT_RX_FIFO0_MSG_PENDING ((uint32_t)CAN_IER_FMPIE0)  /*!< FIFO 0 message pending interrupt */
#define CAN_IT_RX_FIFO0_FULL        ((uint32_t)CAN_IER_FFIE0)   /*!< FIFO 0 full interrupt            */
#define CAN_IT_RX_FIFO0_OVERRUN     ((uint32_t)CAN_IER_FOVIE0)  /*!< FIFO 0 overrun interrupt         */
#define CAN_IT_RX_FIFO1_MSG_PENDING ((uint32_t)CAN_IER_FMPIE1)  /*!< FIFO 1 message pending interrupt */
#define CAN_IT_RX_FIFO1_FULL        ((uint32_t)CAN_IER_FFIE1)   /*!< FIFO 1 full interrupt            */
#define CAN_IT_RX_FIFO1_OVERRUN     ((uint32_t)CAN_IER_FOVIE1)  /*!< FIFO 1 overrun interrupt         */

   这几种中断的差异就不一一解释了,这里使用FIFO0的消息挂起中断。还有一个函数 HAL_CAN_ActivateNotification 也可以设置中断源。

 HAL_CAN_ActivateNotification(&g_canx_handler,CAN_IT_RX_FIFO0_MSG_PENDING);  
 HAL_NVIC_EnableIRQ( CAN1_RX0_IRQn );     /* 使能CAN中断 */
 HAL_NVIC_SetPriority( CAN1_RX0_IRQn, 7, 0 );  /* 抢占优先级7,子优先级0 */  

   其实HAL_CAN_ActivateNotification 函数内部也是通过调用 __HAL_CAN_ENABLE_IT函数来设置中断源的。
在这里插入图片描述
   接下来在初始化回调函数中配置IO口。

void HAL_CAN_MspInit( CAN_HandleTypeDef* hcan )
{
    if ( CAN1 == hcan->Instance )
    {
        CAN_RX_GPIO_CLK_ENABLE();       /* CAN_RX脚时钟使能 */
        CAN_TX_GPIO_CLK_ENABLE();       /* CAN_TX脚时钟使能 */
        __HAL_RCC_CAN1_CLK_ENABLE();    /* 使能CAN1时钟 */
        GPIO_InitTypeDef gpio_init_struct;
        gpio_init_struct.Pin = CAN_TX_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        gpio_init_struct.Alternate = GPIO_AF9_CAN1;
        HAL_GPIO_Init( CAN_TX_GPIO_PORT, &gpio_init_struct ); /* CAN_TX脚 模式设置 */
        gpio_init_struct.Pin = CAN_RX_GPIO_PIN;
        HAL_GPIO_Init( CAN_RX_GPIO_PORT, &gpio_init_struct ); /* CAN_RX脚 必须设置成输入模式 */
    }
}

   初始化完成之后,下面就该设置中断函数的入口了,这个中断函数的名称可以在startup_stm32f407xx.s 文件里面找。
在这里插入图片描述
   在汇编代码的中断向量表里面有中断函数的名称,这里有个CAN1_RX0_IRQHandler 还有个 CAN1_RX1_IRQHandler,那么要使用哪个呢?
在网上找到相关资料如下
在这里插入图片描述
   经过验证,红线里面圈出来的说法是正确的,同时要注意如果要使用FIFO1,那么程序中所有设置FIFO的地方都得写成FIFO1.

在这里插入图片描述
   其中包括中断号,中断名称,中断入库函数名称,中断回调函数名称。
在这里插入图片描述
   由于上面初始化的时候设置的是使用FIFO0,那么这里的函数入口名称就选择CAN1_RX0_IRQHandler。

void CAN1_RX0_IRQHandler( void )
{
      /* 调用HAL库 CAN 中断入口函数*/
}

   接下来就需要在这个中断入口函数中调用HAL库的通用CAN中断处理函数,那这个函数在哪去找呢? 直接打开 stm32f4xx_hal_can.h 头文件在里面搜索以 IRQHandler结尾的函数名。

在这里插入图片描述
   查找后发现这个里面只有一个函数 HAL_CAN_IRQHandler符合,那么这个函数肯定就是HAL库中CAN中断的通用入口函数了。直接在中断函数里面添加这个通用函数。

void CAN1_RX0_IRQHandler( void )
{
    HAL_CAN_IRQHandler( &g_canx_handler );      /* 调用HAL库 CAN 中断入口函数*/
}

   接下来根据HAL库的惯例,在中断函数里面肯定还有一个回调函数。接下来就需要去找这个中断回调函数。可以直接跳转到 HAL_CAN_IRQHandler函数内部进行查看。
在这里插入图片描述
在这里插入图片描述
   但是这个函数内部的代码有些长,找起来不是很方便,那么就可以在 stm32f4xx_hal_can.h头文件里直接去找。由于回掉函数都是以Callback结尾的,那么就可以直接在 stm32f4xx_hal_can.h文件中搜索 Callback结尾的函数。
在这里插入图片描述

   可以找到一个Callbacks functions的注释,那么下面这些函数就是中断回调函数,如果程序写得比较多的时候,可以不用查找的方法,直接用鼠标拖动右边的滚动条就可以定位到回调函数这一块。
在这里插入图片描述
   但是这么多函数,到底是哪一个呢?别着急,还记得在初始化的时候,使能中断时开启了一个FIFO0的消息挂起中断,可以根据这个中断使能的名字去对比,可以发现在右边HAL_CAN_RxFifo0MsgPendingCallback这个函数的名字和CAN_IT_RX_FIFO0_MSG_PENDING名称基本一样,那么HAL_CAN_RxFifo0MsgPendingCallback这个函数肯定就是FIFO0消息挂起中断的回调函数。
在这里插入图片描述
   接下来就可以编写中断回调函数了。

void  HAL_CAN_RxFifo0MsgPendingCallback( CAN_HandleTypeDef* hcan )
{
    uint8_t rxbuf[8];
    uint32_t id;
    uint8_t i;
    HAL_CAN_GetRxMessage( hcan, CAN_RX_FIFO0, &g_canx_rxheader, rxbuf );  /* 读取数据 */
    printf( "接收到 %d 位数据 : ID:0x%08X  data: ", g_canx_rxheader.DLC, g_canx_rxheader.ExtId );
    for ( i = 0; i < 8; i++ )
    {
        printf( "%02X ", rxbuf[i] );   /* 输出接收到的数据 */
    }
    printf( "\r\n" );
}

   在中断回调函数里面使用HAL_CAN_GetRxMessage函数读取接收到的数据,并打印出来。到此中断函数的代码就编写完成了。

   接下来就是下载验证的过程了,这里就不一一列举了。中断部分的函数,需要注意的地方,在上面基本都分析过了。在调试的过程中,暂时也没发现其他需要注意的地方。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kubernetes第4天

第六章 Pod控制器详解 本章节主要介绍各种Pod控制器的详细使用。 Pod控制器介绍 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照pod的创建方式可以将其分为两类&#xff1a; 自主式pod&#xff1a;kubernetes直接创建出来的Pod&#xff0c;这种p…

Nginx的原理

Nginx的原理 1、mater 和 worker2、worker 如何进行工作的3、一个 master 和多个 woker 有好处4、设置多少个 woker 合适5、连接数 worker_connection 1、mater 和 worker 2、worker 如何进行工作的 3、一个 master 和多个 woker 有好处 &#xff08;1&#xff09;可以使用 ng…

Kubernetes第5天

第七章 Service详解 本章节主要介绍kubernetes的流量负载组件&#xff1a;Service和Ingress。 Service介绍 ​ 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着…

【计算机网络详解】——数据链路层(学习笔记)

&#x1f4d6; 前言&#xff1a;数据链路层提供了一种在不可靠的物理介质上传输数据的方式&#xff0c;并负责在网络层和物理层之间提供一个可靠的通信连接。本文将对数据链路层进行详细的介绍&#xff0c;包括数据链路层的定义、协议、功能和应用等方面。 目录 &#x1f552; …

Jenkins持续集成之jenkins安装入门教学

Jenkins安装 1、下载jenkins&#xff1b;官方地址&#xff1a;https://www.jenkins.io/ 2、点击Download 3、下载windows版本的安装包后缀为msi&#xff1b; 4、双击安装&#xff1b;如下图 5、安装到指定的盘&#xff1b;再点击next 6、勾选第一个框&#xff1b;再点击next 7…

《Netty》从零开始学netty源码(五十八)之NioEventLoop.execute()

目录 NioEventLoop.execute()addTask()startThread()NioEventLoop.run()select()处理keys与执行任务processSelectedKeys()处理AbstractNioChannelselectAgain() runAllTasks()fetchFromScheduledTaskQueue()runAllTasksFrom()afterRunningAllTasks() 带截止时间的runAllTasks(…

国考省考行测:图形推理题1,2平移,旋转,翻转

国考省考行测&#xff1a;图形推理题1,2平移&#xff0c;旋转&#xff0c;翻转 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff…

[数据结构 -- C语言] 栈(Stack)

目录 1、栈 1.1 栈的概念及结构 2、栈的实现 2.1 接口 3、接口的实现 3.1 初始化 3.2 入栈/压栈 3.3 出栈 3.4 获取栈顶元素 3.5 获取栈中有效元素个数 3.6.1 bool 类型接口 3.6.2 int 类型接口 3.7 销毁栈 4、完整代码 5、功能测试 1、栈 1.1 栈的概念及结构 …

软件测试工程师简历要怎么写,才能让HR看到

作为软件测试的从业者&#xff0c;面试或者被面试都是常有的事。 可是不管怎样&#xff0c;和简历有着理不清的关系&#xff0c;面试官要通过简历了解面试者的基本信息、过往经历等。】、 如果你不知道软件测试简历怎么写&#xff0c;可以看看这个视频是怎么写的&#xff0c;…

ARM-底层/Day2

.text .global _start _start:mov r0,#9mov r1,#15bl cmp_funccmp_func:cmp r0,r1beq stop 相等则跳转结束 subhi r0,r0,r1subcc r1,r1,r0mov pc,lr 不相等则返回执行 stop: b stop .end 循环实现1~100之间的和 .text .global _start _start:mov r0,#0mov r1,#1bl sum_fun…

CCF-CSP 202104-1 灰度直方图

简单的一题&#xff0c;理解题意&#xff0c;使用哈希数组即可 #include<iostream>using namespace std;int L,n,m; int mapp[505][505]; int arr[300];int main(){cin>>n>>m>>L;for(int i0;i<n;i){for(int j0;j<m;j){cin>>mapp[i][j];arr…

C++之STL

一、六大组件&#xff1a; 容器&#xff1a; 各种数据结构&#xff0c;如vector、list、deque、set、map等,用来存放数据&#xff0c;从实现角度来看&#xff0c;STL容器是一种class template。 算法&#xff1a; 各种常用的算法&#xff0c;如sort、find、copy、for_each。…

你也可以成为营销策划大咖,只需掌握这些技巧

本人是从业营销策划行业近10年的老广告人&#xff0c;我说的话你可以选择相信。 千万不要看轻了广告策划这个职业的技术含量&#xff0c;不是说你语言文字能力比较好就一定能够适合这个行业。 想要进入这个行业的大公司&#xff0c;你如果是刚毕业的新人的话&#xff0c;首先…

Linux的使用

强制停止 ctrlc 停止程序的运行退出当前命令的输入 退出或登出 ctrld 退出账户的登录退出某些特定程序的专属页面不能用于退出vi/vim 历史命令搜索 查看历史输入过的命令 history可通过! 命令前缀&#xff0c;自动执行上一次匹配前缀的命令&#xff08;用于最近的2~3/4~5个…

SpringBoot中如何处理MySQL中存储的JSON数据?

目录 一、MySQL中如何保存JSON类型的数据 1.1 建表 1.2 保存一条带json的记录 1.3 查询 二、Springboot操作当前数据库表 2.1 方式一&#xff08;推荐&#xff09; 2.2 方式二 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式 。简洁…

Nginx安装及其常用命令(实操版)

Nginx安装及其常用命令&#xff08;实操版&#xff09; 一、安装Nginx1、准备工作2、开始进行 nginx 安装 二、Nginx常用命令三、nginx.conf配置文件1、位置2、配置文件中的内容&#xff08;包含三部分&#xff09; 四、Nginx 反向代理实例 21、实现效果2、准备工作 五、Nginx …

从萌芽到巨人: 揭秘Rod Johnson与Spring框架的故事

前言 在这个充满创意和技术追求的世界中&#xff0c;有一个名为 Spring 的框架&#xff0c;它孕育了无数创新和成功的故事。这是一个关于持续进化和超越自我的故事&#xff0c;一个激励人心的旅程&#xff0c;由一位富有远见的程序员和他的团队引领着。从最初的概念到如今的成熟…

【Vue3+TS项目】硅谷甄选day02--后台管理系统模板搭建/项目配置

1 项目初始化 一个项目要有统一的规范&#xff0c;需要使用eslintstylelintprettier来对我们的代码质量做检测和修复&#xff0c;需要使用husky来做commit拦截&#xff0c;需要使用commitlint来统一提交规范&#xff0c;需要使用preinstall来统一包管理工具。 1.1 环境准备 n…

存储系统及主存储器

存储器概述 分类 存储器的分类如下&#xff1a; 主存的分类&#xff1a; 主存分为随机存储器&#xff08;RAM&#xff09;和静态存储器&#xff08;RAM&#xff09;&#xff0c;随机存储器又分为静态RAM和动态RAM 存储器的层次结构 金字塔结构 主存-辅存及主存-缓存结构 …

CentOS安装MySQL5.7/8.0

CentOS安装MySQL 0. 官方教程1. 添加MySQL Yum存储库1.1 官网下载yum存储库1.2 本地安装存储库 2. 安装MySQL数据库2.1 安装MySQL5.72.2 安装MySQL8.0 3. 开启并设置MySQL服务自启动4. 修改数据库root用户密码5. 设置root用户远程连接数据库【可选】 0. 官方教程 官网教程链接…