LS1043A开发调试记录

前言

本次目的是使用自定义的硬件,CPU为LS1043A,系统跑起来,通过配置或者编写驱动使各个外设能够正常使用,硬件部分主要是参考,主要记录软件开发过程中遇到的困难以及解决方法。主要包括的外设有IIC温度传感器 TMP75、IIC RTC时钟 DS1339、看门狗、GPIO、PWM以及四线风扇、网络、普通串口以及485串口等。 本文重点不是系统启动等调试,而是在UBOOT和内核文件系统能正常启动的情况下,依据硬件外设,使外设能够正常符合标准使用,驱动分析涉及一部分,由于内容太多不会太详细。

开发环境

  1. 开发系统:ubuntu20.04
  2. NXP SDK : LSDK21.08
  3. 内核版本 : linux 5.10
  4. 内核默认配置: ls1043ardb_defconfig

一些基础知识

1. 寄存器读写

LS1043的寄存器有的是大端有的是小端,如果要对寄存器进行配置,需要根据手册查找,然后进行配置;并且,官方内核不能使用 /dev/mem 访问物理地址,具体原因未去探究,可使用mtools。

2. 设备树

对于设备树,中断号配置需要说明一下,以 LPUART1 为例, 手册中中断向量号为80,设备树中中断号为 则为 80 - 32 = 48 ,其他相同;
在这里插入图片描述

		lpuart0: serial@2950000 {
			compatible = "fsl,ls1021a-lpuart";
			reg = <0x0 0x2950000 0x0 0x1000>;
			interrupts = <0 48 0x4>;
			clocks = <&clockgen 0 0>;
			clock-names = "ipg";
			status = "disabled";
		};

3.RCW

对于RCW,IO的多路复用,根据手册配置即可,如果使用的是LSDK的工具;

各模块测试

1. RTC时钟 (DS1339)

(1)硬件部分

在这里插入图片描述

使用I2C1,IIC地址为0x68

(2)软件部分

主要是配置设备树以及内核配置,设备树以fsl-ls1043a-rdb.dts 为主,涉及fsl-ls1043.dtsi 作为基础,RTC芯片使用的DS1339。

设备树

I2C控制器硬件信息已经在设备树fsl-ls1043.dtsi中由官方给出, 其中scl-gpios是用来处理I2C死锁问题的,配置I2C总线的SCL总线即可,在发生死锁的时候驱动会将SCL配置成GPIO模型,然后控制总线,解除死锁,具体请看驱动源码:

		i2c0: i2c@2180000 {
			compatible = "fsl,vf610-i2c", "fsl,ls1043a-vf610-i2c";
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <0x0 0x2180000 0x0 0x10000>;
			interrupts = <0 56 0x4>;
			clock-names = "i2c";
			clocks = <&clockgen 4 0>;
			dmas = <&edma0 1 39>,
			       <&edma0 1 38>;
			dma-names = "tx", "rx";
			scl-gpios = <&gpio4 12 0>
			status = "disabled";
		};

注意: 此处的I2C0即硬件我们使用的I2C1, 只是标号不一样,寄存器的值0x2180000是重点,请根据硬件和参考手册去具体选择相应的I2C控制器。
在这里插入图片描述
我们只需要修改fsl-ls1043a-rdb.dts 中i2c0节点的部分即可,ds1339 如下,地址0x68

注意, status = "okay"
&i2c0 {
	status = "okay";
	rtc@68 {
		compatible = "dallas,ds1339";
		reg = <0x68>;
	};
}

设备驱动使用的是 driver/rtc/rtc-ds1307.c ,其支持的部分设备包含DS1339,如下:


static const struct of_device_id ds1307_of_match[] = {
	{
		.compatible = "dallas,ds1307",
		.data = (void *)ds_1307
	},
	{
		.compatible = "dallas,ds1308",
		.data = (void *)ds_1308
	},
	{
		.compatible = "dallas,ds1337",
		.data = (void *)ds_1337
	},
	{
		.compatible = "dallas,ds1338",
		.data = (void *)ds_1338
	},
	{
		.compatible = "dallas,ds1339",
		.data = (void *)ds_1339
	},
	{
		.compatible = "dallas,ds1388",
		.data = (void *)ds_1388
	},
	{
		.compatible = "dallas,ds1340",
		.data = (void *)ds_1340
	},
	{
		.compatible = "dallas,ds1341",
		.data = (void *)ds_1341
	},
	{
		.compatible = "maxim,ds3231",
		.data = (void *)ds_3231
	},
	{
		.compatible = "st,m41t0",
		.data = (void *)m41t0
	},
	{
		.compatible = "st,m41t00",
		.data = (void *)m41t00
	},
	{
		.compatible = "st,m41t11",
		.data = (void *)m41t11
	},
	{
		.compatible = "microchip,mcp7940x",
		.data = (void *)mcp794xx
	},
	{
		.compatible = "microchip,mcp7941x",
		.data = (void *)mcp794xx
	},
	{
		.compatible = "pericom,pt7c4338",
		.data = (void *)ds_1307
	},
	{
		.compatible = "epson,rx8025",
		.data = (void *)rx_8025
	},
	{
		.compatible = "isil,isl12057",
		.data = (void *)ds_1337
	},
	{
		.compatible = "epson,rx8130",
		.data = (void *)rx_8130
	},
	{ }
};
内核配置

根据LSDK手册配置RTC驱动:
在这里插入图片描述在这里插入图片描述
以及DS1339的设备驱动:

软件测试

在以上配置成功之后,进入文件系统,我们会在设备中看到RTC,并且使用工具hwclock能读取到硬件时间,以及写入时间,重启设备时间不变等。
在这里插入图片描述

注意

当使用hwclock的时候,可能i2c_dectect 工具并不能很好的探测到RTC时钟。

2. 温度传感器(tmp75)

(1)硬件部分

RTC

使用I2C1,IIC地址为0x4a

(2)软件部分

设备树

我们只需要修改fsl-ls1043a-rdb.dts 中i2c0节点的部分即可,tmp75 如下,地址0x4a

&i2c0 {
	status = "okay";

	tmp: tmp@4a {
		compatible = "ti,tmp75";
		reg = <0x4a>;
		// 与linux thermal架构相关
		#thermal-sensor-cells = <1>;
	};

	rtc@68 {
		compatible = "dallas,ds1339";
		reg = <0x68>;
	};
};

设备驱动使用的是 driver/hwmon/lm75.c,其支持的设备包含tmp75, 如下:

static const struct of_device_id __maybe_unused lm75_of_match[] = {
	{
		.compatible = "adi,adt75",
		.data = (void *)adt75
	},
	{
		.compatible = "dallas,ds1775",
		.data = (void *)ds1775
	},
	{
		.compatible = "dallas,ds75",
		.data = (void *)ds75
	},
	{
		.compatible = "dallas,ds7505",
		.data = (void *)ds7505
	},
	{
		.compatible = "gmt,g751",
		.data = (void *)g751
	},
	{
		.compatible = "national,lm75",
		.data = (void *)lm75
	},
	{
		.compatible = "national,lm75a",
		.data = (void *)lm75a
	},
	{
		.compatible = "national,lm75b",
		.data = (void *)lm75b
	},
	{
		.compatible = "maxim,max6625",
		.data = (void *)max6625
	},
	{
		.compatible = "maxim,max6626",
		.data = (void *)max6626
	},
	{
		.compatible = "maxim,max31725",
		.data = (void *)max31725
	},
	{
		.compatible = "maxim,max31726",
		.data = (void *)max31725
	},
	{
		.compatible = "maxim,mcp980x",
		.data = (void *)mcp980x
	},
	{
		.compatible = "nxp,pct2075",
		.data = (void *)pct2075
	},
	{
		.compatible = "st,stds75",
		.data = (void *)stds75
	},
	{
		.compatible = "st,stlm75",
		.data = (void *)stlm75
	},
	{
		.compatible = "microchip,tcn75",
		.data = (void *)tcn75
	},
	{
		.compatible = "ti,tmp100",
		.data = (void *)tmp100
	},
	{
		.compatible = "ti,tmp101",
		.data = (void *)tmp101
	},
	{
		.compatible = "ti,tmp105",
		.data = (void *)tmp105
	},
	{
		.compatible = "ti,tmp112",
		.data = (void *)tmp112
	},
	{
		.compatible = "ti,tmp175",
		.data = (void *)tmp175
	},
	{
		.compatible = "ti,tmp275",
		.data = (void *)tmp275
	},
	{
		.compatible = "ti,tmp75",
		.data = (void *)tmp75
	},
	{
		.compatible = "ti,tmp75b",
		.data = (void *)tmp75b
	},
	{
		.compatible = "ti,tmp75c",
		.data = (void *)tmp75c
	},
	{ },
};
内核配置

选择lm75相应的驱动即可:

要选中配置 HWMON 以及 THERMAL,为后面应用层使用提供支持;

在这里插入图片描述

软件测试

软件测试可以有三种方式:

  1. 使用i2ctools 中的i2cget直接操作总线去读相应地址寄存器获取温度,这个需要参考手册去获取;
  2. 使用文件系统中的接口获取
  3. 使用应用程序sensors 获取温度:
    在这里插入图片描述

注意

使用 sensors 工具获取温度可能更新频率比较慢,温度很长时间没有变化,没有直接使用i2ctools 读取的快;

3. PWM

pwm部分主要是为风扇提供可控转速的,可以使用系统PWM或者GPIO模拟;

(1)硬件部分

在这里插入图片描述

我们使用的是PWM8,0通道

(2)软件部分

设备树

官方自带的PWM设备树在 fsl-ls1043a.dtsi 中,包括PWM和GPIO模拟部分:

		pwm0: pwm@29f0000 {
                        compatible = "fsl,vf610-ftm-pwm";
                        #pwm-cells = <3>;
                        reg = <0x0 0x29f0000 0x0 0x10000>;
                        clock-names = "ftm_sys", "ftm_ext",
                                      "ftm_fix", "ftm_cnt_clk_en";
                        clocks = <&clockgen 4 1>, <&clockgen 4 1>,
                                 <&clockgen 4 1>, <&clockgen 4 1>;
                        status = "okay";
                };

参考官方设备树,设置添加自己使用PWM8,基地址为 0x2a4_0000:
在这里插入图片描述

	pwm8: pwm@2a40000 {
		compatible = "fsl,vf610-ftm-pwm";
		#pwm-cells = <3>;
		reg = <0 0x2a40000 0 0x10000>;
        clock-names = "ftm_sys", "ftm_fix",
                      "ftm_ext", "ftm_cnt_clk_en";
		clocks = <&clockgen 4 1>, <&clockgen 4 1>, <&clockgen 4 1>, <&clockgen 4 1>;
		big-endian;
		status = "okay";
	};
	// GPIO模拟PWM, 设备树文件,使用GPIO4_2模拟
	pwm1:gpio-pwms {
		compatible = "gpio-pwms";
		pinctrl-names = "default";
		#pwm-cells = <3>;
		status = "okay";

		pwm1 {
			label = "pwm1";
			gpios = <&gpio4 2 0>;
		};
	};
注意此处的 big-endian; ,官方的PWM0没有这一字段,但是这一字段是必须的,因为这一区域1043是大端的,不设置会有问题,这是一大坑。同时,对于PWM输出,只用CH脚是输出PWM的,比如我用的是FTM8_CH0, 代表FTM8的通道0输出,这个硬件设计应注意。

在这里插入图片描述

GPIO模拟部分PWM部分,参考GPIO模拟PWM驱动

内核配置
  • 首先是引脚的多路复用配置,我使用的是FTM8_CH0管脚,这一个脚配置是在SCFG_RCWPMUXCR0 寄存器中,具体参考:
    LS1043A IIC3 IIC4 复用功能的配置
    UBOOT中使用命令 mm 往 0x157040c 写入 0x11510000 即配置成功;因为是大端,所以 51 为 16 - 23的数据;
    在这里插入图片描述
    内核配置为:
    在这里插入图片描述
    在这里插入图片描述
    GPIO模拟驱动改天另发;
软件测试

该教程使用的 FTM1 的通道1,所以export传入的是1,根据自己的情况导出不同参数;
在这里插入图片描述
因为我的是FTM8 0 通道,所以命令如下,dmesg是我自己添加的内核驱动打印,根据周期占比,风扇会有不同程度的转速改变,或者使用示波器抓取波形即可;
在这里插入图片描述
在这里插入图片描述

注意

PWM整体比较简单,官方源码就能用,主要注意的点就是设备树的大端设置问题,官方指导手册里也没有着重描述这边;

4. 四线风扇

四线风扇的使用,需要用到PWM控制风扇转速,然后需要FG脚获取风扇转速,获取风扇转速是通过获取FG脚上的脉冲频率计算得到的,只需要设置该脚的IO中断即可,具体如下。
通过FG脚检测风扇转速

在这里插入图片描述
根据风扇规格书可知风扇风速=60/(2*脉冲周期),周期T=1/频率。那么我们需要获取FG脚上的脉冲频率,即可获取风扇风速。

(1)硬件部分

在这里插入图片描述

(2)软件部分

设备树展示:
	fan1: pwm_fan1 {
		compatible = "pwm-fan";
		cooling-min-state = <0>;
		cooling-max-state = <3>;
		#cooling-cells = <2>;
		fg_gpio = <&gpio4 3 IRQ_TYPE_EDGE_RISING>;
		pwms = <&pwm1 0 100000000 0>;
		cooling-levels = <102 153 204 250>;
	};
	
	fan2: pwm_fan2 {
		compatible = "pwm-fan";
		cooling-min-state = <0>;
		cooling-max-state = <3>;
		#cooling-cells = <2>;
		fg_gpio = <&gpio4 11 IRQ_TYPE_EDGE_RISING>;
		pwms = <&pwm8 0 10000000 0>;
		cooling-levels = <102 153 204 250>;
	};

&thermal_zones {
	
	core-cluster {
		cooling-maps {
			map1 {
				trip = <&core_cluster_alert>;
				cooling-device =
				<&fan1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
				<&fan2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
		};
	};
	
	// 板级,名字自定义
	board {
		// 温控发生时的轮询周期(ms)
		polling-delay-passive = <1000>;
		// 温控未发生时轮询周期(ms)
		polling-delay = <5000>;
		// 对应的传感器, 使用前面的 tmp75 节点
		thermal-sensors = <&tmp 0>;
		
		// 温控出发点
		trips {
			// 第一部分, 0 - 35° 的时候
			balert0: board-alert0 {
				temperature = <35000>;
				// 滞后温度,表示下降到 35 - 5 = 30° 时解除温控
				hysteresis = <5000>;
				// 四种类型,passive、active、hot、critical
				// 非 critical 类型由 governor 的温控策略来降温
				type = "active";
			};
			// 温度高于 55°的时候
			balert1: board-alert1 {
				temperature = <55000>;
				hysteresis = <5000>;
				type = "passive";
			};
			// 温度高于 65° 的时候
			bcrit: board-crit {
				temperature = <65000>;
				hysteresis = <5000>;
				type = "passive";
			};
		};
			
		cooling-maps {
			// 温度高于35°, 风扇1和风扇2 状态 0 - 1
			map0 {
				trip = <&balert0>;
				cooling-device =
				<&fan1 THERMAL_NO_LIMIT 1>,
				<&fan2 THERMAL_NO_LIMIT 1>;
			};
			// 温度高于55°, 风扇1和风扇2 状态 2 - 3
			map1 {
				trip = <&balert1>;
				cooling-device =
				<&fan1 2 THERMAL_NO_LIMIT>,
				<&fan2 2 THERMAL_NO_LIMIT>;
			};
			// 温度高于65°, 风扇1和风扇2 状态无限制,全速转
			map2 {
				trip = <&bcrit>;
				cooling-device =
				<&fan1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
				<&fan2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
		};
	};
};
内核配置

驱动源码在 driver/hwmon/pwm-fan.c, 详细介绍参考pwm_fan风扇驱动

static int pwm_fan_probe(struct platform_device *pdev)
{
	// 获取平台信息,中断号
	ctx->irq = platform_get_irq_optional(pdev, 0);
	if (ctx->irq == -EPROBE_DEFER)
		return ctx->irq;
	...
	if (ctx->irq > 0) {
		// 获取中断号,申请中断处理,计算风扇转速
		ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
				       pdev->name, ctx);
		if (ret) {
			dev_err(dev, "Failed to request interrupt: %d\n", ret);
			return ret;
		}
		ctx->sample_start = ktime_get();
		mod_timer(&ctx->rpm_timer, jiffies + HZ);
	}
	...
}

static irqreturn_t pulse_handler(int irq, void *dev_id)
{
	struct pwm_fan_ctx *ctx = dev_id;
	// 只是简单的加1计数
	atomic_inc(&ctx->pulses);

	return IRQ_HANDLED;
}

// 注册一个定时器,每一秒中断处理一次
static void sample_timer(struct timer_list *t)
{
	struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
	unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
	int pulses;

	if (delta) {
		pulses = atomic_read(&ctx->pulses);
		atomic_sub(pulses, &ctx->pulses);
		// 计算风扇转速
		ctx->rpm = (unsigned int)(pulses * 1000 * 60) /
			(ctx->pulses_per_revolution * delta);

		ctx->sample_start = ktime_get();
	}

	mod_timer(&ctx->rpm_timer, jiffies + HZ);
}

软件测试
  1. 风扇转速可以使用 sensors 应用获取到;
    在这里插入图片描述

  2. 可以使用 fancontrol 应用进行控制风扇转速;

  3. 因为设备树中已经讲两个风扇添加进 thermal 架构中,所以,当温度传感器 tmp75温度到达指定的阈值时,风扇会自动采用不同的策略进行转速控制,可手动模拟温度,修改文件 /sys/class/thermal/thermal_zone0/emul_temp 模拟设置温度即可,路径根据自己实际情况修改;

注意

此部分注意的点不多,软件部分只有一个PWM和IO中断去处理,注意RCW配置多路复用;如果有问题,可以从硬件着手,先查看PWM是否正常,然后查看IO中断是否能够触发,其他的就没什么了。

5. WATCHDOG(看门狗)

(1)硬件部分

(2)软件部分

设备树

设备树不用改,官方自带的 fsl-ls1043a.dtsi 中有一个,也可根据自身情况进行添加,修改的地方只有寄存器和中断号

		wdog0: wdog@2ad0000 {
			compatible = "fsl,ls1043a-wdt", "fsl,imx21-wdt";
			reg = <0x0 0x2ad0000 0x0 0x10000>;
			interrupts = <0 83 0x4>;
			clocks = <&clockgen 4 0>;
			clock-names = "wdog";
			big-endian;
		};

如果需要产生外部重启信号,需要设备树配置 fsl,ext-reset-output:

	wdev->ext_reset = of_property_read_bool(dev->of_node,
						"fsl,ext-reset-output");
内核配置

在这里插入图片描述
在这里插入图片描述

软件测试

打开/dev/watchdog,写任何数据或者使用 ioctl 即可喂狗,如果超时,会提前一秒产生 PRETIMEOU 中断,且 REST_REQ_B 会有脉冲;


static inline void keep_alive(void)
{
	int dummy;
	int ret;

	ret = ioctl(fd, WDIOC_KEEPALIVE, &dummy);
	if (!ret)
		printf(".");
}


int main(int argc, char *argv[])
{
	int flags;
	int ret;
	char *file = "/dev/watchdog";

	fd = open(file, O_WRONLY);

	if (fd == -1) {
		if (errno == ENOENT)
			printf("Watchdog device (%s) not found.\n", file);
		else if (errno == EACCES)
			printf("Run watchdog as root.\n");
		else
			printf("Watchdog device open failed %s\n",
				strerror(errno));
		exit(-1);
	}

	flags = WATCHOUT_TIMEOUT + 1;
	ret = ioctl(fd, WDIOC_SETTIMEOUT, &flags);
	if (!ret)
		printf("Watchdog timeout set to %u seconds.\n", flags);
	else {
		printf("WDIOC_SETTIMEOUT error '%s'\n", strerror(errno));
		close(fd);
		exit(ret);
	}
	
	flags = 1;
	ret = ioctl(fd, WDIOC_SETPRETIMEOUT, &flags);
	if (!ret)
		printf("Watchdog pretimeout set to %u seconds.\n", flags);
	else {
		printf("WDIOC_SETPRETIMEOUT error '%s'\n", strerror(errno));
		close(fd);
		exit(ret);
	}
	
	while (1) {
		keep_alive();
		sleep(3);
	}


	close(fd);
	
	return 0;
}

注意

  • 看门狗常用的是用来复位,但是官方说,1043的看门狗没有内部复位电路,所以需要外部电路辅助才能复位,当看门狗超时时,会在 REST_REQ_B 脚有个脉冲,外部去处理复位逻辑。
    在这里插入图片描述
  • 1043的看门狗还有一个功能可能会用到,PRETIMEOUT这个功能,中文翻译不清楚,意思是在看门狗超时前多少秒会产生一个中断,进行一些处理,比如 imx2_wdt_set_pretimeout 设置了1秒,看门狗超时时间是10秒,那么在第9秒就会产生一个中断,中断处理中,会调用通知链中的处理函数,这方面改天细说吧。
static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg)
{
	struct watchdog_device *wdog = wdog_arg;
	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);

	regmap_write_bits(wdev->regmap, IMX2_WDT_WICR,
			  IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS);

	watchdog_notify_pretimeout(wdog);

	return IRQ_HANDLED;
}

6. GPIO输入输出

(1)硬件部分

无,就是IO直连

(2)软件部分

默认内核配置已经包含GPIO模块驱动,

设备树

fsl-ls1043a.dtsi系统自带,无需修改:

gpio1: gpio@2300000 {
			compatible = "fsl,ls1043a-gpio", "fsl,qoriq-gpio";
			reg = <0x0 0x2300000 0x0 0x10000>;
			interrupts = <0 66 0x4>;
			gpio-controller;
			#gpio-cells = <2>;
			interrupt-controller;
			#interrupt-cells = <2>;
		};
gpio2:。。。
gpio3:。。。
gpio4:。。。
内核配置

在这里插入图片描述

软件测试

GPIO寄存器比较少,映射地址如下:
在这里插入图片描述
应用层控制GPIO口,以GPIO1_31为例,首先查看GPIO1的目录:
在这里插入图片描述
我们得到,GPIO1 是 gpiochip480目录下的文件, 480 + 31 = 511, 那么我们就导出 511:

cd /sys/class/gpio
echo 511 > export
cd gpio511
echo out > direction # out 设置属性为输出 in 为输入
echo 0 > value # 输出低电平  car value 读取值
echo 1 > value # 输出高电平

注意

所使用的GPIO,要通过RCW配置成GPIO功能。

7. 485串口

(1)硬件部分

硬件部分,主要注意的是使用了RTS,串口使用LPUART1_RTS
在这里插入图片描述

(2)软件部分

设备树
内核配置
软件测试

应用程序初始化部分最为重要,此处仅展示串口初始化函数,其中主要注意的是使能485模式,SER_RS485_ENABLED,这样才能正常使用RTS信号;

int UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)
{
 
    int   i;
    int   status;
    int   speed_arr[] = {B2000000,B1500000,B1152000,B1000000,B921600,B576000,B500000,
						 B460800,B230400, B115200, B19200, B9600, B4800, B2400, B1200, B300};
	
    int   name_arr[] = {2000000,1500000,1152000,1000000,921600,576000,500000,460800,230400,
		                115200,19200,9600,4800,2400,1200,300};
 
    struct termios options;
	struct serial_rs485 rs485conf;
 

    if (ioctl (fd, TIOCGRS485, &rs485conf) < 0) {
        printf("Error: TIOCGRS485 ioctl not supported.\n");
    }
 
    /* Enable RS-485 mode: */
    rs485conf.flags |= SER_RS485_ENABLED;
 
    /* Set rts/txen delay before send, if needed: (in microseconds) */
    rs485conf.delay_rts_before_send = 100;
 
    /* Set rts/txen delay after send, if needed: (in microseconds) */
    rs485conf.delay_rts_after_send = 100;
 
    if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
        printf("Error: TIOCSRS485 ioctl not supported.\n");
    }
		 
    /*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.
    */
    if( tcgetattr( fd,&options)  !=  0)
    {
        perror("SetupSerial 1");
        return(FALSE);
    }
	int seted_flag = 0;
    //设置串口输入波特率和输出波特率
    for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
    {
    	printf("------ baudrate Speed = %d\n",name_arr[i]);
        if  (speed == name_arr[i])
        {
            cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);
			seted_flag = 1;
			printf("Setting baudrate!!\n");
			break;
        }
    }
	
	if(seted_flag == 0)
	{
		printf("Unknow baudrate!!\n");
		return FALSE;
	}
    //修改控制模式,保证程序不会占用串口
    options.c_cflag |= CLOCAL;
    //修改控制模式,使得能够从串口中读取输入数据
    options.c_cflag |= CREAD;
 
    //设置数据流控制
    switch(flow_ctrl)
    {
 
    case 0 ://不使用流控制
        options.c_cflag &= ~CRTSCTS;
        break;
 
    case 1 ://使用硬件流控制
        options.c_cflag |= CRTSCTS;
        break;
    case 2 ://使用软件流控制
        options.c_cflag |= IXON | IXOFF | IXANY;
        break;
    }
    //设置数据位
 
    //屏蔽其他标志位
    options.c_cflag &= ~CSIZE;
    switch (databits)
    {
    case 5    :
        options.c_cflag |= CS5;
        break;
    case 6    :
        options.c_cflag |= CS6;
        break;
    case 7    :
        options.c_cflag |= CS7;
        break;
    case 8:
        options.c_cflag |= CS8;
        break;
    default:     fprintf(stderr,"Unsupported data size\n");
        return (FALSE);
    }
    //设置校验位
    switch (parity)
    {
    case 'n':
    case 'N': //无奇偶校验位。
        options.c_cflag &= ~PARENB;
        options.c_iflag &= ~INPCK;
        break;
    case 'o':
    case 'O'://设置为奇校验
        options.c_cflag |= (PARODD | PARENB);
        options.c_iflag |= INPCK;
        break;
    case 'e':
    case 'E'://设置为偶校验
        options.c_cflag |= PARENB;
        options.c_cflag &= ~PARODD;
        options.c_iflag |= INPCK;
        break;
    case 's':
    case 'S': //设置为空格
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        break;
    default:
        fprintf(stderr,"Unsupported parity\n");
        return (FALSE);
    }
    // 设置停止位
    switch (stopbits)
    {
    case 1:
        options.c_cflag &= ~CSTOPB; break;
    case 2:
        options.c_cflag |= CSTOPB; break;
    default:
        fprintf(stderr,"Unsupported stop bits\n");
        return (FALSE);
    }
 
    //修改输出模式,原始数据输出
    options.c_oflag &= ~OPOST;
 
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    //options.c_lflag &= ~(ISIG | ICANON);
 
    //设置等待时间和最小接收字符
    options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
    options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */
 
    //如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
    tcflush(fd,TCIFLUSH);
 
    //激活配置 (将修改后的termios数据设置到串口中)
    if (tcsetattr(fd,TCSANOW,&options) != 0)
    {
        perror("com set error!\n");
        return (FALSE);
    }
    return (TRUE);
}

注意

如果使用了RTS信号线,就要在软件中使能485模式,才能使用;

fsl_lpuart.c	drivers\tty\serial	485串口配置部分代码

if (rs485->flags & SER_RS485_ENABLED) {
		/* Enable auto RS-485 RTS mode */
		modem |= UARTMODEM_TXRTSE;

		/*
		 * RTS needs to be logic HIGH either during transfer _or_ after
		 * transfer, other variants are not supported by the hardware.
		 */

		if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
				SER_RS485_RTS_AFTER_SEND)))
			rs485->flags |= SER_RS485_RTS_ON_SEND;

		if (rs485->flags & SER_RS485_RTS_ON_SEND &&
				rs485->flags & SER_RS485_RTS_AFTER_SEND)
			rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;

		/*
		 * The hardware defaults to RTS logic HIGH while transfer.
		 * Switch polarity in case RTS shall be logic HIGH
		 * after transfer.
		 * Note: UART is assumed to be active high.
		 */
		if (rs485->flags & SER_RS485_RTS_ON_SEND)
			modem &= ~UARTMODEM_TXRTSPOL;
		else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
			modem |= UARTMODEM_TXRTSPOL;
	}

8. 网络PHY

(1)硬件部分

使用的是裕泰8531和8614;硬件原理图,请参考裕泰硬件进行设计,此处就别贴了。请注意,对于PHY来说,第一次使用不正常,那么就要进行调试。
首先第一步,要确定是否识别到phy。读取PHY ID 使用MDIO,MDIO与MDC两根总线要上拉。然后在内核中添加打印,PHY ID能够正确读取。

在这里插入图片描述
整体架构是这样的,8531使用 EC1和EC2, 8614使用QSGMII,一共六个网口,qsgmii 地址从3开始,下图地址有点出入;

在这里插入图片描述

(2)软件部分

RCW 配置

由芯片手册查看RCW配置:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
SerDes 我们需要配成 0x4558, 可以参考 https://icode.best/i/45534640628716
0x4558 : qsgmii 1,2,5,6 两个PCIE以及一个SATA
在这里插入图片描述
对于RCW的配置,还要注意 SerDes 时钟的配置,否则还是不能使用,我们是100M,00即可:

在这里插入图片描述

设备树

文件名: fsl-ls1043a-rdb.dts, 仅供参考

&fman0 {
	 // 1 2 5 6
	// MAC1
	ethernet@e0000 {
		phy-handle = <&qsgmii_phy1>;
		phy-connection-type = "qsgmii";
		local-mac-address = [a4 ae 12 78 fb 01];
	};
	// MAC2
	ethernet@e2000 {
		phy-handle = <&qsgmii_phy2>;
		phy-connection-type = "qsgmii";
		local-mac-address = [a4 ae 12 78 fb 02];
	};
	// MAC5
	ethernet@e8000 {
		phy-handle = <&qsgmii_phy3>;
		phy-connection-type = "qsgmii";
		local-mac-address = [a4 ae 12 78 fb 05];
	};
	// MAC6
	ethernet@ea000 {
		phy-handle = <&qsgmii_phy4>;
		phy-connection-type = "qsgmii";
		local-mac-address = [a4 ae 12 78 fb 06];
	};
	
	// MAC3
	ethernet@e4000 {
		phy-handle = <&rgmii_phy1>;
		phy-connection-type = "rgmii-id";
		local-mac-address = [a4 ae 12 78 fb 03];
	};
	// MAC4
	ethernet@e6000 {
		phy-handle = <&rgmii_phy2>;
		phy-connection-type = "rgmii-id";
		local-mac-address = [a4 ae 12 78 fb 04];
	};
	
	// MAC9
	ethernet@f0000 { /* 10GEC1 */
		status = "okay";
		local-mac-address = [a4 ae 12 78 fb 09];
	};

	mdio@fc000 {
		rgmii_phy1: ethernet-phy@1 {
			reg = <0x1>;
		};

		qsgmii_phy1: ethernet-phy@3 {
			reg = <0x3>;
		};

		qsgmii_phy2: ethernet-phy@4 {
			reg = <0x4>;
		};

		qsgmii_phy3: ethernet-phy@5 {
			reg = <0x5>;
		};
		qsgmii_phy4: ethernet-phy@6 {
			reg = <0x6>;
		};

	};

	mdio@fd000 {

		rgmii_phy2: ethernet-phy@2 {
			reg = <0x2>;
		};
		
	};
};
内核配置

内核配置方面,没找到具体资料,只能贴部分配置了,在默认配置的基础上,进行添加:

CONFIG_ETHERNET=y
CONFIG_NET_VENDOR_FREESCALE=y
CONFIG_FEC=y
CONFIG_FSL_FMAN=y
CONFIG_FSL_XGMAC_MDIO=y

CONFIG_FSL_DPAA_ETH=y
CONFIG_FSL_DPAA2_ETH=y
CONFIG_FSL_DPAA2_MAC=y
CONFIG_FSL_ENETC_MDIO=y


PHY驱动

PHY驱动可以向裕泰官方去要,不过支持不太积极.8531的驱动就说几点注意的地方:

// 自己添加的初始化配置,需要根据手册配置
static int ytphy_config_init(struct phy_device *phydev)
{	
	int  val = 0;
	val = ytphy_read_ext(phydev, 0xa012);
	printk("phy ext_0a12 val : 0x%x\n", val);

	val = 0xd0;
	ytphy_write_ext(phydev, 0xa012, val);
	
	
	ytphy_write_ext(phydev, 0xa00c, 0x2600);
	ytphy_write_ext(phydev, 0xa00d, 0x1870);
	ytphy_write_ext(phydev, 0xa00e, 0x1870);
	
	
	return 0;
}

// 8531 部分
{
        /* same as 8511 */
		.phy_id		= PHY_ID_YT8531,
		.name		= "YT8531 Gigabit Ethernet",
		.phy_id_mask	= MOTORCOMM_PHY_ID_MASK,
#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) )
		.features	= PHY_BASIC_FEATURES | PHY_GBIT_FEATURES,
		.flags			= PHY_HAS_INTERRUPT,
#endif		
		.config_aneg	= genphy_config_aneg,
#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) || ( LINUX_VERSION_CODE > KERNEL_VERSION(5,3,0) )
		.config_init	= ytphy_config_init,
#else
		.config_init	= genphy_config_init,
#endif
		.read_status	= genphy_read_status,
		.suspend	= genphy_suspend,
		.resume		= genphy_resume,
#if (YTPHY_ENABLE_WOL)
		.get_wol		= &ytphy_get_wol,
		.set_wol		= &ytphy_set_wol,
#endif                
	}

软件测试

网络测试能PING通就好了,一般内网没什么问题的,PING外网要注意DNS设置,否则PING不通的,DPAA没有测试。

注意

这部分时间有点久了,细节没有很清楚,如果有问题与疑问,请留言或者邮箱 1004824817@qq.com.

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Call Me Gavyn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值