基于STM32的CAN总线的数据采集卡设计

题目基于STM32的CAN总线数据采集卡设计

学院自动化学院

专业自动化专业

班级自动化1305班

姓名司文雷

指导教师罗璠

2016 年8 月27 日

任务书

1、要求设计如下功能的数据采集卡:

(1)采用STM32对8路0~5V模拟信号进行采集,采样频率为100Hz。

(2)采集的数据要求通过CAN总线发送出去。

(3)数据采集卡的CAN总线标识符可用拨码开关指定。

2、要求选择合适的STM32微控制器,具备对CAN总线的支持,并选择合适的CAN接口电路芯片;

3、要求完成采集卡的硬件电路设计,包括原理图设计、PCB布线设计;

4、要求完成STM32程序设计,并进行仿真;

5、提交设计报告。

1 STM32简介 (3)

1.1 STM32F105VCT6的参数 .................................................................. 错误!未定义书签。

1.2 内部资源 (4)

1.3 Cortex-M3内核简介 (5)

2 CAN概述 (6)

2.1CAN简介 (6)

2.2工作原理 (7)

2.3CAN的高层协议 (7)

2.4 CAN波特率计算 (8)

2.4.1 波特率 (8)

2.4.2CAN波特率的计算 (8)

3硬件电路设计 (13)

4软件实现 (14)

4.1STM32电压数据采集的软件实现 (14)

4.1.1AD-DA模块的编写 (14)

4.2CAN 总线节点的软件设计 (24)

结论 (32)

参考文献 (33)

附录1 (35)

附录2 (36)

摘要

摘要:CAN(Controller Area Network,控制器局域网)是一种有效支持实时控制的串行数据通信网络。自上世纪80年代诞生以来,CAN总线以其可靠性好、实时性高及组网简便灵活等优势而受到人们的青睐,并在众多行业领域内得到了广泛的应用。随着工业技术的不断进步,控制过程的自动化与网络化是必然的发展趋势,同时数据的传输量越来越大,对数据传输的实时性要求也更加苛刻。笔者采用内置 CAN 控制器的高性能微处理器STM32F105vct6 作为节点的微处理器,使用带隔离的高速集成 CTM1050T 作为 CAN 收发器,数据输入输出通道采用光电耦合器进行隔离,设计了 CAN 总线系统的数据采集节点。本设计节点具有体积小、功耗低、抗干扰能力强、实时性高及数据处理能力强等优势。实现,它主要实现现场节点的监控及数据的显示、分析和存储。数据转换器采用 RS-232 转CAN,它主要实现串口协议与 CAN 协议之间的桥接,并将接收的数据进行转发。各节点与工业现场的仪器仪表相连接,不分主次地挂接在 CAN 总线上,它主要实现数据的采集与处理,并接收来自 CAN 总线上的数据或将数据发送到 CAN 总线上。

关键词:STM32;CAN总线;数据采集

1STM32简介

STM32F105xx增强型系列使用高性能的ARM/Cortex-M3/32位的RISC内核,工作频率为72MHz,内置高速存储器(高达128K字节的闪存和20K字节的SRAM),丰富的增强I/O端口和联接到两条APB总线的外设。所有型号的器件都包含2个12位的ADC 、3个通用16位定时器和一个PWM定时器,还包含标准和先进的通信接口:多达2个I2C和SPI、3个USART、一个USB和一个CAN。

STM32F105xx增强型系列工作于-40℃至+105 ℃的温度范围,供电电压2.0V 至3.6V,一系列的省电模式保证低功耗应用的要求。

这些丰富的外设配置,使得STM32F105xx增强型微控制器适合于多种应用场合:

电机驱动和应用控制;

·医疗和手持设备;

·PC外设和GPS平台;

·工业应用:可编程控制器、变频器、打印机和扫描仪;STM32开发板核心芯片的参数如表1-1

表1-1 器件功能和配置(STM32F105xx 增强型)

芯片引脚图如图1-2:

图1-2 STM32F105xx增强型LQPFP48管脚图

1.2 内部资源

STM32有丰富的内部资源,如下所示:

·RealView MDK(Miertocontroller Development Kit)基于ARM微控制器的专业嵌入式开发工具;

·内置闪存存储器;

·内置SRAM;

·嵌套的向量式中断控制器(NVIC);

·外部中断/事件控制器(EXTI);

·时钟和启动;

·自举模式;

·DMA;

·RTC(实时时钟)和后备寄存器;

·窗口看门狗;

·I2C总线;

·通用同步/异步接受发送器(USART);

·串行外设接口(SPI);

·控制器区域网络(CAN);

·通用串行总线(USB);

·通用输入输出接口(GPIO);

·ADC(模拟/数字转换器);

·温度传感器;

·串行线JTAG调试口(SWJ-DP)。

1.3 Cortex-M3内核简介

Cortex-M3内核包含一个适用于传统Thumb和新型Thumb-2指令的译码器、一个支持硬件乘法和硬件除法的先进ALU、控制逻辑和用于连接处理器其他部件的接口。Cortex-M3处理器是首款基于ARMv7-M架构的ARM处理器。中央Cortex-M3内核使用3级流水线哈佛架构,运用分支预测、单周期乘法和硬件除法功能实现了出色的效率(1.25DMIPS/MHz)。Cortex-M3处理器是一个32位处理器,带有32位宽的数据路径、寄存器库和基于传统ARM7处理器的系统只支持访问对齐的数据,沿着对齐的字边界即可对数据进行访问和存储。Cortex-M3处理器采用非对齐数据访问方式,使非对齐数据可以在单核访问中进行传输。

Cortex-M3处理器是专为那些对成本和功耗非常敏感但同时对性能要求又相当高的应用而设计的。凭借缩小的内核尺寸和出色的中断延迟性能、集成的系统部件、灵活的配置、简单的高级编程和强大的软件系统,Cortex-M3处理器将成为从复杂的芯片系统到低端微控制器等各种系统的理想解决方案。表2-3为Cortex-M3处理器与ARM7作比较。

表2-3 Cortex-M3与ARM7相比较

2 CAN概述

2.1CAN简介

CAN,全称为“Controller Area Network”,即控制器局域网,是国际上应用最广泛的现场总线之一。最初,CAN被设计作为汽车环境中的微控制器通讯,在车载各电子控制装置ECU之间交换信息,形成汽车电子控制网络。比如:发动机管理系统、变速箱控制器、仪表装备、电子主干系统中,均嵌入CAN控制装置。

一个由CAN 总线构成的单一网络中,理论上可以挂接无数个节点。实际应用中,节点数目受网络硬件的电气特性所限制。例如,当使用Philips P82C250

作为CAN收发器时,同一网络中允许挂接110个节点。CAN 可提供高达1Mbit/s 的数据传输速率,这使实时控制变得非常容易。另外,硬件的错误检定特性也增强了CAN的抗电磁干扰能力。

2.2工作原理

当CAN总线上的一个节点(站)发送数据时,它以报文形式广播给网络中所有节点。对每个节点来说,无论数据是否是发给自己的,都对其进行接收。每组报文开头的11位字符为标识符,定义了报文的优先级,这种报文格式称为面向内容的编址方案。在同一系统中标识符是唯一的,不可能有两个站发送具有相同标识符的报文。当几个站同时竞争总线读取时,这种配置十分重要。当一个站要向其它站发送数据时,该站的CPU将要发送的数据和自己的标识符传送给本站的CAN芯片,并处于准备状态;当它收到总线分配时, 转为发送报文状态。CAN芯片将数据根据协议组织成一定的报文格式发出,这时网上的其它站处于接收状态。每个处于接收状态的站对接收到的报文进行检测,判断这些报文是否是发给自己的,以确定是否接收它。

由于CAN总线是一种面向内容的编址方案,因此很容易建立高水准的控制系统并灵活地进行配置。我们可以很容易地在CAN总线中加进一些新站而无需在硬件或软件上进行修改。当所提供的新站是纯数据接收设备时,数据传输协议不要求独立的部分有物理目的地址。它允许分布过程同步化,即总线上控制器需要测量数据时,可由网上获得,而无须每个控制器都有自己独立的传感器。

2.3CAN的高层协议

CAN的高层协议(也可理解为应用层协议)是一种在现有的底层协议(物理层和数据链路层)之上实现的协议。高层协议是在CAN规范的基础上发展起来的应用层。许多系统(像汽车工业)中,可以特别制定一个合适的应用层,但对于许多的行业来说,这种方法是不经济的。一些组织已经研究并开放了应用层标准,以使系统的综合应用变得十分容易。

一些可使用的CAN高层协议有:

制定组织主要高层协议

CiA CAL协议

CiA CANOpen协议

ODVA DeviceNet 协议

Honeywell SDS 协议

Kvaser CANKingdom协议

2.4 CAN波特率计算

2.4.1 波特率

波特率(Baud rate)即调制速率,指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数。它是对符号传输速率的一种度量,1波特即指每秒传输1个符号。单位“波特”本身就已经是代表每秒的调制数。

2.4.2CAN波特率的计算

CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;

CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;

注意//#define CAN_BS1_3tq ((uint8_t)0x02) /*!< 3 time quantum */ CAN_InitStructure.CAN_BS2 = CAN_BS2_5tq;

CAN_InitStructure.CAN_Prescaler = 4;//2

nominal bit time(3+5+1)tq=9tq

关于分频系数查看 system_stm32f10x.c下面的

static void SetSysClockTo72(void) 函数

/* HCLK = SYSCLK */

/* PCLK2 = HCLK */

/* PCLK1 = HCLK/2 */

所以can时钟 72MHZ/2/4=9 Mhz

tq=1/36Mhz

波特率为 1/nominal bit time= 9/9=1MHZ

void CAN_Configuration(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_InitStructure.CAN_AWUM=DISABLE;

CAN_InitStructure.CAN_NART=DISABLE;

CAN_InitStructure.CAN_RFLM=DISABLE;

CAN_InitStructure.CAN_TXFP=DISABLE;

CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;

CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;

CAN_InitStructure.CAN_BS1=CAN_BS1_9tq;

CAN_InitStructure.CAN_BS2=CAN_BS2_8tq;

CAN_InitStructure.CAN_Prescaler=200;

CAN_Init(&CAN_InitStructure);

/* CAN filter init */

CAN_FilterInitStructure.CAN_FilterNumber=0;

CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;

CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_16bit;

CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;

CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;

CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;

CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;

CAN_FilterInitStructure.CAN_FilterFIFOAssignment=0;

CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;

CAN_FilterInit(&CAN_FilterInitStructure);

}

注意//#define CAN_BS1_3tq((uint8_t)0x02) /*!< 3 time quantum */

拨特率10K,公式:72MHZ/2/200/(1+9+8)=0.01,即10K,和SJA1000测试通过,要加上120欧姆电阻。

CAN->BTR = (u32)((u32)CAN_InitStruct->CAN_Mode << 30) | ((u32)CAN_InitStruct->CAN_SJW << 24) |

((u32)CAN_InitStruct->CAN_BS1 << 16) | ((u32)CAN_InitStruct->CAN_BS2 << 20) |

((u32)CAN_InitStruct->CAN_Prescaler - 1);

Fpclk=36M 时can波特率250k的配置为

/* CAN cell init */

CAN_InitStructure.CAN_TTCM=DISABLE;

CAN_InitStructure.CAN_ABOM=DISABLE;

CAN_InitStructure.CAN_AWUM=DISABLE;

CAN_InitStructure.CAN_NART=DISABLE;

CAN_InitStructure.CAN_RFLM=DISABLE;

CAN_InitStructure.CAN_TXFP=DISABLE;

CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;

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;

CAN_Init(&CAN_InitStructure);

100k 查询和500k 中断方式的例子分别设置如下:

CAN_Polling:

/* CAN cell init */

CAN_InitStructure.CAN_TTCM=DISABLE;

CAN_InitStructure.CAN_ABOM=DISABLE;

CAN_InitStructure.CAN_AWUM=DISABLE;

CAN_InitStructure.CAN_NART=DISABLE;

CAN_InitStructure.CAN_RFLM=DISABLE;

CAN_InitStructure.CAN_TXFP=DISABLE;

CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;

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=5;

CAN_Init(&CAN_InitStructure); 100k

/* CAN cell init */ CAN_Interrupt

CAN_InitStructure.CAN_TTCM=DISABLE;

CAN_InitStructure.CAN_ABOM=DISABLE;

CAN_InitStructure.CAN_AWUM=DISABLE;

CAN_InitStructure.CAN_NART=DISABLE;

CAN_InitStructure.CAN_RFLM=DISABLE;

CAN_InitStructure.CAN_TXFP=DISABLE;

CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;

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=1;

CAN_Init(&CAN_InitStructure); //500k

can时钟是RCC_APB1PeriphClock,需注意CAN时钟频率

CAN波特率 = RCC_APB1PeriphClock/CAN_SJW+CAN_BS1+CAN_BS2/CAN_Prescaler; 如果CAN时钟为8M, CAN_SJW = 1,CAN_BS1 = 8,CAN_BS2 = 7,CAN_Prescaler = 2 那么波特率就是=8M/(1+8+7)/2=250K

得到500Kb/s的波特率

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=1;

每一位的Tq数目 = 1 (固定SYNC_SEG) + 8 (BS1) + 7 (BS2) = 16

如果CAN时钟是 8 MHz : (8M / 1 ) / 16 = 500K

其中:

1 为分频系数

16 为每一位的Tq数目

为了设置为100K,把分频系数改为5即可,BS1 BS2不变

每一位的Tq数目 = 1(固定)+8 (BS1)+7 (BS2) = 16

如果CAN时钟是8MHz : (8M/5)/16 = 100K

如果想得到1M 的波特率,CAN时钟仍然是 8MHz的情况下,分频系数不变应该改变 BS1 BS2

CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;

CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;

每一位的Tq数目 = 1(固定)+5 (BS1) + 2 (BS2) = 8

如果CAN时钟是 8 MHz : (8M / 1 ) / 8 = 1000K

另外尽可能的把采样点设置为 CiA 推荐的值:

75% when 波特率> 800K

80% when 波特率> 500K

87.5% when 波特率<= 500K

所以对于 100K 的波特率(假定使用 8MHz 时钟)

可以修改该BS1 BS2 为:

CAN_InitStructure.CAN_Prescaler=5;

CAN_InitStructure.CAN_BS1=CAN_BS1_13tq;

CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;

(1+13) / (1+13+2) = 87.5%

所以对于 500K 的波特率(假定使用 8MHz 时钟)

可以修改该BS1 BS2 为:

CAN_InitStructure.CAN_Prescaler=1;

CAN_InitStructure.CAN_BS1=CAN_BS1_13tq;

CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;

(1+13) / (1+13+2) = 87.5%

所以对于 1000K 的波特率(假定使用 8MHz 时钟)

可以修改该BS1 BS2 为:

CAN_InitStructure.CAN_Prescaler=1;

CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;

CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;

(1+5) / (1+5+2) = 75%

上边这个公式算出来的就是CAN的速率了

3硬件电路设计

数据采集卡整体硬件程序框图如图3.1所示,由信息调理电路,STM32芯片(内含CAN总线控制器),CAN总线收发单元组成

图3.1

因题目要求测量为0~5v电压,而STM32参考电压为3.3v,所以应采用放大器将5v转化为3.3v,正相放大器放大倍数均大于一,反相放大器放大后结果为负,因此采用两个反向放大器来实现。放大电路如图3.2

图3.2 将采集信号放大为参考电压电路

其中Rf1/R1=3.3/5,Rf2=R1’

Vo R

Vi

Rf

-

=)

(则,输入电压为5v是,Vo为3.3v,为参考

R-

Rf

)'1

(*

/2

/

1

电压。

整体硬件原理图见附录1

4软件实现

软件实现主要分为STM32的电压数据采集实现和CAN总线CAN 总线节点的软件设计。

4.1STM32电压数据采集的软件实现

4.1.1AD-DA模块的编写

STM32F105最少有2个AD模数转换器,每个ADC都有18个通道,可以测量16个外部和2个内部模拟量。最大转换频率为1Mhz,也就是转换时间为1us(在ADCCLK = 14Mhz,采样周期为1.5个时钟周期时)。最大时钟超过14Mhz,将导致ADC转换准确度降低。stm32的ADC是12位精度的。stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。此处只使用规则通道实现独立模式的中断采样。stm32的ADC可以由外部事件触发(例如定时器捕获,EXTI线)和软件触发(即在配置相关寄存器时,直接开启采样)。

ADC采样得到的只是一个相对值,将转换值/4096*参考电压即可得到采样电压这里的4096是因为stm32的adc为12位精度,表示参考电压时即为

2^12=4096

AD转换程序代码如下:

#include

#include "system.h"

#include "usart.h"

#include "adc.h"

#include "stdio.h"

#define LED1 PAout(4)

#define LED2 PAout(5)

#define VREF 3.3 //参考电压

void Gpio_Init(void);

int main(void)

{

u16 ADValue;

float temp;

Rcc_Init(9); //系统时钟设置

Usart1_Init(72,9600); //设置串口时钟和波特率

Adc1_Init(8,7); //使用8通道采样,采样时间系数为7(111),据手册可得采样时间为 (239.5+12.5)/12= 21 (us)

Gpio_Init();

while(1){

ADValue = Get_Adc(ADC_1,8);

temp = (float)VREF*(ADValue/4096); //ADC精度为12位精度,即达到 VREF电压时为 2^12 = 4096

printf("\r\n ADValue = %.2fv\r\n",temp);

LED2 = !LED2;

delay(100000); //延时100ms

}

}

void Gpio_Init(void)

{

RCC->APB2ENR|=1<<2; //使能PORTA时钟

RCC->APB2ENR|=1<<3; //使能PORTB时钟

GPIOA->CRL&=0xFF0FFFF0;

GPIOA->CRL|=0xFF3FFFF0; // PA0设置为模拟输入,PA4推挽输出

GPIOB->CRL&=0xFFFFFFF0;

GPIOB->CRL|=0xFFFFFFF0; // PB0设置为模拟输入

//USART1 串口I/O设置

GPIOA -> CRH&=0xFFFFF00F; //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入

GPIOA -> CRH|=0x000008B0;

}

Library/src/adc.c

#include

#include "adc.h"

//ADC1采样初始化

//独立工作模式

//参数说明:

// ADC_CH_x 选择使用通道(0~17),目前暂支持0~15通道// ADC_CH_SMP 设定采样周期(0~7)

//采样周期算法:

void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP)

{

RCC -> APB2ENR |= 1<<9; //开启ADC1时钟

RCC -> APB2RSTR |= 1<<9; //复位ADC1

RCC -> APB2RSTR &= ~(1<<9); //ADC1复位结束

RCC -> CFGR &= ~(3<<14); //分频因子清零

RCC -> CFGR |= 2<<14; //设定分频因数为2,PCLK2 6分频后作为ADC时钟

ADC1 -> CR1 &= 0xF0FFFF; //工作模式清零

ADC1 -> CR1 |= 0<<16; //设定为独立模式

ADC1 -> CR1 &= ~(1<<8); //非扫描工作模式

ADC1 -> CR2 &= ~(1<<1); //关闭连续转换

ADC1 -> CR2 &= ~(7<<17); //清除规则通道启动事件

ADC1 -> CR2 |= 7<<17; //设定规则通道启动事件为软件启动(SWSTART)

ADC1 -> CR2 |= 1<<20; //使用外部事件触发 SWSTART

ADC1 -> CR2 &= ~(1<<11); //设置对齐模式为右对齐

ADC1 -> SQR1 &= ~(0xF<<20); //清零规则序列的数量

ADC1 -> SQR1 |= 15<<20; //设置规则序列的数量为16

ADC1 -> SMPR2 &= 0x00000000; //清零通道采样时间

ADC1 -> SMPR1 &= 0xFF000000;

if(ADC_CH_x <= 9 ){

ADC1 -> SMPR2 |= 7<<(ADC_CH_x*3); //设置通道x采样时间,提高采样时间可以提高采样精度

}

if(ADC_CH_x > 9 ){

ADC1 -> SMPR1 |= 7<<((ADC_CH_x-10)*3);

}

ADC1 -> CR2 |= 1<<0; //开启AD转换

ADC1 -> CR2 |= 1<<3; //使能复位校准,由硬件清零

while((ADC1 -> CR2)& (1<<3)); //等待校准结束

ADC1 -> CR2 |= 1<<2; //开启AD校准,由硬件清零

while((ADC1 -> CR2)& (1<<2)); //等待校准结束

}

//取得数模转换的值

//参数说明:(参数定义于adc.h)

// ADC_x (0~3),选择数模转换器

// ADC_CH_x (0~15),选择通道

u16 Get_Adc(u8 ADC_x,u8 ADC_CH_x)

{

u16 data = 0;

switch(ADC_x)

{

case 1 : {

ADC1 -> SQR3 &= 0xFFFFFFE0; //清除通道选择

ADC1 -> SQR3 |= ADC_CH_x; //选择通道

ADC1 -> CR2 |= 1<<22; //开启AD转换

while(!(ADC1 -> SR & 1<<1)); //等待转换结束

data = ADC1->DR;

相关文档
最新文档