由于最近工作需要,需要用到can总线,自己以前又没有用到过can总线,所以记录下来自己的学习过程。
由于我是在linux下操作can总线的,所以一下内容主要是linux下的can操作过程。
首先,配置linux下can驱动,我所用的平台是AM335x,AM335x有两个can接口,can0和can1,以下为can0的配置过程,can1类似
1:进入mach_omap2目录修改mux33xx.c文件,修改后:
_AM33XX_MUXENTRY(UART1_CTSN,0,
"uart1_ctsn",NULL, "d_can0_tx", "i2c2_sda",
"spi1_cs0",NULL, NULL,"gpio0_12"),
_AM33XX_MUXENTRY(UART1_RTSN,0,
"uart1_rtsn",NULL, "d_can0_rx", "i2c2_scl",
"spi1_cs1", NULL,NULL, "gpio0_13"),
2:修改board-am335xevm.c:
static structpinmux_config d_can_wxudong_pin_mux[] = {
{"uart1_ctsn.d_can0_tx",OMAP_MUX_MODE2 | AM33XX_PULL_ENBL},
{"uart1_rtsn.d_can0_rx",OMAP_MUX_MODE2 | AM33XX_PIN_INPUT_PULLUP},
{NULL, 0},
};
static voidd_can_init(int evm_id, int profile)
{
lsd_dbg(LSD_DBG,"Enter boardinit:%s\n",__FUNCTION__);
switch (evm_id){
caseLOW_COST_EVM:
setup_pin_mux(d_can_wxudong_pin_mux);
am33xx_d_can_init(0);
break;
}
}
static struct evm_dev_cfg beaglebone_dev_cfg[]= {
{d_can_init, DEV_ON_BASEBOARD,PROFILE_NONE},
{NULL, 0, 0},
};
添加d_can_init函数
修改完之后重新编译内核,烧录内核到AM335x平台上,在命令行中输入ifconfig-a会发现can0设备。
第二部,编写can应用层demo,这里要注意,can单设备可以用示波器测量TX引脚,但是只能用回环模式,
在命令行输入canconfigcan0 ctrlmode loopback on,回环模式测试
测试程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<linux/if.h>
#include<linux/sockios.h>
#include<linux/ioctl.h>
#include<getopt.h>
#include<sys/mman.h>
#include"ioctl.h"
#include"can.h"
int signalquit;
void sighandler(intsig)
{
switch(sig){
case SIGINT:
signalquit =1;
break;
}
}
static voidprint_usage(char *prg)
{
fprintf(stderr,
"Usage: %s <can-interface> [Options] \n"
"Options:\n"
" -h,--help thishelp\n"
" -b,--baudrate=BPS baudrate in bits/sec\n"
" -B,--bittime=BRP:PROP_SEG:PHASE_SEG1:PHASE_SEG2:SJW:SAM\n",
prg);
}
can_baudrate_tstring_to_baudrate(char *str)
{
can_baudrate_t baudrate;
if (sscanf(str, "%i",&baudrate)!= 1)
return-1;
return baudrate;
}
int main(int argc,char *argv[])
{
char ifname[16];
int can_fd = -1;
int new_baudrate = -1;
int bittime_count = 0, bittime_data[6];
struct ifreq ifr;
structsockaddr_can addr;
structcan_frame frame;
can_baudrate_t*baudrate;
structcan_bittime *bittime;
intopt, ret;
char*ptr;
intnbytes,i;
structoption long_options[] = {
{ "help",no_argument, 0, 'h' },
{ "baudrate",required_argument, 0, 'b'},
{ "bittime",required_argument, 0, 'B'},
{ 0, 0, 0, 0},
};
while((opt =getopt_long(argc, argv, "hb:B:",long_options, NULL)) != -1) {
switch (opt) {
case 'h':
print_usage(argv[0]);
exit(0);
case 'b':
new_baudrate =string_to_baudrate(optarg);
if (new_baudrate ==-1) {
print_usage(argv[0]);
exit(0);
}
break;
case 'B':
ptr = optarg;
while (1) {
bittime_data[bittime_count++]= strtoul(ptr, NULL, 0);
if (!(ptr = strchr(ptr,':')))
break;
ptr++;
}
if (bittime_count!= 6) {
print_usage(argv[0]);
exit(0);
}
break;
default:
fprintf(stderr,"Unknown option %c\n", opt);
break;
}
}
/* Get CAN interface name */
if(optind != argc - 1 &&optind != argc - 2) {
print_usage(argv[0]);
return 0;
}
strncpy(ifname,argv[optind], IFNAMSIZ);
strncpy(ifr.ifr_name,ifname, IFNAMSIZ);
can_fd=socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(can_fd <0) {
perror("socket");
return can_fd;
}
ret=ioctl(can_fd, SIOCGIFINDEX, &ifr);
if(ret) {
perror("ioctl:SIOCGIFINDEX");
return ret;
}
addr.can_family = PF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(new_baudrate != -1) {
printf("baudrate:%d\n", new_baudrate);
baudrate = (can_baudrate_t*)&ifr.ifr_ifru;
*baudrate = new_baudrate;
ret = ioctl(can_fd,SIOCSCANBAUDRATE, &ifr);
if (ret) {
perror("ioctl:SIOCSCANBAUDRATE");
gotoabort;
}
}
if(bittime_count) {
bittime = (structcan_bittime *)&ifr.ifr_ifru;
bittime->type =CAN_BITTIME_STD;
bittime->std.brp=bittime_data[0];
bittime->std.prop_seg=bittime_data[1];
bittime->std.phase_seg1=bittime_data[2];
bittime->std.phase_seg2=bittime_data[3];
bittime->std.sjw=bittime_data[4];
bittime->std.sam=bittime_data[5];
printf("bit-time:brp=%d prop_seg=%d phase_seg1=%d "
"phase_seg2=%d sjw=%dsam=%d\n",
bittime->std.brp,
bittime->std.prop_seg,
bittime->std.phase_seg1,
bittime->std.phase_seg2,
bittime->std.sjw,
bittime->std.sam);
ret = ioctl(can_fd,SIOCSCANCUSTOMBITTIME, &ifr);
if (ret) {
perror("ioctl:SIOCSCANCUSTOMBITTIME\n");
goto abort;
}
}
if(bind(can_fd,(struct sockaddr*)&addr,sizeof(addr))){
perror("bind\n");
goto abort;
}
<span style="white-space:pre"></span> memset(&frame,0x0,sizeof(frame));
frame.can_id = 0x123;
frame.can_dlc =8;
strncpy(frame.data,"12345678",8);
while(!signalquit){
nbytes =write(can_fd,&frame,sizeof(frame));
if(nbytes < 0){
perror("write");
goto abort;
}
if(nbytes < sizeof(structcan_frame)){
printf("write:incompletecan frame\n");
goto abort;
}
printf("write acanframe:can_id=0x%x,can_dlc=%d,data=%s\n",frame.can_id,frame.can_dlc,frame.data);
for(i=0;i<10000;i++);
}
close(can_fd);
return0;
abort:
close(can_fd);
returnret;
}
然后再用 candump can0或者示波器测量tx脚即可看到输出波形。
参考文献
http://processors.wiki.ti.com/index.php/Talk:AM335X_DCAN_Driver_Guide