【CubeMX学习笔记】关于CAN通信协议

news2024/9/25 22:59:14

目录

一、CAN通信简介

二、CAN数据帧类型

三、格式帧

四、位同步

传输数据时可能遇到的问题

最小时间单位

硬同步

再同步

波特率的计算

=============STM32中的CAN外设=============

一、原理图

二、标识符筛选

三、配置单个邮箱(正常模式或自发自收只需要修改模式)

四、如何配置两个邮箱(用于接收两个电机的数据) 


一、CAN通信简介

can通信只有两根信号线(CAN High)和CAN_L(CAN Low),所有设备都搭载在这两根信号线上,可以实现多设备之间的高速实时的通信。其中每个设备都可以是主机/从机,都有对can总线的控制权。

can通信使用差分信号,不容易受干扰。当两根通信线的电压相同时,属于隐形电平1,当CANH被拉高而CANL被拉低时,属于显性电平0。在没有进行通信时,can总线表现为隐性电平1,当需要进行数据传输时,有且只有一个设备对can总线进行控制,其他设备选择是否接收。(当出现错误、过载或者应答时,其他设备才可以对总线进行控制)

二、CAN数据帧类型

这里主要了解的是数据帧。

三、格式帧

SOF:帧起始,数据传输开始时,总线从隐性电平1转变成显性电平0;

ID:11位设备ID号,表示设备信息,用于仲裁;

RTR:远程传输请求,用于区分数据帧(0)和遥控帧(1)(数据帧优先级高)

IDE:区分标准格式帧和扩展格式帧(0:为标准格式)

RB0:保留位(默认逻辑0)

DLC:4位,表示数据长度(字节),最多表示8字节,即64位;

CRC:校验位,检测数据是否错误;

CRC界定符:恒为隐性1,表示发送结束;

ACK:应答处理,发送端表现为隐性1,如果有设备接收了数据,那个设备会表现为显性电平1,此时整个can总线表现为显性电平;

ACK界定符:为隐性1,表示应答结束;

EOF:帧结束,为7位的隐性电平1;

SRR:用于代替RTR,真正的RTR在扩展ID后面,由于标准格式优先级高于扩展格式优先级,所以这一位虽然没用,但是必须为隐性1;

RB1,RB0:保留位(总是显性电平)

关于位填充

当发送数据连续五个位是相同的时,会自动发送一个相反位的数据,接收方接收时,也会自动去除这个位。

关于仲裁段

如果有两个设备同时开始发送数据,则会进行仲裁,ID号小的会在此获胜,继续数据发送,ID号大的则会退出。如果ID号相同,则标准格式帧优先。

四、位同步

CAN总线没有时钟线,总线上的所有设备通过约定波特率的方式确定每一个数据位的时长;

发送方以约定的位时长每隔固定时间输出一个数据位;

接收方以约定的位时长每隔固定时间采样总线的电平,输入一个数据位;

理想状态下,接收方能依次采样到发送方发出的每个数据位,且采样点位于数据位中心附近。

传输数据时可能遇到的问题

一个是接收设备的采样点可能位于跳变沿周围,导致采样信号不准确;一个是接收设备和发送设备的时钟有一点点偏差,导致采样信号不准确。

为了避免这两个问题,有硬同步再同步两个方法。

最小时间单位

为了灵活调整每个采样点的位置,使采样点对齐数据位中心附近,CAN总线传播时间段对每一个数据位的时长进行了更细的划分,分为同步段(SS)(PTS)、相位缓冲段1(PBS1)和相位缓冲段2(PBS2)每个段又由若干个最小时间单位(Tq)构成。后面三个数据可以自己指定长度。

其中PTS用于处理网络延迟,PBS1和PBS2长度可以改变,用于再同步。

硬同步

当有设备需要发送数据时,它会选择在自身时钟周期的ss段进行发送,此时其他设备接收到跳变信号,会将自己的时钟之间调整到ss段,实现同步。

再同步

为了解决设备时钟有偏差的问题,接收设备可以对自己的时间进行补偿,通过补偿宽度值(SWJ)加长PBS1或缩短PBS2对时序进行补偿。

波特率的计算

=============STM32中的CAN外设=============

一、原理图

CAN有三个发送邮箱,可以对要发送的消息进行缓存。

CAN有14个过滤器,可以对接收的报文ID进行过滤,通过过滤器的报文才会被存入接收FIFO。

接收FIFO有两个,每个有三级深度,也是缓存的功能,可以配置重要的报文进FIFO0,不重要的进FIFO1,防止重要的消息丢失。

二、标识符筛选

(1)ID表示需要接收的ID号,在掩码模式下,掩码位为1,则只能接收与ID相同的位一致的ID号,掩码位为0,则没有要求

(2)只有匹配两个ID的帧才能被筛选

(3)相当于(1)中ID低16位为ID,高16位为掩码,掩码低16位为ID,高16位为掩码

(4)相同

列表模式相当于所有位都匹配的掩码模式

三、配置单个邮箱(正常模式或自发自收只需要修改模式)

1、在CubeMX中的配置

配置传输速率位1Mbps,(根据自己的时钟来)

设置传输模式

开启接收中断

2、代码(普通模式或回环模式代码相同)

初始化

HAL_CAN_Start(&hcan1);//打开CAN
    CAN1_Filter_Init();//接收初始化
    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启中断
    {
        Error_Handler();
    }

can.c中配置宏

CANTxMsg_t TxMsg;  //定义发送邮件实体
CANRxMsg_t RxMsg;  //定义接收邮件实体
uint8_t rcvdFlag=0;        //接收标志位

can.c中编写的函数

//筛选器配置
void CAN1_Filter_Init(void)
{
        CAN_FilterTypeDef CAN1_FilerConf;
    /*筛选器具体的Id要求(配合掩码使用)
    比如:下面的掩码为 0x0101,即二进制0000|0001|0000|0001,表示我只关心从右数第一位,以及第9位。
    因此当具体Id要求为 0x0000,即二进制0000|0000|0000|0000。
    结合掩码实际就是筛选器最终允许通过的Id为
                                      xxxx|xxx0|xxxx|xxx0  (x表示0或者1都可以)
    因此具体筛选器允许的Id范围是需要结合掩码来看的
    */
        CAN1_FilerConf.FilterIdHigh=0X0000 << 5;                       //具体Id要求高16位
        CAN1_FilerConf.FilterIdLow=0X0000 << 5;                    //具体Id要求低16位
        //高16位左移五位即为标准id号
        CAN1_FilerConf.FilterMaskIdHigh=0X0000;                //掩码高16位全设置为0,表示对所有位报文Id高16位都不关心
        CAN1_FilerConf.FilterMaskIdLow=0X0000;                 //掩码低16位全设置为0,表示对所有位报文Id低16位都不关心
        CAN1_FilerConf.FilterFIFOAssignment=CAN_FILTER_FIFO0;  //筛选器接收到的报文放入到FIFO0中,即为接收邮箱0
        CAN1_FilerConf.FilterActivation=ENABLE;                //筛选器使能(开启)
        CAN1_FilerConf.FilterMode=CAN_FILTERMODE_IDMASK;       //筛选器掩码模式
        CAN1_FilerConf.FilterScale=CAN_FILTERSCALE_32BIT;      //掩码用32位表示
        /*这里说明一下为什么填0
            首先一个筛选器有28个组。一块stm32板子的can外设共用一个can外设
            比如F4系列基本都有两个can模块,如果只用一个can的时候,我们FilterBank可选0-13,因此下面就是0,其实填0-13都可以。
            如果用两个can,则要分配两个筛选器组。一个是FilterBank
            则
            can1的筛选器组选择0-13。如CAN1_FilerConf.FilterBank=0;   
            can2的筛选器组选择14-27。如CAN1_FilerConf.SlaveStartFilterBank=14;   
        */
        CAN1_FilerConf.FilterBank=0;                            
        CAN1_FilerConf.SlaveStartFilterBank=14;

        /*  此处&hcan1指明是can1的筛选器配置,但实际上can1和can2的筛选器都配置好了。因为两个can是共用的。但是接收FIFO每个CAN都有独立的两个。
            这是因为STM32的双路CAN共用过滤器组,
            而且过滤器组寄存器与CAN1配置寄存器在物理上是挨着的,HAL库将这些寄存器合并在一个结构里访问而已。
            下面通过调用 "HAL_CAN_ConfigFilter(&hcan1,&CAN1_FilerConf)" 配置can筛选器即可生效。
            无需再调用HAL_CAN_ConfigFilter(&hcan2,&CAN1_FilerConf)
        */
        if(HAL_CAN_ConfigFilter(&hcan1,&CAN1_FilerConf)!=HAL_OK)
        {
                Error_Handler();
        }
}

uint8_t CAN1_Send_Msg(CANTxMsg_t *msg,uint16_t mailbox_id,uint8_t *sendbuff)
{
        uint8_t id;

        msg->TxMessage.StdId=mailbox_id;            //邮箱id号
        msg->TxMessage.IDE=CAN_ID_STD;              //邮件的id格式(标准为CAN_ID_STD   |   拓展为CAM_ID_EXT)
        msg->TxMessage.DLC=8;                       //邮件数据长度 此处为8个字节
        msg->TxMessage.RTR=CAN_RTR_DATA;            //数据帧 一般都是数据帧
        msg->TxMessage.TransmitGlobalTime=DISABLE;  //默认DISABLE
        for(id=0;id<8;id++)
        {
                msg->payload[id]=sendbuff[id];  //装填数据
        }

        //发送邮件 注意邮件邮件信息(id号,邮件类型等等)和数据内容是分开发送的,具体看下面这句函数参数
        if(HAL_CAN_AddTxMessage(&hcan1,&msg->TxMessage,msg->payload,&msg->mailbox)!=HAL_OK)
                return 0;
        else
                return 1;
}
/*视频第4节有讲为什么要起这样的函数名字,这个叫回调函数
接收到邮件最终会来到这里。由于我们开了RX0中断,因此我们的回调函数名是
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
如果我们开的是RX1中断,那么回调函数名是void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan);
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    //这句话是判断对方发来的信息是发给本板子的can1还是can2,如果是can1,那通过接线就知道是哪个设备发来的数据。
        if(hcan->Instance==CAN1)
        {
            /*
                这个是获取邮件的函数,既然进了接收中断,那么就说明接收到了数据因此我们调用下方函数获取。
                我们提供一个RxMessage以及payload分别接收邮件邮件信息(id号,邮件类型等等)和数据内容。
            */
                if(HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&(RxMsg.RxMessage),(RxMsg.payload))==HAL_OK)
                        rcvdFlag=1;
                else
                        Error_Handler();
        }
        //HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
        //开启中断,在初始化已经调用
}

can.h中声明

#include "stm32f1xx_hal_can.h"

typedef struct
{
        uint32_t mailbox;
        CAN_TxHeaderTypeDef TxMessage;
        uint8_t payload[8];
}CANTxMsg_t;                          //发送结构体

typedef struct
{
        CAN_RxHeaderTypeDef RxMessage;
        uint8_t payload[8];
}CANRxMsg_t;                        //接收结构体
/* USER CODE END Includes */

extern CAN_HandleTypeDef hcan1;

/* USER CODE BEGIN Private defines */
extern uint8_t rcvdFlag;
extern CANTxMsg_t TxMsg;
extern CANRxMsg_t RxMsg;
/* USER CODE END Private defines */

void MX_CAN_Init(void);

/* USER CODE BEGIN Prototypes */
void CAN1_Filter_Init(void);
uint8_t CAN1_Send_Msg(CANTxMsg_t *msg,uint16_t mailbox_id,uint8_t *sendbuff);
void CAN1_Receive_Msg(CANRxMsg_t *msg);

主循环

uint8_t SendMsg[8]={1,2,3,4,5,6,7,8};  //发送的数据

CAN1_Send_Msg(&TxMsg,0X201,SendMsg); //发送数据
  while (1)//这部分可以直接在中断回调函数中写
  {
      if(rcvdFlag==1)//receive msg
            {
                    rcvdFlag=0;
                    if(RxMsg.RxMessage.StdId==0x201)
                      //将过滤器配置好,就没必要再判断id号了
                      //判断接收的ID号,回环模式下,ID相同
                    {
                            printf("%d %d %d %d\r\n",RxMsg.payload[0],RxMsg.payload[1],RxMsg.payload[2],RxMsg.payload[3]);
                    }
                      //打印接收的数据
            }
}

四、如何配置两个邮箱(用于接收两个电机的数据) 

打开fifo0和fifo1的中断

HAL_CAN_Start(&hcan1);//打开CAN
    CAN1_Filter_Init();//接收初始化
    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启FIFO0中断
    {
        Error_Handler();
    }
    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING) != HAL_OK)//开启FIFO1中断
    {
        Error_Handler();
    }

配置各自的接收结构体、标志位

CANTxMsg_t TxMsg;
CANRxMsg_t RxMsg0,RxMsg1;
uint8_t rcvdFlag1=0,rcvdFlag2=0;

初始化邮箱的筛选器(这里的代码是fifo0接收0x200的id号的数据,fifo1接收0x201id号的数据)

void CAN1_Filter_Init(void)
{
        CAN_FilterTypeDef CAN1_FilerConf;
        CAN1_FilerConf.FilterIdHigh=0X0200 << 5;//高16位左移五位即为标准id号
        CAN1_FilerConf.FilterIdLow=0X0000 << 5;
        CAN1_FilerConf.FilterMaskIdHigh=0Xffff;
        CAN1_FilerConf.FilterMaskIdLow=0Xffff;//掩码全部为f,表示所有位需要相同
        CAN1_FilerConf.FilterFIFOAssignment = CAN_FILTER_FIFO0;
        CAN1_FilerConf.FilterActivation=ENABLE;
        CAN1_FilerConf.FilterMode=CAN_FILTERMODE_IDMASK;//掩码模式
        CAN1_FilerConf.FilterScale=CAN_FILTERSCALE_32BIT;
        CAN1_FilerConf.FilterBank=0;                //小的先接收
        //CAN1_FilerConf.SlaveStartFilterBank=14;

        if(HAL_CAN_ConfigFilter(&hcan1,&CAN1_FilerConf)!=HAL_OK)//配置CAN过滤器
        {
                Error_Handler();
        }

        CAN1_FilerConf.FilterIdHigh=0X0201 << 5;
        CAN1_FilerConf.FilterIdLow=0X0000 << 5;
        CAN1_FilerConf.FilterMaskIdHigh=0Xffff;
        CAN1_FilerConf.FilterMaskIdLow=0Xffff;//掩码全部为f,表示所有位需要相同
        CAN1_FilerConf.FilterFIFOAssignment = CAN_FILTER_FIFO1;
        CAN1_FilerConf.FilterActivation=ENABLE;
        CAN1_FilerConf.FilterMode=CAN_FILTERMODE_IDMASK;//掩码模式
        CAN1_FilerConf.FilterScale=CAN_FILTERSCALE_32BIT;
        CAN1_FilerConf.FilterBank=1;
        //CAN1_FilerConf.SlaveStartFilterBank=14;

        if(HAL_CAN_ConfigFilter(&hcan1,&CAN1_FilerConf)!=HAL_OK)//配置CAN过滤器
        {
                Error_Handler();
        }
}

每个邮箱都有回调函数

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//FIFO0接收中断回调函数
{
        if(hcan->Instance==CAN1)
        {
                if(HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&(RxMsg0.RxMessage),(RxMsg0.payload))==HAL_OK)
                rcvdFlag1 = 1;
                else
                Error_Handler();
        }
        //HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
}

void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)//FIFO1接收中断回调函数
{
        if(hcan->Instance==CAN1)
        {
                if(HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO1,&(RxMsg1.RxMessage),(RxMsg1.payload))==HAL_OK)
                rcvdFlag2 = 1;
                else
                Error_Handler();
        }
        //HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
}

主循环判断标志位

while (1)
  {
      CAN1_Send_Msg(&TxMsg,0X200,SendMsg);//发送数据
      HAL_Delay(50);
      if(rcvdFlag1==1)//receive msg
            {
                    rcvdFlag1 = 0;
                    printf("%d %d %d %d\r\n",RxMsg0.payload[0],RxMsg0.payload[1],                                                RxMsg0.payload[2],RxMsg0.payload[3]);

            }
      if(rcvdFlag2==1)//receive msg
            {
                    rcvdFlag2 = 0;
                    printf("%d %d %d %d\r\n",RxMsg1.payload[4],RxMsg1.payload[5],
                                             RxMsg1.payload[6],RxMsg1.payload[7]);

            }
}

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

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

相关文章

探索 ShellGPT:终端中的 AI 助手

文章目录 探索 ShellGPT&#xff1a;终端中的 AI 助手背景介绍ShellGPT 是什么&#xff1f;如何安装 ShellGPT&#xff1f;简单的库函数使用方法场景应用常见问题及解决方案总结 探索 ShellGPT&#xff1a;终端中的 AI 助手 背景介绍 在当今快速发展的技术领域&#xff0c;命…

人体动捕相关算法

SMPL、SMPLify SMPL&#xff08;Skinned Multi-Person Linear (SMPL) Model&#xff09;Keep it SMPL: Automatic Estimation of 3D Human Pose and Shape from a Single Image&#xff0c;2015 SMPL 关节点数量&#xff1a;K23&#xff0c;其中0号节点&#xff08;不在K之…

一区黏菌算法+双向深度学习+注意力机制!SMA-BiTCN-BiGRU-Attention黏菌算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测

一区黏菌算法双向深度学习注意力机制&#xff01;SMA-BiTCN-BiGRU-Attention黏菌算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测 目录 一区黏菌算法双向深度学习注意力机制&#xff01;SMA-BiTCN-BiGRU-Attention黏菌算法优化双向时间卷积双向门控循环单元…

keil的debug功能

文章目录 一.窗口介绍二.功能2.1 debug断点调试和窗口变量2.2 四个花括号功能2.2.1 进去函数和下一步功能2.2.2 跳过函数和跳过该行代码功能2.2.3 函数内部跳出功能2.2.4 执行到光标处 2.3 查看内部寄存器 一.窗口介绍 二.功能 2.1 debug断点调试和窗口变量 先打开下图debug断…

Scanner流程控制语句

1. Scanner类 Scanner的意思是扫描 Scanner是JDK提供的一个类&#xff0c;位于java.util包下&#xff0c;所以我们如果需要使用则必须导包&#xff0c;导包的语句必须在声明包之后&#xff0c;在声明类之前 Scanner类是用来接受用户输入的各种信息 Scanner类提供了用于接受…

Ubuntu中交叉编译armdillo库

网上关于交叉编译armdillo库比较少&#xff0c;借鉴了一些但是在前几天编译时总是磕磕绊绊&#xff0c;于是写一个详细的编译过程。 交叉编译armdillo库包含两个步骤&#xff1a;交叉编译依赖库和交叉编译armdillo库。armdillo官网介绍依赖库如下图所示&#xff1a; 需要注意如…

01【MATLAB】最小二乘系统辨识

目录 1.系统辨识的定义及其分类 1.1 系统辨识的定义 1.2 系统辨识的分类 2.参数模型 3.系统辨识的步骤 一、最小二乘法&#xff08;Least Squares Method&#xff09;一般步骤 二、LSM原理及应用 三、LSM在控制系统建模中的应用 1.系统辨识的定义及其分类 1.1 系统辨识的…

Leetcode面试经典150题-201.数字范围按位与

给你两个整数 left 和 right &#xff0c;表示区间 [left, right] &#xff0c;返回此区间内所有数字 按位与 的结果&#xff08;包含 left 、right 端点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;left 5, right 7 输出&#xff1a;4示例 2&#xff1a; 输入&…

Kolmogorov-Arnold——代替 MLP以提高模型的代表性和性能

前言 论文地址&#xff1a;https://arxiv.org/abs/2409.10594 源码地址&#xff1a;https://github.com/Adamdad/kat.git 传统的变压器模型使用多层感知器&#xff08;MLP&#xff09;来混合通道间的信息&#xff0c;而本文则使用了科尔莫哥罗德网络&#xff08;KAN&#xff0…

微信第三方开发平台接入公众号整体技术方案

1.1 公众号简介 官网地址&#xff1a;微信公众平台 微信公众号 就像是你在微信里的一个“官方小号”&#xff0c;专门用来发布内容、和粉丝互动。用户可以关注你的公众号&#xff0c;收到你发布的消息&#xff0c;就像朋友圈那样。公众号还能提供很多实用功能&#xff0c;比如…

在 React 中模拟输入

需求 与 Bug 项目的 C# 桌面端使用 CefSharp 内嵌了一个三方网站&#xff0c;在外部实现了一个登录控件&#xff0c;外部登录后希望内嵌的三方网站自动登录&#xff0c;实现代码如下&#xff1a; browser.ExecuteScriptAsync($"document.getElementsByName(username)[0]…

s3c2440——ADC模数转换器,Linux驱动编程——u-boot

一、ADC 模拟&#xff1a;连续&#xff1b;数字&#xff1a;离散。 模拟信号一般指连续变化的电压值。转换的步骤&#xff1a;采样、量化。 ADC中soc电压转为数字信号的方法&#xff1a;逐次逼近法。 分辨率&#xff1a;nbit&#xff1b;表示一个电压比较器比较10次&#xf…

CoreDNS实现跨集群service解析实践

CoreDNS实现跨集群service解析实践 背景介绍使用条件实现方案 CoreDNS是一款使用Go语言实现的专为云原生应用而生的DNS服务器。本文介绍CoreDNS在特定实际场景下的一种进阶使用实践&#xff0c;也许能为其他也在使用CoreDNS做服务发现的同学提供一些启发和思考。 背景介绍 在…

三星推出990 EVO Plus固态硬盘,支持PCIe 4.0性能出色

容量高达4TB&#xff0c;提供增强的性能和能效。性能卓越&#xff0c;随机读写速度分别为为 1,050K IOPS 和 1,400K IOPS。 韩国——2024年9月25日—三星电子于今天宣布推出990 EVO Plus固态硬盘&#xff0c;为其固态硬盘产品线再添新成员。990 EVO Plus 支持 PCIe 4.0和最新的…

Icarus翼星求生教你使用服务器开服

1、购买后登录服务器&#xff08;百度莱卡云游戏面板&#xff09; 登录面板的信息在绿色的登陆面板按键下方&#xff0c;不是你的莱卡云账号 进入控制面板后会出现正在安装的界面&#xff0c;大约10分钟左右就能安装完成 2、创建端口 点击目录上的网络&#xff0c;再次页面下点…

kali-linux-2023.4 安装与配置

kali官网 作者&#xff1a;程序那点事儿 日期&#xff1a;2024/01/15 21:34 进入kali官网&#xff0c;点到下载页面 选择安装方式&#xff08;本次私用虚拟机安装&#xff09;。裸机安装是指&#xff0c;先要安装虚拟机&#xff08;例如&#xff1a;CentOS7&#xff09…

【算法】贪心+堆排序实现大根堆及标准库容器类的融合使用

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

centos7安装Redis单机版

一、检查是否有GCC环境 gcc --version # 提示-bash: gcc: 未找到命令 说明没有gcc环境# 安装gcc环境 yum install gcc# 如果yum源报错 # 1.检查网络是否正常 ping www.baidu.com # 2.备份当前的yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo…

IntraWeb制作汉堡菜单

IntraWeb开发web网站时如何制作手机页面&#xff1f; delphi源代码&#xff1a;示例两列布局带顶部汉堡菜单&#xff08;兼容电脑与手机&#xff09; 功能&#xff1a;交互式网页&#xff0c;两列布局&#xff0c;顶部汉堡菜单&#xff0c;点击汉堡图标关闭左侧栏&#xff0c;…

【NLP】基于“检测器-纠错器”中文文本纠错框架

前言 许多方法将中文拼写纠正&#xff08;检测和纠正给定中文句子中的错误字符&#xff09;视为序列标注任务&#xff0c;并在句子对上进行微调。一些方法使用错误检测器作为初步任务&#xff0c;然后将检测结果用于辅助后续的错误纠正过程。然而&#xff0c;现有方法在使用检…