用硬件模拟车载系统(过程记录)

硬件 :
开发板:正点原子idmx6ull mini
电位器
方案:

	- .adc管道读取电位器的值来模拟车速
	- .用内核socket can进行驱动模块开发
  1. 移植linux内核
  2. 移植ip,can-untils
  3. 开发内核驱动模块
  4. 测试验证

移植linux内核

uboot移植

  • uboot用于启动linux大概流程:先初始化DDR等外设,然后将linux内核从flash拷贝到DDR中,启动内核。
  • 从正点原子或者NXP官方下载官方uboot源码和交叉编译工具
  • 使用交叉编译器编译源码
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_deconfig
    make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
    
  • 使用正点原子提供的烧录工具将编译出的u-boot.bin文件拷贝到sd卡中
    ./imxdownload u-boot.bin /dev/sd
    
  • 将sd卡插入开发板后开发板成功启动
  • 查看EMMC驱动是否正常
    => mmc list
    	FSL_SDHC: 0 (SD)  #SD卡
    	FSL_SDHC: 1		  #emmc
    => mmc dev 1
    	switch to partitions #0, OK
    	mmc1(part 0) is current device
    => mmc info
    	Device: FSL_SDHC
    	Manufacturer ID: 15
    	OEM: 100
    	Name: 8GTF4
    	Tran Speed: 52000000
    	Rd Block Len: 512
    	MMC version 4.0
    	High Capacity: Yes
    	Capacity: 7.3 GiB
    	Bus Width: 8-bit
    	Erase Group Size: 512 KiB
    
    emmc输出结果正常,驱动没有问题
  • 网络无法识别,需要修改网络驱动
    在这里插入图片描述
    mini开发板只有enet2一个网卡,ENET2 的复位引脚 ENET2_RST 接到了I.MX6ULL 的 SNVS_TAMPER8 上。PHY 器件地址为 0X1。
    打开u-boot/board/freescale/mx6ull_14x14_evk/mx6ull_14x14_evk.c
    		/*原代码部分修需改部分*/
    /*宏部分*/
    #define IOX_SDI IMX_GPIO_NR(5, 10)
    #define IOX_STCP IMX_GPIO_NR(5, 7)
    #define IOX_SHCP IMX_GPIO_NR(5, 11)
    #define IOX_OE IMX_GPIO_NR(5, 8)
    
    					/*iomux_v3_cfg_t const iox_pads[]*/
    static iomux_v3_cfg_t const iox_pads[] = {
     /* IOX_SDI */
     MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
    /* IOX_SHCP */
    MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
    /* IOX_STCP */
     MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
    /* IOX_nOE */
    MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
    };
    
    				/*iox74lv_init iox74lv_set board_init*/
    static void iox74lv_init(void);
    void iox74lv_set(int index);
    
    int board_init(void)
    {
    ......
     imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
     iox74lv_init();
     ......
     return 0;
    }
    
    		/*iomux_v3_cfg_t const fec2_pads[]*/
    static iomux_v3_cfg_t const fec2_pads[] = {
    	MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL), 
    	MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
    ......
    	MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    	MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
    	};
    
    
    	/*setup_iomux_fec(int fec_id)*/
    static void setup_iomux_fec(int fec_id)
    {
    	if (fec_id == 0)
    	imx_iomux_v3_setup_multiple_pads(fec1_pads,
    	ARRAY_SIZE(fec1_pads));
    	else
    	imx_iomux_v3_setup_multiple_pads(fec2_pads,
    	ARRAY_SIZE(fec2_pads));
    }
    
    		/*修改后*/
    /*宏部分*/
    /* #define IOX_SDI IMX_GPIO_NR(5, 10)
    #define IOX_STCP IMX_GPIO_NR(5, 7)
    #define IOX_SHCP IMX_GPIO_NR5(5, 11)
    #define IOX_OE IMX_GPIO_NR(5, 8)*/
    
    #define ENET1_RESET IMX_GPIO_NR(5, 7)
    #define ENET2_RESET IMX_GPIO_NR(5, 8)
    
                 /*iomux_v3_cfg_t const iox_pads[]*/
    //static iomux_v3_cfg_t const iox_pads[] = {
     ///* IOX_SDI */
     //MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
    ///* IOX_SHCP */
    //MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
    ///* IOX_STCP */
     //MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
    ///* IOX_nOE */
    //MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
    //};
    
              /*iox74lv_init iox74lv_set board_init*/
    //static void iox74lv_init(void);将原函数删除
    //void iox74lv_set(int index);将原函数删除
    
    int board_init(void)
    {
    ......
     //imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
     //iox74lv_init();
     ......
     return 0;
    }
    
    		/*iomux_v3_cfg_t const fec2_pads[]*/
    static iomux_v3_cfg_t const fec2_pads[] = {
    	MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
    	MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
    ......
    	MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    	MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
    	MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
    	};
    
    	/*setup_iomux_fec(int fec_id)*/
    static void setup_iomux_fec(int fec_id)
    {
    	if (fec_id == 0)
    	{
    
    		imx_iomux_v3_setup_multiple_pads(fec1_pads,
    		ARRAY_SIZE(fec1_pads));
    	
    		gpio_direction_output(ENET1_RESET, 1);
    		gpio_set_value(ENET1_RESET, 0);
    		mdelay(20);
    		gpio_set_value(ENET1_RESET, 1);
    	}
    	else
    	{
    		imx_iomux_v3_setup_multiple_pads(fec2_pads,
    		ARRAY_SIZE(fec2_pads));
    		gpio_direction_output(ENET2_RESET, 1);
    		gpio_set_value(ENET2_RESET, 0);
    		mdelay(20);
    		gpio_set_value(ENET2_RESET, 1);
    	}
    	mdelay(150); /* 复位结束后至少延时 150ms 才能正常使用*/
    }
    
  • 修改后重新编译烧录到开发板中
        U-Boot 2016.03 (Apr 03 2025 - 20:12:53 +0800)
      
      CPU:   Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)
      CPU:   Industrial temperature grade (-40C to 105C) at 50C
      Reset cause: WDOG
      Board: ALIENTEK ALPHA EMMC
      I2C:   ready
      DRAM:  512 MiB
      MMC:   FSL_SDHC: 0, FSL_SDHC: 1
      In:    serial
      Out:   serial
      Err:   serial
      switch to partitions #0, OK
      mmc0 is current device
      Net:   FEC1  #网络驱动加载成功
      Normal Boot
      Hit any key to stop autoboot:  0
      => ping 192.168.6.7
      FEC1 Waiting for PHY auto negotiation to complete.... done
      Using FEC1 device
      host 192.168.6.7 is alive #成功ping通pc网络
    
    网卡驱动修改完成

linux内核

  • 从正点原子官或NXP官方下载linux内核

    #编译内核
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make imx_v7_mfg_defconfig
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
    

    编译后能得到内核镜像zImage文件与设备树文件imx6ull-14x14-evk.dtb

  • 配置tftp启动内核
    使用交换机将开发板与pc连接到交换机使交换机与pc在同一网络
    pc端

      #下载tftp
      sudo apt-get install tftp-hpa tftpd-hpa xinetd
      mkdir /home/user/linux/tftpboot && chmod 777 /home/user/linux/tftpboot
      #将内核文件与设备树文件拷贝到tftpboot目录中
      cp arch/arm/boot/zImage /home/user/linux/tftpboot
      cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb  /home/user/linux/tftpboot
    

    开发板u-boot界面

    	setenv ipaddr 192.168.6.133         # 开发板IP
      	setenv serverip 192.168.6.7       # Ubuntu的IP
      	setenv gatewayip 192.168.6.1        # 网关
      	setenv netmask 255.255.255.0        # 子网掩码
      	saveenv
      
      	#tftp加载内核,将内核镜像与设备树通过tftp加载到DDR中的指定位置
      setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000'
      saveenv
    

    重启开发板
    在这里插入图片描述
    从启动日志可以看出成功加载了zImage与设备树并成功启动内核

  • 修改emmc驱动
    在这里插入图片描述
    需要将EMMC的驱动从4线模式修改为8线模式
    打开文件imx6ull-alientek-emmc.dts

    	//原代码
    	&usdhc2 {
    		pinctrl-names ="default";
    		pinctrl-0=<&pinctrl_usdhc2>;
    		non-removable;
    		status ="okay";
    		};
    	//修改为
    	&usdhc2 {
    		pinctrl-names ="default","state_100mhz","state_200mhz";
    		pinctrl-0=<&pinctrl_usdhc2_8bit>;
    		pinctrl-1=<&pinctrl_usdhc2_8bit_100mhz>;
    		pinctrl-2=<&pinctrl_usdhc2_8bit_200mhz>;
    		bus-width =<8>;
    		non-removable;
    		no-1-8-v;	//关闭1.8v供电选项
    		status ="okay";
    	};
    
  • 修改网络设备引脚
    之前移植u-boot时发现网卡部分有做改动,所有在内核中也需修改对应网络驱动
    imx6ull-alientek-emmc.dts

    pinctrl_spi4:spi4grp {
    				fsl,pins =<
    						MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10     0x70a1
    						MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11     0x70a1
    						//MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07   0x70a1  //删除此行
    						//MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08   0x80000000 //删除此行
    					>;
    			};
    
    spi4 {
    		compatible ="spi-gpio";
    		pinctrl-names ="default";
    		pinctrl-0=<&pinctrl_spi4>;
    		// pinctrl-assert-gpios =<&gpio5 8GPIO_ACTIVE_LOW>;//删除此行
    	......
    		// cs-gpios =<&gpio5 70>;//删除此行
    
    &iomuxc_snvs {	
      		imx6ul-evk {
      						/*添加*/
      	 			pinctrl_enet2_reset:enet2resetgrp {
      	 						fsl,pins =<MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08      0x10B0>;
      					};
      		};
    
    pinctrl_enet2:enet2grp {
    			fsl,pins =<
    					MX6UL_PAD_GPIO1_IO07__ENET2_MDC     0x1b0b0
    					MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 	0x1b0b0
    					MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN  0x1b0b0
    					MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER	0x1b0b0
    					MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
    					MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
    					MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
    					MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
    					MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
    					MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009  //原默认值0x4001b031
    			>;
    		};
    
    &fec2 {  //修改网卡
        	pinctrl-names ="default";
        	pinctrl-0=<&pinctrl_enet2
        					&pinctrl_enet2_reset>;
        	phy-mode ="rmii";
        	phy-handle =<&ethphy1>;
        	phy-reset-gpios =<&gpio5 8GPIO_ACTIVE_LOW>;
        	phy-reset-duration =<200>;
        	status ="okay";
        		mdio {
        				#address-cells =<1>;
        				#size-cells =<0>;
      					
      				ethphy1:ethernet-phy@1 {
      							compatible ="ethernet-phy-ieee802.3-c22";
      							smsc,disable-energy-detect;
      							reg =<1>;
      							};
      			};
      };
    

    修改文件drivers/net/ethernet/freescale/fec_main.c

    static int fec_probe(structplatform_device *pdev)
    {
    ......
    	//添加设置MX6UL_PAD_ENET2_TX_CLK,IO的复用寄存器的SION位为1。
    	void__iomem *IMX6U_ENET2_TX_CLK;
    	IMX6U_ENET2_TX_CLK =ioremap(0X020E00FC,4);
    	writel(0X14,IMX6U_ENET2_TX_CLK);
    ......
    }
    

    使用make menuconfig使能LAN8720

    -> Device Drivers 
    	-> Network device support 
    		-> PHYDevice support and infrastructure 
    		  -> Drivers for SMSC PHYs
    

    修改drivers/net/phy/smsc.c

    staticintsmsc_phy_reset(structphy_device *phydev)
    {
    .......
    	if(phydev->addr ==1)/* FEC2  */{
    	np =of_find_node_by_path("/soc/aips-bus@02000000/ethernet@020b4000");
    	if(np ==NULL){
    		return-EINVAL;
    	}
    	err =of_property_read_u32(np,"phy-reset-duration",&msec);
    	if(!err &&msec >1000)
    		msec =1;
    	phy_reset =of_get_named_gpio(np,"phy-reset-gpios",0);
    	if(!gpio_is_valid(phy_reset))
    		return;
    	gpio_direction_output(phy_reset,0);
    	gpio_set_value(phy_reset,0);
    	msleep(msec);
    	gpio_set_value(phy_reset,1);
    	int rc =phy_read(phydev,MII_LAN83C185_SPECIAL_MODES);
    	if(rc <0)
    		return rc;
    	if((rc &MII_LAN83C185_MODE_MASK)==MII_LAN83C185_MODE_POWERDOWN){
    			rc |=MII_LAN83C185_MODE_ALL;
    			phy_write(phydev,MII_LAN83C185_SPECIAL_MODES,rc);
    	}
    	phy_write(phydev,MII_BMCR,BMCR_RESET);
    	do{
    		udelay(10);
    		if(timeout--==0)
    			return-1;
    		rc =phy_read(phydev,MII_BMCR);
    		}while(rc &BMCR_RESET);
    		return 0;
    	};
    ......
    

nfs文件系统

  • 制作文件系统从正点原子下载busybox源码
  • 编译
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
    
    配置
    Location: 
    	-> Settings 
    		-> Build static binary (no shared libs)
    
    Location:   
    	-> Settings  
    		-> vi-style line editing commands
    
    Location: 
    	-> Linux Module Utilities
    	-> Simplified modutils
    
    Location:
    	-> Linux System Utilities   
    	->mdev (16 kb) 
    
    Location: 
    	-> Settings
    		-> Support Unicode
    			-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables
    
  • 继续编译
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=/home/zuozhongkai/linux/nfs/rootfs

添加库文件

	mkdir lib
	cp /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/*so* *.a \
		/home/zuozhongkai/linux/nfs/rootfs/lib/ -d

pc安装nfs服务并配置

	sudo apt-get install nfs-kernel-server
	mkdir /home/user/linux/nfs
	echo "/home/user/linux/nfs *(rw,sync,no_root_squash)" >> /etc/exports
	sudo service nfs-kernel-server restart

配置开发板参数

 ==>setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw\
 					nfsroot=192.168.6.7:/home/user/linux/nfs/rootfs\
					ip=192.168.6.133:192.168.6.7:192.168.6.1:255.255.255.0::eth0:off'
 ==>saveenv
  • 重启开发板 成功进入文件系统
    在这里插入图片描述
    成功完成移植linux

移植ip,can-untils

  • ip命令 正点原子资料库下载iproute源码
	//修改顶层Makefile
		//CC := gcc
		CC:=arm-linux-gnueabihf-gcc

直接make进行编译将ip命令拷入开发板

cp ip nfsroot/sbin/ip -f

开发板查看是否移植成功

	/ # ip -V
	ip utility, iproute2-ss160111

此输出表示移植成功

  • can-untils命令 正点原子资料库下载can-untils源码
    编译
      ./autogen.sh 
      ./configure --target=arm-linux-gnueabihf --host=arm-linux-gnueabihf --
      prefix=/home/zuozhongkai/linux/IMX6ULL/tool/can-utils --disable-static --enable-shared
      make 
      make instal
      
      sudo cp bin/* nfsroot/usr/bin/ -f
    
    开发板配置
      ip link set can0 type can bitrate 500000 loopback on
      ifconfig can0 up 
      candump can0 & 
      cansend can0 5A1#11.22.33.44.55.66.77.88 
    
    在这里插入图片描述
    此信息表示can配置成功

开发内核驱动

ADC驱动(电位器)

  • 将电位器sig交连接开发板的gpio01引脚
  • 修改设备树
	&iomuxc {
	......
			//添加
			pinctrl_adc1: adc1grp {
   			fsl,pins = <
   				MX6UL_PAD_GPIO1_IO01__GPIO1_IO01    0xb0
   			>;
   			};
	......	
	}

   regulators {
   	......
   	//添加
   			reg_vref_3v3: regulator@3 {
   				compatible = "regulator-fixed";
   				regulator-name = "vref-3v3";
   				regulator-min-microvolt = <3300000>;
   				regulator-max-microvolt = <3300000>;
   				regulator-always-on;
   			};
   	......
   }
   
   &adc1 {
   	pinctrl-names = "default";
   	pinctrl-0 = <&pinctrl_adc1>;
   	num-channels = <2>;
   	vref-supply = <&reg_vref_3v3>;
   	status = "okay";
   };

修改完成后将新编译的设备树加载到开发板
因为官方提供的内核自带有iio adc驱动所以我们直接查看是否获取到正确值即可

cat /sys/bus/iio/devices/iio\:device0/in_voltage1_raw
1030

读取到变位器数据,adc加载成功

内核驱动模块

获取adc的值并转化为can报文发送

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/netdevice.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/timer.h>
#include <linux/workqueue.h>  
#include <linux/can/raw.h>

#define CAN_ID 0x123
#define ADC_RAW_PATH "/sys/bus/iio/devices/iio:device0/in_voltage1_raw"

static int read_adc_value(int *val);
static void send_can_frame(int speed);
static struct timer_list adc_timer;
static struct net_device *can_dev;
static struct file *adc_filp;
static struct work_struct adc_work;  // 定义工作队列

void send_can_frame(int speed)  
{
    struct sk_buff *skb;
    struct can_frame *frame;

    if (!can_dev || !netif_running(can_dev))
        return;

    skb = alloc_can_skb(can_dev, &frame);
    if (!skb) {
        printk(KERN_ERR "CAN skb alloc failed\n");
        return;
    }

    frame->can_id = CAN_ID;
    frame->can_dlc = 2;
    frame->data[0] = speed >> 8;
    frame->data[1] = speed & 0xFF;

    netif_rx(skb);
    printk(KERN_DEBUG "CAN frame sent\n");
}
//--------------------------------------------------
// 工作队列处理函数(进程上下文)
//--------------------------------------------------
static void adc_work_handler(struct work_struct *work)
{

    int adc_val, speed;
    
    if (read_adc_value(&adc_val) == 0) {
        speed = (adc_val * 200) / 4095;
        send_can_frame(speed);
    }

}

//--------------------------------------------------
// 定时器回调函数(中断上下文)
//--------------------------------------------------
static void adc_timer_callback(unsigned long data)
{
    // 仅调度工作队列,不直接操作文件或CAN
    schedule_work(&adc_work);
    mod_timer(&adc_timer, jiffies + msecs_to_jiffies(100));
}

//--------------------------------------------------
// ADC读取函数(需在进程上下文调用)
//--------------------------------------------------
int read_adc_value(int *val)
{
    char buf[16] = {0};
    loff_t pos = 0;
    int ret;

    if (IS_ERR(adc_filp))
    {
        printk("err adc_filp");
        return PTR_ERR(adc_filp);
    }

    ret = kernel_read(adc_filp, pos, buf, sizeof(buf)-1);
    if (ret < 0) {
        printk(KERN_ERR "Read ADC failed: %d\n", ret);
        return ret;
    }

    if (ret >= 0 && ret < sizeof(buf))
        buf[ret] = '\0';
    else
        buf[sizeof(buf)-1] = '\0';

    if (kstrtoint(buf, 10, val)) {
        printk(KERN_ERR "Convert ADC value failed\n");
        return -EINVAL;
    }

    return 0;
}

//--------------------------------------------------
// 模块初始化
//--------------------------------------------------
static int __init gpio_adc_init(void)
{
    printk(KERN_INFO "Opening ADC path: %s\n", ADC_RAW_PATH);

    // 初始化工作队列
    INIT_WORK(&adc_work, adc_work_handler);

    // 打开ADC文件(在进程上下文中提前打开)
    adc_filp = filp_open(ADC_RAW_PATH, O_RDONLY, 0);
    if (IS_ERR(adc_filp)) {
        printk(KERN_ERR "Open ADC failed: %ld, Path: %s\n", PTR_ERR(adc_filp), ADC_RAW_PATH);
        return PTR_ERR(adc_filp);
    }

    // 获取CAN设备
    can_dev = dev_get_by_name(&init_net, "can0");
    if (!can_dev) {
        filp_close(adc_filp, NULL);
        printk(KERN_ERR "CAN device can0 not found\n");
        return -ENODEV;
    }

    // 检查并启动CAN接口
    if (!netif_running(can_dev)) {
        int err = dev_open(can_dev);
        if (err) {
            printk(KERN_ERR "Failed to start can0: %d\n", err);
            dev_put(can_dev);
            filp_close(adc_filp, NULL);
            return err;
        }
    }

    // 启动定时器(回调函数仅触发工作队列)
printk("start timer\n");
    init_timer(&adc_timer);
    adc_timer.function = adc_timer_callback;
    adc_timer.expires = jiffies + msecs_to_jiffies(100);
    add_timer(&adc_timer);

printk("init success\n");

    return 0;
}

//--------------------------------------------------
// 模块退出
//--------------------------------------------------
static void __exit gpio_adc_exit(void)
{
    del_timer_sync(&adc_timer);
    cancel_work_sync(&adc_work); // 等待工作队列完成
    if (!IS_ERR(adc_filp))
        filp_close(adc_filp, NULL);
    if (can_dev)
        dev_put(can_dev);
}

module_init(gpio_adc_init);
module_exit(gpio_adc_exit);
MODULE_LICENSE("GPL");

# drivers/adc/Makefile
KERNELDIR := /home/xiao/project/vehicle-system/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)

obj-m = engine_sim.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules  

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean    

编译成功后将编译出来engine_sim.ko的文件拷贝到开发板moudule/lib/4.1.15/中进行验证


测试验证

在这里插入图片描述
成功捕获到can数据报文
实验成功


后续

后续将使用PCAN模拟更多的CAN报文、添加车载系统的信号解析、状态管理、诊断服务、网络管理等功能。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐