CAN是 Controller Area Network 的简称, 最初由BOSCH公司开发, 后来成为国际标准(ISO 11898), 是当前应用最广泛的现场总线之一, 是汽车控制系统和嵌入式工业控制局域网事实上的标准.
相对于近距离传输的I2C, SPI协议, 以及RS485总线, CAN 总线定义了更先进的物理层和链路层, 以及种类丰富的上层协议. 与I2C, SPI等基于时钟信号同步的通讯方式不同, CAN通讯不使用时钟信号进行同步, 它是一种异步通讯, 只有 CAN_High 和 CAN_Low 两条信号线, 共同构成一组差分信号线, 以差分信号的形式进行通讯.
CAN 物理层主要分为闭环总线及开环总线网络两种形式, 一个适合于高速通讯, 一个适合于远距离通讯.
CAN 在多个收发器之间的连接, 可以不共地, 只需要 CANH 和 CANL 两线连接.
CAN总线上可以挂载多个通讯节点, 节点之间的信号经过总线传输, 实现节点间通讯. CAN通讯协议不对节点进行地址编码, 而是对数据内容进行编码, 所以网络中的节点个数理论上不受限制, 只要总线的负载足够即可, 可以通过中继器增强负载.
CAN通讯节点由一个CAN控制器及CAN收发器组成, 控制器与收发器之间通过 CAN_Tx及 CAN_Rx 信号线相连, 收发器与CAN总线之间使用 CAN_High 及 CAN_Low 信号线相连. 其中 CAN_Tx 及 CAN_Rx 使用普通的类似TTL逻辑信号, 而 CAN_High 及 CAN_Low 是一对差分信号线, 当CAN节点需要发送数据时, 控制器把要发送的二进制编码通过 CAN_Tx 线发送到收发器, 然后由收发器把这个普通的逻辑电平信号转化成差分信号, 通过差分线 CAN_High 和 CAN_Low 输出到CAN总线网络. 收发器接收总线上的数据时则是相反的过程, 收发器把总线上收到的 CAN_High 及 CAN_Low 信号转化成普通的逻辑电平信号, 再通过 CAN_Rx 输出到控制器中.
差分信号又称差模信号, 与传统使用单根信号线电压表示逻辑的方式有区别, 使用差分信号传输时, 需要两根信号线, 这两个信号线的振幅相等, 相位相反, 通过两根信号线的电压差值来表示逻辑0和逻辑1. 相对于单信号线传输的方式, 使用差分信号传输具有如下优点
由于差分信号的这些优点, 在USB协议, 485协议, 以太网协议及CAN协议的物理层中, 都使用了差分信号传输.
CAN协议中对它使用的 CAN_High 及 CAN_Low 表示的差分信号做了规定. 以高速CAN协议为例, 当表示逻辑1时(隐性电平), CAN_High 和 CAN_Low 线上的电压均为2.5V, 即它们的电压差为 0, 而表示逻辑0时(显性电平), CAN_High的电平为 3.5V, CAN_Low线的电平为1.5V, 电压差为 2V.
CAN 总线网络是一种多主网络, 在总线处于空闲状态时, 任何一个节点单元都可以申请成为主机, 向总线发送消息. 其原则是: 最先访问总线的节点单元可以获得总线的控制权, 多个节点单元同时尝试获取总线的控制权时, 将发生仲裁事件, 具有高优先级的节点单元将获得总线控制权.
CAN 协议中, 所有的消息都以固定的数据格式打包发送. 两个以上的节点单元同时发送信息时, 根据节点标识符(常称为 ID, 打包在固定的数据格式中)决定各自优先级关系, CAN 总线没有其他总线的地址概念, 在总线上增加节点单元时, 连接在总线的其他节点单元的软硬件都不需要改变.
CAN 总线的通信速率和总线长度有关, 在总线长度小于 40m 的场合中, 数据传输速率可以达到 1Mbps, 而即便总线长度上升至 1000m, 数据的传输速率仍可达到 50Kbps, 无论在速率还是传输距离都明显优于常见的 RS232, RS485 和 I2C 总线.
对于总线错误, CAN 总线有错误检测功能, 错误通知功能, 错误恢复功能三种应对措施, CAN 总线上的每个节点都可以通过判断得出, 当前总线上的错误是暂时错误(如瞬间的强干扰)还是持续错误(如总线断裂). 当总线上发生持续错误时, 引起故障的节点单元会自动脱离总线.
CAN 总线上的节点数量在理论上没有上限, 但在实际上收到总线上的时间延时及电气负载的限制. 降低最大通信速率可以增加节点单元的连接数, 反之减少节点单元的连接数则最大通信速率可以提高.
为了实现位同步, CAN协议把每一个数据位(bit)的时序分解成SS段, PTS段, PBS1段, PBS2段, 这四段的长度加起来即为一个CAN数据位的长度.
CAN传输的最小的时间单位是Tq(即CAN外设的时钟周期), 一个完整的位由8~25个Tq组成.
CAN 总线的数据通信是以数据帧的格式进行的, 了解CAN的数据帧, 可以帮助了解CAN的过滤机制. 下面是一个完整的CAN数据帧的结构

用C语言描述的CAN帧整体结构为
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8];
uint32_t TxMailbox;
帧头的结构和帧数据
TxHeader.StdId = 0x446;
TxHeader.RTR = CAN_RTR_DATA; // Remote or data frame
TxHeader.IDE = CAN_ID_STD; // Standard or extended
// reserved bit
TxHeader.DLC = 2; // Data length in bytes
TxData[0] = 50;
TxData[1] = 0xAA;
以下的描述适用于AIR32F103和STM32.
bxCAN 控制器 (Basic Extended CAN) 支持CAN协议2.0A和2.0B标准. 该CAN控制器支持最高的通讯速率为1Mbps, 可以自动地接收和发送CAN报文, 支持使用标准ID和扩展ID的报文. 外设中具有3个发送邮箱, 发送报文的优先级可以使用软件控制, 还可以记录发送的时间;具有2个3级深度的接收FIFO, 可使用过滤功能只接收或不接收某些ID号的报文; 可配置成自动重发; 不支持使用DMA进行数据收发.
通过配置位时序寄存器CAN_BTR的TS1[3:0]及TS2[2:0]寄存器位设定BS1及BS2段的长度后, 可以确定每个CAN数据位的时间
BS1段时间
Tbs1 =Tq x (TS1[3:0] + 1)
BS2段时间
Tbs2 = Tq x (TS2[2:0] + 1)
整个数据位的时间
Tbit = 1Tq + Tbs1 +Tbs2 = 1 + (TS1[3:0] + 1)+ (TS2[2:0] + 1)
Tq 是 CAN 通信的最小时间单元, 与 CAN 时钟总线及分频器配置有关, CAN1和CAN2外设都是挂载在APB1总线上的, 而位时序寄存器 CAN_BTR 中的 BRP[9:0] 寄存器位可以设置CAN外设时钟的分频值 , 所以
Tq = brp * Tpclk = (BRP[9:0]+1) * Tpclk
其中的PCLK指APB1时钟, 默认值为36MHz. 可以计算出 CAN 的波特率:
BaudRate = 1 / Tbit = Fpclk / ((Tbs1 + Tbs2 + 1) * brp)
CAN_InitStructure.CAN_TTCM = DISABLE; // time triggered communication mode off
CAN_InitStructure.CAN_ABOM = DISABLE; // automatic bus-off management off
CAN_InitStructure.CAN_AWUM = DISABLE; // automatic wake-up mode off, wakeup by software cleaar CAN->MCR SLEEP bit
CAN_InitStructure.CAN_NART = ENABLE; // no-automatic retransmission mode on
CAN_InitStructure.CAN_RFLM = DISABLE; // rx FIFO Locked mode off
CAN_InitStructure.CAN_TXFP = DISABLE; // transmit FIFO priority off
CAN_InitStructure.CAN_Mode = mode;
// Set baud rate
CAN_InitStructure.CAN_SJW = tsjw; // synchronisation_jump_width, CAN_SJW_1tq ~ CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = tbs1; // number of time quanta in Bit Segment 1, CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_BS2 = tbs2; // number of time quanta in Bit Segment 2, CAN_BS1_1tq ~ CAN_BS1_16tq
CAN_InitStructure.CAN_Prescaler = brp; // clock prescaler, 1~1024
CAN_Init(CAN1, &CAN_InitStructure);
CAN 是一种典型的广播式网络, 在实际应用中, 如果只希望接收到特定类型的数据, 就要借助过滤器来实现. AIR32/STM32的CAN控制器包含14个过滤器, 可以设置为 屏蔽模式 或 列表模式 对CAN总线上的报文进行过滤. 当节点希望接收到一种报文时, 可以用屏蔽位模式进行过滤, 当节点希望接受到单一类型报文时, 应该配置为列表模式.
CAN控制器的每个过滤器都具备一个寄存器, 称为屏蔽寄存器。其中标识符寄存器的每一位都有屏蔽寄存器的每一位所对应.
AIR32/STM32 使用 CAN 外设内建的过滤器, 初始化代码为
CAN_FilterTypeDef canfilterconfig;
canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
canfilterconfig.FilterBank = 18; // 指定使用哪个过滤器
canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
canfilterconfig.FilterIdHigh = 0x103<<5;
canfilterconfig.FilterIdLow = 0;
canfilterconfig.FilterMaskIdHigh = 0x103<<5;
canfilterconfig.FilterMaskIdLow = 0x0000;
canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
canfilterconfig.SlaveStartFilterBank = 20; // how many filters to assign to the CAN1 (master can)
HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);
FilterMode 用于设置过滤模式, 在STM32中有两种过滤模式, 这里使用的是掩码模式
FilterScale 用于指定是 1)一个32bit的过滤寄存器, 还是 2)两个16bit的过滤寄存器. 这里使用的是一个 32 Bit 寄存器.
FilterIdHigh 用于设置 ID 寄存器的高16 Bits, 这里的值会被用于与输入的ID进行比较.
FilterMaskIdHigh 是掩码寄存器的高16 Bits, 在对接收到的消息的ID进行比较时, 会忽略这个寄存器中bit=0的位, 仅对会对bit=1对应的位, 与ID寄存器中对应的位进行比较.

上图中, CAN_FxR1 和 CAN_FxR2 都是32bit寄存器, 用于存储过滤器的 ID 和 Mask 设置, 红色框和绿色框分别对应代码中的 FilterIdHigh + FilterIdLow 和 FilterMaskIdHigh + FilterMaskIdLow.
根据上面的设置
根据手册 standard frames with 11-bit identifiers as well as extended frames with 29-bit identifiers, 扩展帧除了原有的 11 bits 标准ID外, 还带 18 bits 的扩展ID. 为什么是29 bits? 因为后面还有3个bit的功能标志位
如果
过滤条件的设置代码为
// 取值 STID[10:0] & EXTID[17:13], 因为 CAN_FilterIdHigh 是 16bit, 所以 filterId << 5 的高16bit会被忽略.
filter.CAN_FilterIdHigh = ((filterId << 5) | (filterId >> (32 - 5))) & 0xFFFF;
// 取值 EXID[12:5] & 3 Reserved bits, 这里同样, filterId 移位后的高16bit会被忽略
filter.CAN_FilterIdLow = (filterId >> (11 - 3)) & 0xFFF8;
// 与上面同理
filter.CAN_FilterMaskIdHigh = ((filterMask << 5) | (filterMask >> (32 - 5))) & 0xFFFF;
filter.CAN_FilterMaskIdLow = (filterMask >> (11 - 3)) & 0xFFF8;
因为 TJA1050 和 MCP2551 都是5V供电, 因此开发板上要有5V输出, 否则需要单独供电
代码仓库目录 https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/CAN
这个目录下包含两个模式的例子, 一个是 Loopback, 一个是 Normal, 从合宙官方仓库的例子参考(抄)的.
Loopback 是测试模式, 发送的数据直接进入接收队列, 这个模式不需要CAN模块也能运行. 运行后在串口输入's', 会发送8个字节并将接收到的数据通过串口回显.
正常的通信模式, 需要两套 MCU + CAN 收发器. CAN 收发器之间通过 CANH 和 CANL 连接. 代码中设置过滤器时, 使用的是相同的 ID 和 Mask 值, 对两个MCU编译烧录时需要将 ID_TARGET 和 ID_RECEIV 的值互换一下. 如果希望接收所有(不过滤), 可以将 Mask ID 设为 0.
运行后, 在一侧串口输入's', 在另一侧会通过串口显示接收到的数据.
在测试中, 一开始给 TJA1050 错误使用了 3.3V 电压, 在 Loopback 模式工作正常, 但是在 Normal 模式工作不正常, 只有将两边收发器共地才能正常通信, 如果换成 MCP2551 则完全不能通信. 这些问题在将电压换成 5V 后就正常了.
TJA1050 是 SOP8 封装, 但是与常见的 SOP8 封装不同的是没有圆点, 在手册第14页有特意说明, Pin 1 对应的是有斜面的那个边.
这个型号可能有不少厂家生产, 市面上有带圆点的也有不带圆点的, 不同来源的 TJA1050, Pin 间电阻差异很大. 有些 VCC 和 GND 之间电阻只有9K, 有些是1.2M, 而 RX TX 之间的电阻有些上M, 有些只有14K, 但是在同样的电路上, 都能正常工作.
CANH 和 CANL 之间串接的 120R 电阻, 换成 200R 也没问题.
SL 不能浮空, 需要接地才能正常通信.
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
CSV.open(name,"r").eachdo|row|putsrowend我得到以下错误:CSV::MalformedCSVErrorUnquotedfieldsdonotallow\ror\n文件名是一个.txt制表符分隔文件。我是专门做的。我有一个.csv文件,我转到excel,并将文件保存为.txt制表符分隔的文件。所以它是制表符分隔的。CSV.open不应该能够读取制表符分隔的文件吗? 最佳答案 尝试像这样指定字段分隔符:CSV.open("name","r",{:col_sep=>"\t"}).eachdo|row|
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
因此,当我遵循MichaelHartl的RubyonRails教程时,我注意到在用户表中,我们为:email属性添加了一个唯一索引,以提高find的效率方法,因此它不会逐行搜索。到目前为止,我们一直在根据情况使用find_by_email和find_by_id进行搜索。然而,我们从未为:id属性设置索引。:id是否自动索引,因为它在默认情况下是唯一的并且本质上是顺序的?或者情况并非如此,我应该为:id搜索添加索引吗? 最佳答案 大多数数据库(包括sqlite,这是RoR中的默认数据库)会自动索引主键,对于RailsMigration
我编写了一个非常简单的“部署”脚本,作为我的裸git存储库中的post-updateHook运行。变量如下livedomain=~/mydomain.comstagingdomain=~/stage.mydomain.comgitrepolocation=~/git.mydomain.com/thisrepo.git(bare)core=~/git.mydomain.com/thisrepo.gitcore==addedremoteintoeachlive&stagegitslive和stage都初始化了gitrepos(非裸),我已经将我的裸仓库作为远程添加到它们中的每一个(名为co
在Ruby(或Rails)中,我们可以做到new_params=params.merge({:order=>'asc'})现在new_params是一个带有添加键:order的散列。但是是否有一行可以返回带有已删除key的散列?线路new_params=params.delete(:order)不会工作,因为delete方法返回值,仅此而已。我们必须分3步完成吗?tmp_params=paramstmp_params.delete(:order)returntmp_params有没有更好的方法?因为我想做一个new_params=(params[:order].blank?||para
capybara找不到在我的cucumber测试中用它的id标记。当我save_and_open_page时,我能够看到该元素.但我无法通过has_css?找到它或find:pry(#)>page.html.scan(/notice_sent/).count=>1pry(#)>page.html.scan(/id=\"notice_sent\"/).count=>1pry(#)>page.find('#notice_sent')Capybara::ElementNotFound:Unabletofindcss"#notice_sent"from/Users/me/.gem/ruby/2
目前我正在使用这个正则表达式从YoutubeURL中提取视频ID:url.match(/v=([^&]*)/)[1]我怎样才能改变它,以便它也可以从这个没有v参数的YoutubeURL获取视频ID:http://www.youtube.com/user/SHAYTARDS#p/u/9/Xc81AajGUMU感谢阅读。编辑:我正在使用ruby1.8.7 最佳答案 对于Ruby1.8.7,这就可以了。url_1='http://www.youtube.com/watch?v=8WVTOUh53QY&feature=feedf'url
在开发模式下:nil.id=>"Calledidfornil,whichwouldmistakenlybe4--ifyoureallywantedtheidofnil,useobject_id"在生产模式中:nil.id=>4为什么? 最佳答案 在您的环境配置中查找包含以下内容的行:#Logerrormessageswhenyouaccidentallycallmethodsonnil.config.whiny_nils=true#orfalseinproduction.rb这是为了防止您在开发模式下调用nil上的方法。我猜他们在生