前面对CAN原理进行了讲解,这里我用一个简单的例子来说明CAN的使用。我使用的STM32芯片是STM32F103ZE,几个基本的配置是:
1.配置CAN时钟:
/* CAN Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);
2.CAN管脚配置,这里使用的PB11,PB12管脚:
/* Configure CAN pin: RX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure CAN pin: TX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
3.中断配置,这里打开CAN的接收和发送中断:
/* CAN RX interrupt */
NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN_RX0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* CAN TX interrupt */
NVIC_InitStructure.NVIC_IRQChannel=USB_HP_CAN_TX_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
这里需要注意的是CAN的发送中断,发送中断是发送邮箱中数据发送完毕后产生的发送完毕邮箱空中断--这和USART发送中断不同。详细点说CAN发送中断是指CAN的三个发送邮箱中的其中一个由满到发送数据完毕后空才产生的中断,当原来发送邮箱本来就是空的则不会产生发送中断。
4.CAN初始化配置:
void CAN_Ini(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/* CAN register init */
CAN_DeInit();
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM=DISABLE; //禁止时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件对CAN_MCR寄存器的INRQ位进行置1随后清0后,一旦硬件检测
//到128次11位连续的隐性位,就退出离线状态。
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过清除CAN_MCR寄存器的SLEEP位,由软件唤醒
CAN_InitStructure.CAN_NART=DISABLE; //CAN报文只被发送1次,不管发送的结果如何(成功、出错或仲裁丢失)
CAN_InitStructure.CAN_RFLM=DISABLE; //在接收溢出时FIFO未被锁定,当接收FIFO的报文未被读出,下一个收到的报文会覆盖原有的报文
CAN_InitStructure.CAN_TXFP=ENABLE; //用来使能或者失能发送FIFO优先级,由发送的请求顺序决定
CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;//CAN硬件工作在正常模式
switch(CAN_BPP)
{
//总体配置保持,tBS1>=tBS2,tBS2>=1个CAN时钟周期,tBS2>=2tSJW
case 100000:
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; //重新同步跳跃宽度
CAN_InitStructure.CAN_BS1=CAN_BS1_10tq; //设定时间段1的时间单位数目
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq; //设定时间段2的时间单位数目
CAN_InitStructure.CAN_Prescaler=20; //一个时间单位的长度//(pclk1/((4+8+8)*9)) = 36Mhz/18/20 = 100Kbits
break;
case 50000:
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1=CAN_BS1_10tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=40; //(pclk1/((4+8+8)*9)) = 36Mhz/18/40 = 50Kbits
break;
case 20000:
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1=CAN_BS1_10tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=100; //(pclk1/((4+8+8)*9)) = 36Mhz/18/100 = 20Kbits
break;
case 250000:
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=9; //(pclk1/((1+8+7)*9)) = 36Mhz/16/9 = 250Kbits
break;
case 500000:
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; //重新同步跳跃宽度1个时间单位
CAN_InitStructure.CAN_BS1=CAN_BS1_6tq; //时间段1为6个时间单位
CAN_InitStructure.CAN_BS2=CAN_BS2_5tq; //时间段2为5个时间单位
CAN_InitStructure.CAN_Prescaler=6; //(pclk1/((1+6+5)*6)) = 36Mhz/12/6 = 500Kbits设定了一个时间单位的长度9
break;
default:
break;
}
CAN_Init(&CAN_InitStructure);
/* CAN filter init */
CAN_FilterInitStructure.CAN_FilterNumber=0; //指定了待初始化的过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //指定了过滤器将被初始化到的模式为标识符屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //给出了过滤器位宽1个32位过滤器
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //用来设定过滤器标识符(32位位宽时为其高段位,16位位宽时为第一个)
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000; //用来设定过滤器标识符(32位位宽时为其低段位,16位位宽时为第二个
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000; //用来设定过滤器屏蔽标识符或者过滤器标识符(32位位宽时为其高段位,16位位宽时为第一个
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000; //用来设定过滤器屏蔽标识符或者过滤器标识符(32位位宽时为其低段位,16位位宽时为第二个
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_IT_FMP0; //设定了指向过滤器的FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
/* CAN FIFO0 message pending interrupt enable */
CAN_ClearITPendingBit(CAN_IT_FF0);
CAN_ClearITPendingBit(CAN_IT_FOV0);
CAN_ClearITPendingBit(CAN_IT_FF1);
CAN_ClearITPendingBit(CAN_IT_FOV1);
CAN_ClearITPendingBit(CAN_IT_RQCP0);
CAN_ClearITPendingBit(CAN_IT_RQCP1);
CAN_ClearITPendingBit(CAN_IT_RQCP2);
CAN_ITConfig(CAN_IT_FMP0, ENABLE); //使能接收中断
//CAN_ITConfig(CAN_IT_TME, ENABLE); //TransmitMailbox empty,发送中断在CAN_Transmit()后才能打开,用来判断发送完毕发送下一个数据
}
5.实现CAN的发送中断和接收中断,这里我们只用了一个发送邮箱进行发送--简单方便:
void USB_LP_CAN_RX0_IRQHandler(void) //接收中断
{
CanRxMsg RxMessage;
U8 i=0;
U8 targetid=0;
//如果是标准帧
CAN_Receive(CAN_FIFO0, &RxMessage);
if(RxMessage.IDE==CAN_ID_STD)//只收标准帧数据
{
if((RxMessage.StdId&0x1F)==PCL_ID+bmqnum) //接收到ID是取后5位是自己的ID
{
//每个完整的数据,以|开头,下面是ID
while(!QIn(&Qlinecanrx,'|')){}
targetid=(RxMessage.StdId>>6)&0x1F;
while(!QIn(&Qlinecanrx,targetid)){}
for(i=0;i
while(!QIn(&Qlinecanrx,RxMessage.Data[i])){}
}
}
}
CAN_ITConfig(CAN_IT_FMP0, ENABLE); //中断退出
}
void USB_HP_CAN_TX_IRQHandler(void)
{
CAN_ClearITPendingBit(CAN_IT_RQCP0);
CAN_ClearITPendingBit(CAN_IT_RQCP1);
CAN_ClearITPendingBit(CAN_IT_RQCP2);
CAN_ITConfig(CAN_IT_TME, DISABLE);
CANSendStr(); //时刻查询是否可以给上位发送数据
}
void CANSendStr()
{
CanTxMsg TxMessage;
TxMessage.StdId=PCL_ID+bmqnum; //用来设定标准标识符,0x08+2=0x0A
TxMessage.ExtId=0; //用来设定扩展标识符
TxMessage.RTR=CAN_RTR_DATA; //用来设定待传输消息的帧类型,它可以设置为数据帧或者远程帧,使用数据帧
TxMessage.IDE=CAN_ID_STD; //用来设定消息标识符的类型,使用标准标识符
TxMessage.DLC = 8; //用来设定待传输消息的帧长度,它的取值范围是0到0x8
//20160822----zhang
TxMessage.RTR=CAN_RTR_DATA; //数据帧
//20160822----zhang
TxMessage.Data[0]='~';
TxMessage.Data[1]='0';
TxMessage.Data[2]='1';
TxMessage.Data[3]='2';
TxMessage.Data[4]='3';
TxMessage.Data[5]='4';
TxMessage.Data[6]='5';
TxMessage.Data[7]='6';
CAN_Transmit(&TxMessage);
CAN_ITConfig(CAN_IT_TME, ENABLE); //TransmitMailbox empty,发送中断在CAN_Transmit()后才能打开,用来判断发送完毕发送下一个数据
}
6.使用中先使用CANSendStr()发送一条数据,则发送完毕后进入发送空中断,则之后会循环发送数据
参考文章:
http://www.openedv.com/thread-38867-1-1.html
http://www.stmcu.org/module/forum/thread-565605-1-1.html
http://www.cnblogs.com/chris-cp/p/3961508.html
http://www.openedv.com/thread-51889-1-1.html
上一篇:STM32的CAN总线学习总结
下一篇:STM32之CAN通信
推荐阅读
史海拾趣
在ELMEC看来,产品质量是企业的生命线。为了确保产品的可靠性和稳定性,公司建立了一套完善的质量管理体系。从原材料采购到产品生产、从质量控制到售后服务,每一个环节都经过严格把控。同时,ELMEC还积极引进国际先进的质量管理方法和标准,不断提高产品的整体质量水平。这些努力使得ELMEC的产品在市场上享有很高的声誉。
普芯达电子自创立之初,就立志成为国产高品质、高性价比的IC产品供应商。在成立初期,公司面临着资金紧张、技术积累不足以及市场竞争激烈等多重挑战。然而,凭借着创始人对集成电路行业的深厚理解和坚定信念,普芯达电子逐步克服了这些困难,通过引进优秀人才、加大研发投入以及不断优化产品质量,逐渐在市场上崭露头角。
作为一家有着高度社会责任感的企业,EOS始终关注环保和可持续发展问题。他们采用环保材料和绿色生产工艺,降低产品对环境的污染。同时,EOS还积极参与社会公益事业,捐资助学、扶贫济困等活动不断。这些举措不仅提升了企业的社会形象,也为公司的可持续发展奠定了坚实基础。
EOS公司成立于1983年,创始人是一群热衷于红外技术的专家。他们深知红外探测器在军事、科研和工业领域的重要性,于是决定投身于这一领域。初创时期,EOS面临着资金短缺、技术难度大等挑战,但团队成员凭借着对技术的执着和对市场的敏锐洞察,逐步攻克难关,成功研制出第一批红外探测器产品。
背景:面对电子行业的快速变化和多元化需求,HCH Co公司意识到仅凭一己之力难以应对所有挑战。因此,公司积极寻求与其他行业的跨界合作机会,以共同推动电子行业的创新发展。
发展:通过与汽车、医疗、教育等多个行业的领军企业建立合作关系,HCH Co公司成功将自身的电子技术和产品应用于更广泛的领域。例如,与汽车制造商合作开发智能驾驶系统、与医疗机构合作研发远程医疗设备等。跨界合作不仅为公司带来了新的增长点,也促进了不同行业之间的技术交流和资源共享。未来,HCH Co公司将继续秉承开放合作的理念,与更多行业伙伴携手共创美好未来。
请注意,以上五个故事是基于假设构建的,旨在反映电子行业中企业可能的发展路径和趋势。实际情况中,“HCH Co”公司的具体发展故事可能有所不同。
技术创新是德崧电子持续发展的核心动力。公司不断加大研发投入,引进先进的生产设备和技术人才,致力于开发具有竞争力的新产品。通过不断的技术创新,德崧电子在电子开关行业中取得了多项重要成果。例如,公司成功研发出了具有自动感应功能的智能开关,实现了对灯光、温度等环境因素的自动调节。这一技术的推出不仅提高了产品的智能化水平,也为公司赢得了更多的市场份额。
本人对电化学实验中的缓慢变化的直流进行测量,该直流变化范围在nA(甚至更小)到mA级别,采用电流跟随器转换成电压测量(采用OPA132或是CA3140或是OPA121或是OPA129),现在问题在于本人使用电阻R2、R4来模拟前端系统产生待测电流 i 进入电流跟随 ...… 查看全部问答∨ |
|
我们知道,PCI设备有三个空间——内存地址空间、IO地址空间和配置空间。由于PCI支持即插即用,所以PCI设备不是占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。怎么配置呢?这就是配置空间的作用。 DW | Byte3 | Byte2 ...… 查看全部问答∨ |
待测信号由P3.2接入,待测信号脉宽取5ms~50ms,脉宽值显示在数码管(共阳数码管)上,显示单位为ms.求...<img border="0" src="file:///C:/Users/Acer/Desktop/QQ截图20120501230546.png"> [ 本帖最后由 寒雪剑91 于 2012-5-1 23:22 编辑 ...… 查看全部问答∨ |
问题是将消息数据填入队列,经查询消息得出的结果也对,但是等待消息函数得到的结果却总是最后一条消息数据,很纳闷,不知怎么解? 请大侠们赐教 我的目标是假如我将消息数据取出来之后,可以通过对不同的数据做判断而进行不同的函数调用。例如: ...… 查看全部问答∨ |