fiddlesticks 发表于 2013-5-26 23:10:38

新手编写的pwm驱动

本帖最后由 matson 于 2013-5-27 21:42 编辑

   为了学习arm下的linux,入手了一块cubieboard,编写了一个pwm小驱动来练手。
   由于新内核添加了pwm子系统,所以我练手用的kernel版本使用了当天最新的3.9.2(我是5月15日从kernel.org上下载的)u-boot使用cubieboard提供的,为了简化调试过程,我的系统仅仅在ramfs中运行,不挂载ubuntu的文件系统。
      第一步:修改u-boot使其支持fdt(我的驱动初始化是基于fdt做的)。
      1:在include/configs/sun4i.h档案中添加:
            #define CONFIG_OF_LIBFDT
            #define CONFIG_OF_CONTROL
            #define CONFIG_OF_SEPARATE
       2:在arch/arm/lib/bootm.c文件中,在if(images->ft_len)的判断之前加上
             images->ft_addr = gd->fdt_blob;
             images->ft_len = 2 *1024 *1024;
然后使用
make -j8 cubieboard CROSS_COMPILE=arm-none-linux-gnueabi-
指令编译(我的環境變量中已經是有arm-none-linux工具鏈的,假如你的沒有還需要
export PATH=$PATH:XXXX
XXXX表示你工具鏈的路徑
我的电脑是I7   8 线程的CPU所以可以使用-j8)
編譯的最後會報錯
make:正在离开目录 `/home/workdir/u-boot-sunxi/u-boot-sunxi/spl'
make: 正在进入目录 `/home/workdir/u-boot-sunxi/u-boot-sunxi/dts'
Makefile:30: *** Please define CONFIG_DEFAULT_DEVICE_TREE in your board header file。 停止。
make:正在离开目录 `/home/workdir/u-boot-sunxi/u-boot-sunxi/dts'
make: *** 错误 2
make: *** 正在等待未完成的任务....
這個是因為u-boot中並沒有cubieboard的dts文件,不過這個不重要,只要
spl/sunxi-spl.bin 和u-boot.bin生成了就行了,dtb文件我們使用編譯內核生成的那個就行了。

      第二步:修改linux3.9.2的源码使其能编译cubieboard并添加pwm驱动
下面使用的是git diff的输出结果来显示修改
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1cacda4..78a300f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1014,7 +1014,7 @@ config ARCH_MULTI_V7
      bool "ARMv7 based platforms (Cortex-A, PJ4, Scorpion, Krait)"
      default y
      select ARCH_MULTI_V6_V7
-       select ARCH_VEXPRESS
+#      select ARCH_VEXPRESS
      select CPU_V7
config ARCH_MULTI_V6_V7
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ee4605f..b07377a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -227,7 +227,7 @@ else
MACHINE:=
endif

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0e0bfa0..6585db6 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -203,4 +203,9 @@ config PWM_VT8500
          To compile this driver as a module, choose M here: the module
          will be called pwm-vt8500.
+config PWM_SUNXI
+       tristate "sunxi PWM support"
+       depends on ARCH_SUNXI
+       help
+         Generic PWM framework driver for sunxi.
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 94ba21e..852cbf6 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_PWM_TIPWMSS)   += pwm-tipwmss.o
obj-$(CONFIG_PWM_TWL)          += pwm-twl.o
obj-$(CONFIG_PWM_TWL_LED)      += pwm-twl-led.o
obj-$(CONFIG_PWM_VT8500)       += pwm-vt8500.o
+obj-$(CONFIG_PWM_SUNXI)                += pwm-sunxi.o
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 8709a39..44ca175 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -7,4 +7,5 @@ config ARCH_SUNXI
      select PINCTRL
      select SPARSE_IRQ
      select SUNXI_TIMER
-       select PINCTRL_SUNXI
\ No newline at end of file
+       select PINCTRL_SUNXI
+       select PWM_SUNXI
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index f99f60d..d083f7b 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -44,6 +44,20 @@
                              allwinner,drive = <0>;
                              allwinner,pull = <0>;
                        };
+
+                     gpio_pwm0: pwm0@0 {
+                               allwinner,pins = "PB2";
+                               allwinner,function = "pwm0";
+                               allwinner,drive = <4>;
+                               allwinner,pull = <0>;
+                     };
+
+                     gpio_pwm1: pwm1@0 {
+                               allwinner,pins = "PI3";
+                               allwinner,function = "pwm1";
+                               allwinner,drive = <4>;
+                               allwinner,pull = <0>;
+                     };
                };
      };
};
diff --git a/arch/arm/boot/dts/sunxi.dtsi b/arch/arm/boot/dts/sunxi.dtsi
index 8b36abe..ca1fd73 100644
--- a/arch/arm/boot/dts/sunxi.dtsi
+++ b/arch/arm/boot/dts/sunxi.dtsi
@@ -78,5 +78,17 @@
                        clock-frequency = <24000000>;
                        status = "disabled";
                };
+
+               pwm0: pwm@0x01c20e04 {
+                     compatible = "allwinner,sunxi-pwm";
+                     reg = <0x01c20e04 0x4>;
+                     id = <0>;
+               };
+
+               pwm1: pwm@0x01c20e08 {
+                     compatible = "allwinner,sunxi-pwm";
+                     reg = <0x01c20e08 0x4>;
+                     id = <1>;
+               };
      };
};
diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
index 5cab825..b84e180 100644
--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
@@ -34,5 +34,16 @@
                uart1: uart@01c28400 {
                        status = "okay";
                };
+
+               pwm0: pwm@0x01c20e04 {
+                     pinctrl-names = "default";
+                     pinctrl-0 = <&gpio_pwm0>;
+                     status = "okay";
+               };
+               pwm1: pwm@0x01c20e08 {
+                     pinctrl-names = "default";
+                     pinctrl-0 = <&gpio_pwm1>;
+                     status = "okay";
+               };
      };
};
ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y)
-MACHINE:=
+MACHINE:= arch/arm/mach-sunxi
endif
machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))

添加檔案:
.config(基於arch/arm/configs/multi_v7_defconfig 修改)
build.sh(根據cubieborad sdk修改的簡易編譯腳本)
cubieboard.cpio.gz(ramfs壓縮包)
drivers/pwm/pwm-sunxi.c

./build.sh

在linux-3.9.2/output目錄中
我們可以看到我們需要的uImage 和sun4i-a10-cubieboard.dtb
cat u-boot.bin sun4i-a10-cubieboard.dtb > image.bin
使用上面的命令生成的鏡像,再燒寫時幾率導致dtb文件的尾部被截斷,
導致跑到start_kernel->setup_arch->dump_machine_table處掛掉
PS:我使用的是一個windows下的一個裸寫SD卡的小工具,並不是用
dd命令,那個工具是以sector問單位寫的(1 sector =512 Bytes)。
所以在文件尾部湊一個全為0的小文件
(dd if=/dev/zero of=pad bs=1024 count=1
cat u-boot.bin sun4i-a10-cubieboard.dtb pad> image.bin

製作啟動卡:
具體步驟可參照http://just4fun.cn/?p=643
這位朋友寫得非常好
大致步驟就是
重新格式化SD卡,
分2個區,第一個64M的fat32格式
第二個ext2/ext4格式(我這裡暫時不會用到)
linux下燒寫
在u-boot的目錄下(/dev/sda為讀卡器的設備名)
ddif=spl/sunxi-spl.binof=/dev/sda   bs=1024    seek=8
ddif=image.binof=/dev/sda   bs=1024    seek=32
然後將uImage拷貝到卡的第一個fat32格式的分區中,
將卡插入cubieboard的卡槽,上電啟動。
啟動並簡單的使用pwm模塊
在編譯時在drivers/pwm/pwm-sunxi.c文件的
#ifdef AW_PWM_DEBUG_SYSFS
上添加:#define AW_PWM_DEBUG_SYSFS
這樣我寫的驅動會在/sys/kernel目錄下創建sunxi_pwm目錄供調試pwm的功能使用
(這段代碼也可以作為其他驅動調用sunxi pwm驅動的示例)
在開啟調式功能後,進入系統後
echo "channel_numerduty_ns period_ns" > /sys/kernel/sunxi_pwm/start 啟動pwm
echo "channel_numer" > /sys/kernel/sunxi_pwm/stop 關閉pwm
示例:
echo "020 30" > /sys/kernel/sunxi_pwm/start (啟動pwm0 設置duty_ns = 20 nsperiod_ns = 30 ns)
這時pb2管腳會輸出占空比2/3的波形(pwm0是PB2管腳,pwm1為PI3管腳)
echo "0" > /sys/kernel/sunxi_pwm/stop(關閉pwm0)
這裡注明2點,第一我對輸入值的匹配分2個情況,
第一個是完全匹配,就是對寄存器的設置值完全和預期值一樣,
第二個是,在硬件無法設置剛好的duty_ns ,period_ns 值時,改為設置其他的值,
輸出的波形的占空比為duty_ns /period_ns的波形,
假如2個情況都滿足不了,就返回失敗。
附件中有我修改和添加的文件,以及在示波器上测量出的输出波形


fiddlesticks 发表于 2013-5-26 23:14:58

請問下版主怎麼添加附件?

fiddlesticks 发表于 2013-5-26 23:16:25

對不起大家了,圖片太大發不了,只能把修改的文件發上來了

matson 发表于 2013-5-27 21:44:24

good!好文章~呵呵

tll 发表于 2013-6-2 18:17:43

支持,传github上吧

gootoomoon 发表于 2013-6-28 09:55:50

请问 文中提到的pwm-sunxi.c 是你自己写的还是 官方有相关的源码?我找了半天没找到,能不能帮忙提供一个 gootoomoon@163.com,官方的源码最高好像只到3.4: https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.4/drivers里面貌似木有/drivers/pwm的文件夹

gootoomoon 发表于 2013-6-28 09:57:26

fiddlesticks 发表于 2013-5-26 23:16 static/image/common/back.gif
對不起大家了,圖片太大發不了,只能把修改的文件發上來了

原来有附件,我也试试看,谢谢楼主

寒寒 发表于 2013-7-8 11:51:18

过来学习{:soso_e113:}

aaron 发表于 2013-7-9 08:38:27

试试,求突破 。

cubieplayer 发表于 2013-7-10 17:59:33

厉害,能拍好版就更好了
页: [1]
查看完整版本: 新手编写的pwm驱动