硬件模拟车载系统1
编译成功后将编译出来engine_sim.ko的文件拷贝到开发板moudule/lib/4.1.15/中进行验证。后续将使用CANoe模拟更多的CAN报文、添加车载系统的信号解析、状态管理、诊断服务、网络管理等功能。编译后能得到内核镜像zImage文件与设备树文件imx6ull-14x14-evk.dtb。修改文件drivers/net/ethernet/freescale/fec_main.c。
用硬件模拟车载系统(过程记录)
硬件 :
开发板:正点原子idmx6ull mini
电位器
方案:
- .adc管道读取电位器的值来模拟车速
- .用内核socket can进行驱动模块开发
- 移植linux内核
- 移植ip,can-untils
- 开发内核驱动模块
- 测试验证
移植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驱动是否正常
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 - 网络无法识别,需要修改网络驱动

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.dtspinctrl_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 =<ðphy1>; 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- menuconfigLocation: -> 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/ -fip 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 = <®_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报文、添加车载系统的信号解析、状态管理、诊断服务、网络管理等功能。
更多推荐


所有评论(0)