CAN总线协议的理解以及移植stm32代码并使用

news2024/11/23 16:14:08

什么是CAN总线协议

是一种异步半双工的通讯协议,只有CAN_High与CAN_Low两条信号线。
有两种连接形式:闭环总线(高速)和开环总线(远距离)

他使用的是一种差分信号来传输电信号
所谓差分信号就是两条信号线电平之差,来传输电信号1/0,

该方式优点是抗干扰能力强、可以远距离传输,因为当信号线过长的时候电阻累积过大,使得传输到的电信号不准确,而差分信号是使用两条信号线电平差,两条信号线只要长度相似信号误差都不会很大。

电位差为0时为逻辑1,电位差>=2.0时为逻辑0。

CAN协议的协议层原理

协议层内规定了一个数据位有四个段,分别为:SS段、PTS段、PBS1段、PBS2段
1Tq为1个时基单元
在这里插入图片描述
SS段:是同步段,若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。SS 段的大小固定为 1Tq。

PTS段:是传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS 段的大小可以为 1~8Tq。

PBS1段:PBS1 译为相位缓冲段,补偿SS段的误差,它的时间长度在重新同步的时候可以加长,如下图相位超前,既ss段比预计的时间超期一个时间基准,可以通过PBS1段校准。

PBS2段:这是另一个相位缓冲段,它的时间长度在重新同步时可以缩短,补偿整个数据对齐。
在这里插入图片描述

CAN报文

当节点1与节点2同时发送报文时,总线电平会有优先级,按照ID优先级选择其1
当节点1或节点2发现自身电平和总线电平不同时会停止发送报文,总线显性电平为0,有1和0时总线电平取0

帧种类及用途:

在这里插入图片描述

数据帧的结构

在这里插入图片描述
仲裁段:0为最高优先级,用于总线判断优先级
控制段:

  • RTR位为0表示数据帧(发送数据的),为1表示遥控帧(请求数据的)
  • IDE位为0表示标准格式,为1表示为扩展格式
  • DLE表示本报文的数据有多少个字节,4个数据位,不过只能写入0~8
  • SRR 位 ,只存在于扩展格式,它用于替代标准格式中的 RTR位。由于扩展帧中的 SRR 位为隐性位,RTR 在数据帧为显性位,所以在两个 ID 相同的标准格式报文与扩展格式报文中,标准格式的优先级较高。

CRC 段:为了保证报文的正确传输,CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的CRC 码跟接收到的 CRC 码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC 部分的计算一般由 CAN 控制器硬件完成,出错时的处理则由软件控制最大重发数。在 CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK段间隔起来。

ACK 段:ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界定符间隔开。

STM32的CAN外设

stm32f103c8芯片中带有CAN的控制器,但不带CAN的收发器,所以如果想用最小系统板进行CAN外设实验,需要自己连接CAN的收发器。

在这里插入图片描述

CAN内部框图剖析

有一下五个主要部分

  • CAN控制内核
  • CAN发送邮箱
  • CAN接收fifo
  • 验收筛选器
  • 整体控制逻辑

stm32的CAN外设寄存器

主要使用的功能:

  • DBF调试冻结功能:可设置CAN处于工作状态或者禁止收发状态,静止收发时仍可访问接收FIFO的数据
  • TTCM:生成时间戳,在CAN_RDTxR和CAN_TDTxR中保存
  • ABOM:自动离线管理,在节点检测到它发送错误或者接收错误超过一定值时会自动进入离线状态。
  • AWUM:自动唤醒,如果软件进入CAN外设的低功耗模式,如果使能了该功能,当CAN总线检测到活动时自动换醒。
  • NART:自动重传,报文发送失败会自动重传至成功为止
  • RFLM:锁定模式,使能该模式的话,只有6个接收FIFO如果6个满了还未处理则第7个来时不接入FIFO,否则会覆盖第一个FIFO
  • TXFP:报文发送优先级判断方法:ID优先级/时序优先级

寄存器

工作模式寄存器CAN_BTR

寄存器的31位30位分别控制两个模式在这里插入图片描述
31位:是否静默
30位:是否回环
23-20位:TS2,表示TSS2占用多少个时间单元,TSS2=Tq*(TS2+1)
19-15位:TS1,与上同理
25-24位:SJW,定义每次可活动增减TS1与TS2数据位的最大值(调整时序时最大增减的时间单元个数)
9-0位:BRP,波特率分频器Tq=(BRP+1)*tPCLK

其他寄存器

  • 标识符寄存器CAN_ITxR:存储发送报文的ID、扩展ID,IDE位和RTR位,其中的TMIDxR_TxRQ位置1即可以发送数据
  • 数据长度控制寄存器CAN_TDTxR:存储待发送报文的DLC段
  • 低位数据寄存器CAN_TDLxR:存储发送报文的Date0~Date3
  • 高位数据寄存器CAN_TDHxR:存储发送报文的Date4~Date7

CAN的接收邮箱

每个CAN有两个接收FIFO邮箱,每个邮箱有3级
两个FIFO接收邮箱公用28个筛选器组(f103是14个、f105是28个)
Alt

掩码模式

一个筛选器有两个寄存器,在上图的掩码映射中,写入1就代表ID寄存器中的这一位需要筛选,如果全写入0,即不筛选。

stm32的CAN代码移植使用

在hal_can.h文件中

#define GPIO_CAN_RX_Pin                 GPIO_Pin_8
#define GPIO_CAN_TX_Pin                 GPIO_Pin_9

初始化CAN的GPIO重定义引脚

/****************************************************************************
*@*名称 : hal_CAN_GPIO_Config
*@*功能 : CAN的引脚口(PB8R/9T)
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_GPIO_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct_CAN_TX;
	GPIO_InitTypeDef  GPIO_InitStruct_CAN_RX;
	

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap1_CAN1,ENABLE);
	
	GPIO_InitStruct_CAN_TX.GPIO_Pin = GPIO_CAN_TX_Pin ;
	GPIO_InitStruct_CAN_TX.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct_CAN_TX.GPIO_Speed = GPIO_Speed_50MHz;	
	
	GPIO_Init(GPIOB,&GPIO_InitStruct_CAN_TX);
	
	GPIO_InitStruct_CAN_RX.GPIO_Pin = GPIO_CAN_RX_Pin ;
	GPIO_InitStruct_CAN_RX.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct_CAN_RX.GPIO_Speed = GPIO_Speed_50MHz;	
	
	GPIO_Init(GPIOB,&GPIO_InitStruct_CAN_RX);
}

初始化CAN的模式

这里需要提一下,因为核心板没有CAN的接收器,如果要验证功能,要使用回环模式

/****************************************************************************
*@*名称 : hal_CAN_Mode_Config
*@*功能 : 定义CAN模块的模式
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_Mode_Config(void)
{
	CAN_InitTypeDef	GAN_InitStruct_Mode;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);

	/*CAN寄存器初始化*/
	CAN_DeInit(CAN1);
	CAN_StructInit(&GAN_InitStruct_Mode);
	
	GAN_InitStruct_Mode.CAN_Mode = CAN_Mode_Normal; //普通工作模式,CAN_Mode_LoopBack为回环模式
	GAN_InitStruct_Mode.CAN_SJW = CAN_SJW_1tq; //可活动时间单元1tq
	GAN_InitStruct_Mode.CAN_BS1 = CAN_BS1_5tq;
	GAN_InitStruct_Mode.CAN_BS2 = CAN_BS2_3tq;
	/*CAN单元初始化*/
	GAN_InitStruct_Mode.CAN_TTCM=DISABLE;			   //MCR-TTCM  关闭时间触发通信模式使能
	GAN_InitStruct_Mode.CAN_ABOM=ENABLE;			   //MCR-ABOM  自动离线管理 
	GAN_InitStruct_Mode.CAN_AWUM=ENABLE;			   //MCR-AWUM  使用自动唤醒模式
	GAN_InitStruct_Mode.CAN_NART=DISABLE;			   //MCR-NART  禁止报文自动重传	  DISABLE-自动重传
	GAN_InitStruct_Mode.CAN_RFLM=DISABLE;			   //MCR-RFLM  接收FIFO 锁定模式  DISABLE-溢出时新报文会覆盖原有报文  
	GAN_InitStruct_Mode.CAN_TXFP=DISABLE;			   //MCR-TXFP  发送FIFO优先级 DISABLE-优先级取决于报文标示符 
	
	/* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB1 = 36 MHz) */
	GAN_InitStruct_Mode.CAN_Prescaler =4;		   BTR-BRP 波特率分频器  定义了时间单元的时间长度 36/(1+5+3)/4=1 Mbps
	
	CAN_Init(CAN1,&GAN_InitStruct_Mode)
}

初始化筛选器

/****************************************************************************
*@*名称 : hal_CAN_Filter_Config
*@*功能 : 初始化CAN模块的过滤器
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_Filter_Config(void)
{
	CAN_FilterInitTypeDef GAN_InitStruct_Filter0;
	CAN_FilterInitTypeDef GAN_InitStruct_Filter1;
	
	GAN_InitStruct_Filter0.CAN_FilterActivation = ENABLE;			//使能筛选器
	GAN_InitStruct_Filter0.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0 ;				//筛选器被关联到FIFO0
	GAN_InitStruct_Filter0.CAN_FilterIdHigh = (((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data))>>16;
	GAN_InitStruct_Filter0.CAN_FilterIdLow = ((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data);
	GAN_InitStruct_Filter0.CAN_FilterMaskIdHigh = 0xFFFF;			//筛选器高16位每位必须匹配
	GAN_InitStruct_Filter0.CAN_FilterMaskIdLow= 0xFF00;			//筛选器低16位每位必须匹配
	GAN_InitStruct_Filter0.CAN_FilterMode = CAN_FilterMode_IdMask;		//工作在掩码模式
	GAN_InitStruct_Filter0.CAN_FilterNumber = 0;
	GAN_InitStruct_Filter0.CAN_FilterScale = CAN_FilterScale_32bit;	//筛选器位宽为单个32位。
	
	CAN_FilterInit(&GAN_InitStruct_Filter0);
	/*CAN通信中断使能*/
	
	
	GAN_InitStruct_Filter1.CAN_FilterActivation = ENABLE;			//使能筛选器
	GAN_InitStruct_Filter1.CAN_FilterFIFOAssignment = CAN_Filter_FIFO1 ;				//筛选器被关联到FIFO1
	GAN_InitStruct_Filter1.CAN_FilterIdHigh = ((CAN_FIFO1_ID<<3)|CAN_Id_Extended|CAN_RTR_Data)>>16;
	GAN_InitStruct_Filter1.CAN_FilterIdLow = ((CAN_FIFO1_ID<<3)|CAN_Id_Extended|CAN_RTR_Data);
	GAN_InitStruct_Filter1.CAN_FilterMaskIdHigh = 0xFFFF;			//筛选器高16位每位必须匹配
	GAN_InitStruct_Filter1.CAN_FilterMaskIdLow= 0xFF00;			//筛选器低16位每位必须匹配
	GAN_InitStruct_Filter1.CAN_FilterMode = CAN_FilterMode_IdMask;		//工作在掩码模式
	GAN_InitStruct_Filter1.CAN_FilterNumber = 2;
	GAN_InitStruct_Filter1.CAN_FilterScale = CAN_FilterScale_32bit;	//筛选器位宽为单个32位。
	
	CAN_FilterInit(&GAN_InitStruct_Filter1);
	/*CAN通信中断使能*/
	CAN_ITConfig(CAN1, CAN_IT_FMP1, ENABLE);
	CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
}

这里我打开了两个接收FIFO
打开两个接收FIFO时需要注意:
该筛选器关联到的FIFO:GAN_InitStruct_Filter1.CAN_FilterFIFOAssignment
筛选器编号:GAN_InitStruct_Filter1.CAN_FilterNumber
这里不要配置好了一个,另一个就直接复制结构体导致错误了。

筛选器掩码配置要注意:因为低3位是配置RTR的标志位,高29位才是掩码ID配置,所以一开始将掩码ID左移3位,然后在或上IDE 位标志“宏 CAN_ID_EXT”以及RTR 位标
“宏 CAN_RTR_DATA”然后再赋值入筛选器的高八位和低八位

GAN_InitStruct_Filter0.CAN_FilterIdHigh = (((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data))>>16;
GAN_InitStruct_Filter0.CAN_FilterIdLow = ((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data);
GAN_InitStruct_Filter0.CAN_FilterMaskIdHigh = 0xFFFF; //筛选器高16位每位必须匹配
GAN_InitStruct_Filter0.CAN_FilterMaskIdLow= 0xFF00; //筛选器低16位每位必须匹配

初始化CAN的外设中断NVIC

/****************************************************************************
*@*名称 : hal_CAN_NVIC_Config
*@*功能 : 初始化CAN模块的中断
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_NVIC_Config(void)
{
	  NVIC_InitTypeDef NVIC_InitStructure;
    /* Configure one bit for preemption priority */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    /*中断设置*/
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;	   //CAN1 RX0中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;		   //抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			   //子优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
	
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    /*中断设置*/
    NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX1_IRQn;	   //CAN1 RX0中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;		   //抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			   //子优先级为1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

这里要注意,想要打开两个FIFO的中断是有区别的
FIFO0的中断通道名称是:USB_LP_CAN1_RX0_IRQn
FIFO1的中断通道名称是:CAN1_RX1_IRQn

FIFO0的中断函数名称是:void USB_LP_CAN1_RX0_IRQHandler(void)
FIFO1的中断函数名称是:void CAN1_RX1_IRQHandler(void)

CAN发送函数

/****************************************************************************
*@*名称 : CAN_SetMsg
*@*功能 : 配置CAN的发送信箱进行发送
*@*形参 : CanTxMsg型的结构体地址,发送的拓展ID,发送的数据存放地址
*@*返回值 : 无
****************************************************************************/
void CAN_SetMsg(CanTxMsg *TxMessage,uint32_t EXT_ID ,uint8_t* Data)
{	  
	uint8_t ubCounter = 0;

  //TxMessage.StdId=0x00;						 
  TxMessage->ExtId=EXT_ID;					 //使用的扩展ID
  TxMessage->IDE=CAN_ID_EXT;					 //扩展模式
  TxMessage->RTR=CAN_RTR_DATA;				 //发送的是数据
  TxMessage->DLC=8;							 //数据长度为8字节
	
	/*设置要发送的数据0-7*/
	for (ubCounter = 0; ubCounter < 8; ubCounter++)
  {
    TxMessage->Data[ubCounter] = Data[ubCounter];
  }
	
	
	CAN_Transmit(CAN1, TxMessage);//将TMIDxR_TxRQ标志位置1,发送CAN发送邮箱的报文发送出去
}

接收中断函数编写

/****************************************************************************
*@*名称 : USB_LP_CAN1_RX0_IRQHandler
*@*功能 : CAN模块的FIFO0接收中断函数
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void USB_LP_CAN1_RX0_IRQHandler(void)
{
	/*从邮箱中读出报文*/
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);

	/* 比较ID是否为0x1111 */ 
	if((RxMessage.ExtId >= 0x1110) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )
	{
		CAN_flag = 1; 					       //接收成功  
	}
	else
	{
		CAN_flag = 0; 					   //接收失败
	}
}

总结

在以上函数中我打开了两个接收FIFO,希望能一次性接收6个CAN的报文,但在实际实验中,连续发送6个报文,只能接收3个成功。
查了好久的原因还是没有解决该问题,如果有大佬知道为什么,希望能帮我解惑。

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

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

相关文章

软件测试|黑盒测试方法论-判定表

在因果图分析法中最后会得出一个判定表&#xff0c;可以看出因果图和判定表是有联系的&#xff0c;一般需要结合起来使用。 因果图是一种分析工具&#xff0c;通过分析最终得到判定表&#xff0c;再通过判定表编写测试用例。在一定情况下也可以直接书写判定表&#xff0c;省略…

Stable Diffusion webui 源码调试(一)

Stable Diffusion webui 源码调试&#xff08;一&#xff09; 个人模型主页&#xff1a;LibLibai stable-diffusion-webui 版本&#xff1a;v1.4.1 内容更新随机&#xff0c;看心情调试代码~ 调试txt2img的参数和工作流 文件 /work/stable-diffusion-webui/modules/txt2img…

软件测试|selenium执行js脚本

JavaScript是运行在客户端&#xff08;浏览器&#xff09;和服务器端的脚本语言&#xff0c;允许将静态网页转换为交互式网页。可以通过 Python Selenium WebDriver 执行 JavaScript 语句&#xff0c;在Web页面中进行js交互。那么js能做的事&#xff0c;Selenium应该大部分也能…

地铁机电设备健康管理现状及改善方法

轨道交通和我们的生活息息相关&#xff0c;从火车到地铁再到轻轨&#xff0c;给人们的出行带来了很大的便利。因此&#xff0c;保障轨道交通的的正常运行和安全至关重要&#xff0c;需要运维人员及时排查设备的问题&#xff0c;解决故障&#xff0c;保证轨道交通的安全运行。本…

Cygwin工具制作Redis服务端Window版本

文章目录 前言一、cygwin是什么&#xff1f;二、cygwin安装Redis源码编译 前言 在学习到redis&#xff0c;经常需要用到一个redis服务端&#xff0c;如果有买服务器或者本机可以支持经常开虚拟机&#xff0c;也是可以的&#xff0c;如果不具备这些条件&#xff0c;还是本机win…

Leetcode刷题详解——字母大小写全排列

1. 题目链接&#xff1a;784. 字母大小写全排列 2. 题目描述&#xff1a; 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。 示例 1&#xff1a; 输入&…

2023年9月少儿编程 中国电子学会图形化编程等级考试Scratch编程二级真题解析(判断题)

2023年9月scratch编程等级考试二级真题 判断题(共10题,每题2分,共20分) 26、下列两个程序运行效果一样 答案:对 考点分析:考查积木综合使用,重点考查重复执行和坐标积木 两个程序都是在x=0,y=100的时候停止,所以正确 27、甲、乙和丙,一位是山东人,一位是河南人,…

死锁问题概述

文章目录 死锁的概念死锁的定义相似概念&#xff1a;饥饿死锁产生的原因死锁产生的必要条件死锁的预防破坏互斥条件破坏不可剥夺/不可抢占条件破坏请求并保持条件破坏循环等待条件 死锁避免安全性算法 死锁的处理策略死锁的检测死锁的解除 死锁的概念 死锁的定义 多个进程由于…

什么是代理IP池?如何判断IP代理商的IP池是否真实优质?

代理池充当多个代理服务器的存储库&#xff0c;提供在线安全和匿名层。代理池允许用户抓取数据、访问受限制的内容以及执行其他在线任务&#xff0c;而无需担心被检测或阻止的风险。代理池为各种在线活动&#xff08;例如网页抓取、安全浏览等&#xff09;提高后勤保障。 读完…

<C++> list模拟实现

目录 前言 一、list的使用 1. list的构造函数 2. list iterator的使用 3. list capacity 4. list modifiers 5. list的算法 1. unique​ 2. sort 3. merge 4. remove 5. splice 二、list模拟实现 1. 设置节点类 && list类 2. push_back 3. 迭代器 重载 * 重载前置 …

统计一个只包含大写字母的字符串中顺序对的数量.其中顺序对的定义为前面的字符小后面的字符大.例如在“ABC“中的顺序对为3,因为有AB,AC,BC

哈希法&#xff1a;扫描字符串&#xff0c;将出现的字符次数加1&#xff0c;统计比当前字符字典序小的字母出现的次数&#xff0c;即为顺序串的个数。 int CounSq(const char* arr)//时间复杂度O&#xff08;n&#xff09; {int sig[26] { 0 };int index 0;int sum 0;for (…

【TiDB】TiDB CLuster部署

目录 0 大纲 一 集群部署工具TiUP简介 1 TiUP 简介 2 TiUP使用 3 TiUP使用举例 二 TiDB Cluster安装配置需求 1 生产环境硬件需求 2 操作系统需求 三 TIDB部署 1 软硬件需求以及前置检查​编辑 2 安装TiUP 组件 ​3 集群拓扑文件 4 执行部署命令 &#xff08;1&…

如何使用 NFTScan NFT API 在 zkSync 网络上开发 Web3 应用

zkSync 是由 Matter Labs 创建的&#xff0c;是一个以用户为中心的 zk rollup 平台&#xff0c;它是以太坊的第 2 层扩展解决方案&#xff0c;使用 zk-rollups 作为扩展技术&#xff0c;与 optimistic rollups 一样&#xff0c;zk-rollups 将会汇总以太坊主网上的交易并将交易证…

SSM图书管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 图书管理系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和 数据库&#xff0c;系统主要…

模拟ASP.NET Core MVC设计与实现

前几天有人在我的《ASP.NET Core框架揭秘》读者群跟我留言说&#xff1a;“我最近在看ASP.NET Core MVC的源代码&#xff0c;发现整个系统太复杂&#xff0c;涉及的东西太多&#xff0c;完全找不到方向&#xff0c;你能不能按照《200行代码&#xff0c;7个对象——让你了解ASP.…

Hive 知识点八股文记录 ——(一)特性

Hive通俗的特性 结构化数据文件变为数据库表sql查询功能sql语句转化为MR运行建立在hadoop的数据仓库基础架构使用hadoop的HDFS存储文件实时性较差&#xff08;应用于海量数据&#xff09;存储、计算能力容易拓展&#xff08;源于Hadoop&#xff09; 支持这些特性的架构 CLI&…

Annotation Processor

Annotation Processor Processor处理约定 JavaC编译环境获取当前的源文件及类文件&#xff0c; 建立多轮次的处理过程。每一次轮次的处理结果将作为下一轮的输入。当某一轮处理完成后没有产生新的源文件或类文件&#xff0c;触发最后一轮。Processors 通过getSupportedAnnotat…

“位不配财”?程序员兼职,稳妥挣钱才是王道!

一、配不上 戏称程序员为“码农”&#xff0c;一年到头&#xff0c;像那地里的老黄牛和勤勤恳恳的老农民。 又像极了那工地上的农民工&#xff0c;天天搬砖&#xff0c;苦得嘞。 作为推动时代进步的得力干将&#xff0c;工作量自然是不容小觑。说程序员不加班都没人信&#x…

【前段基础入门之】=>CSS3新特性 文本多列 布局

概述&#xff1a; 作用&#xff1a;专门用于实现类似于报纸的布局。属于是一行文本多列布局 属性/值描述column-count 指定列数&#xff0c;值是数字。column-width指定列宽&#xff0c;值是长度单位columns同时指定列宽和列数&#xff0c;复合属性&#xff1b;值没有数量和顺序…

2023年眼镜行业分析(京东眼镜销量数据分析):市场规模同比增长26%,消费需求持续释放

随着我国经济的不断发展&#xff0c;电子产品不断普及&#xff0c;低龄及老龄人口的用眼场景不断增多&#xff0c;不同年龄阶段的人群有不同的视力问题&#xff0c;因此&#xff0c;视力问题人口基数也随之不断加大&#xff0c;由此佩戴眼镜的人群也不断增多。 同时&#xff0c…