pcDuino 硬件LED驱动实战

最近调驱动时,调试led时遇到了点问题,于是回过头来再写个led裸板程序。在我写的pcDuino第一个裸板程序uart的基础上,再写个led裸板程序还是很轻松的。很多人觉得没有必要写什么pcDuino裸板程序,觉得没啥意义。我觉得可以用来熟悉硬件,特别是想做底层驱动开发,以及系统移植,熟悉底层硬件还是有用的。其实做底层驱动开发,也是跟硬件打交道,硬件相关的操作和裸板程序是一样的。下面介绍怎样在pcDuino上跑一个最简单的led裸板程序。

开发环境:

宿主机:ubuntu 12.04 64位

目标机:pcDuino V2

编译器:arm-linux-gnueabihf-gcc   (4.6)

目标:实现pcDuino上的TX_LED闪烁

文档说明:

命令提示符 $ 表示在pcDuino上面运行的指令;

命令提示符 # 表示在x86_64的linux主机上运行的指令

命令提示符 > 表示在u-boot状态下运行的指令

仔细看pcDuino上的原理图和pcDuino的手册,发现二者不是完全对应的,还是以原理图为准。根据原理图知道TX_LED是接到PH15上,可以当做普通IO口用,不需要连跳线

主要是看手册30.Port Controller,根据手册写led初始化程序主要包括设为输出、是能上拉及Multi-Driving寄存器设置。包括start.S、main.c、clock.c、clock.h、Makefile,下面贴出全部代码

1

2

3

4

5

6

.
global

_start

_start
:

ldr
sp
,

=
0x00007f00

b

main

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

#include "clock.h"

#define PH_CFG1             (*(volatile unsigned int *)0x01c20900)

#define PH_DAT              (*(volatile unsigned int *)0x01c2090C)

#define PH_DRI              (*(volatile unsigned int *)0x01c20910)

#define PH_PULL             (*(volatile unsigned int *)0x01c20918)

void

gpio_init
(
)

{

/*PCDUINO GPIO4--PH9:

*bit[6:4]:PH9_SELECT 001:OUTPUT

*PCDUINO GPIO5--PH10:

*bit[10:8]:PH10_SELECT 001:OUTPUT

*/

PH_CFG1

|=

(
(
0x1
<<
4
)
|
(
0x1
<<
8
)
|
(
0X1
<<
28
)
)
;

PH_DRI
  
=

0XFFFFFFFF
;

PH_PULL
  
=

0X55555555
;

}

void

delay
(
)

{

volatile
int

i

=

0x300000
;

while

(
i
--
)
;

}

int

main
(
void
)

{

char

c
;

clock_init
(
)
;

/* 初始化时钟 */

gpio_init
(
)
;

while

(
1
)

{

PH_DAT

=

0x00
;

delay
(
)
;

PH_DAT

=

0xffff
;

delay
(
)
;

}

return

0
;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

#define CPU_AHB_APB0_CFG    (*(volatile unsigned int *)0x01c20054)

#define PLL1_CFG            (*(volatile unsigned int *)0x01c20000)

#define APB1_CLK_DIV_CFG    (*(volatile unsigned int *)0x01c20058)

#define APB1_GATE           (*(volatile unsigned int *)0x01c2006C)

void

sdelay
(
unsigned

long

loops
)

{

__asm__
volatile
(
"1:\n"

"subs %0, %1, #1\n"

"bne 1b"
:
"=r"

(
loops
)
:
"0"
(
loops
)
)
;

}

void

clock_init
(
void
)

{

/*AXI_DIV_1[1:0]  AXI_CLK_DIV_RATIO 00:/1 AXI Clock source is CPU clock

*AHB_DIV_2[5:4]  AHP_CLK_DIV_RATIO 01:/2 AHB Clock source is AXI CLOCK

*APB0_DIV_1[9:8] APB0_CLK_RATIO    00:/2 APB0 clock source is AHB2 clock

*CPU_CLK_SRC_OSC24M[17:16] CPU_CLK_SRC_SEL 01:OSC24M

*/

CPU_AHB_APB0_CFG

=

(
(
0
<<
0
)
|
(
0x1
<<
4
)
|
(
0
<<
8
)
|
(
1
<<
16
)
)
;

/*bit31:PLL1_Enable 1:Enable

*bit25:EXG_MODE 0x0:Exchange mode

*bit[17:16]:PLL1_OUT_EXT_DIVP 0x0:P=1

*bit[12:8]:PLL1_FACTOR_N 0x10:Factor=16,N=16

*bit[5:4]:PLL1_FACTOR_K 0x0:K=1

*bit3:SIG_DELT_PAT_IN 0x0

*bit2:SIG_DELT_PAT_EN 0x0

*bit[1:0]PLL1_FACTOR_M 0x0:M=1

*The PLL1 output=(24M*N*K)/(M*P)=(24M*16*1)/(1*1)=384M is for the coreclk

*/

PLL1_CFG

=

0xa1005000
;

sdelay
(
200
)
;

CPU_AHB_APB0_CFG

=

(
(
0
<<
0
)
|
(
0x1
<<
4
)
|
(
0
<<
8
)
|
(
2
<<
16
)
)
;
//CPU_CLK_SRC_SEL 10:PLL1

/*uart clock source is apb1,config apb1 clock*/

/*bit[25:24]:APB1_CLK_SRC_SEL 00:OSC24M

*bit[17:16]:CLK_RAT_N 0X0:1 The select clock source is pre-divided by 2^1

*bit[4:0]:CLK_RAT_M 0x0:1 The pre-devided clock is divided by(m+1)

*/

APB1_CLK_DIV_CFG

=

(
(
0
<<
5
)
|
(
0
<<
16
)
|
(
0
<<
24
)
)
;

/*open the clock for uart0*/

/*bit16:UART0_APB_GATING 1:pass 0:mask*/

APB1_GATE

=

(
0x1
<<
16
)
;

}

1

void

clock_init
(
void
)
;

1

2

3

4

5

6

7

8

9

led
.
bin
:
start
.
S

main
.
c

clock
.
c

arm
-
linux
-
gnueabihf
-
gcc

-
nostdlib

-
c

start
.
S

-
o

start
.
o

arm
-
linux
-
gnueabihf
-
gcc

-
nostdlib

-
c

main
.
c

-
o

main
.
o

arm
-
linux
-
gnueabihf
-
gcc

-
nostdlib

-
c

clock
.
c

-
o

clock
.
o

arm
-
linux
-
gnueabihf
-
ld

-
Ttext

0xD0020010

start
.
o

main
.
o

clock
.
o
  
-
o

led_elf

arm
-
linux
-
gnueabihf
-
objcopy

-
O

binary

-
S

led_elf
led
.
bin

clean
:

rm

-
rf *
.
o *
.
bin
led_elf *
.
dis

1.编译

change@change :~$ cd Si/A10/2_led/
change@change :~/Si/A10/2_led$ ls
clock.c  clock.h  main.c  Makefile  mksunxiboot  start.S
change@change :~/Si/A10/2_led$ make
arm-none-linux-gnueabi-gcc -nostdlib -c start.S -o start.o
arm-none-linux-gnueabi-gcc -nostdlib -c main.c -o main.o
arm-none-linux-gnueabi-gcc -nostdlib -c clock.c -o clock.o
arm-none-linux-gnueabi-ld -Ttext 0xD0020010 start.o main.o clock.o  -o led_elf
arm-none-linux-gnueabi-objcopy -O binary -S led_elf led.bin
change@change :~/Si/A10/2_led$ ./mksunxiboot led.bin leds.bin
File size: 0x154
Load size: 0x154
Read 0x154 bytes
Write 0x200 bytes
change@change :~/Si/A10/2_led$
其中有个./mksunxiboot led.bin leds.bin要注意,不经过mksunxiboot工具 的.bin文件,pcDuino是运行不了的。这个工具在官网上都有下。现在的处理启动都很复杂,内有固化有bl0代码,在跳转到bl1时需要校验程序的合法性,这个工具mksunxiboot简单点少就是给我们程序加了点头部,让处理器能够识别我们写的代码。你可以分析led.bin和leds.bin的反汇编代码,就一目了然了。这部分感兴趣的可以一起讨论。

2.测试

上面生成的leds.bin就可以放到板子上运行了。为了不破会NAND中的系统,直接放到tf卡运行。不用担心那个先启动,看全志手册就知道pcDuino默认先从tf卡启动,只有tf卡没有启动的引导程序才会跳到NAND启动。插上tf卡到PC机

[email protected]:~/Si/A10/2_led$ sudo dd if=/dev/zero of=/dev/sdb bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB) copied, 0.425886 s, 2.5 MB/s
[email protected]:~/Si/A10/2_led$ sudo dd if=leds.bin of=/dev/sdb bs=1024 seek=8
0+1 records in
0+1 records out
512 bytes (512 B) copied, 0.00600667 s, 85.2 kB/s
[email protected]:~/Si/A10/2_led$

然后取下tf卡,插到pcDino上,RX LED就开始闪烁了。如果你手上有led,接到GPIO4、GPIO5也会闪烁。

时间: 2024-08-13 12:03:15

pcDuino 硬件LED驱动实战的相关文章

linux驱动(九)platform驱动模型详解,以及基于platform驱动模型的led驱动

参考: http://blog.csdn.net/qq_28992301/article/details/52385518 http://blog.csdn.net/zoe6553/article/details/6372445 http://blog.chinaunix.net/uid-25014876-id-111745.html 1:什么是platform总线?platform总线是区别于实体总线USB. I2C.SPI .PIC总线的虚拟总线,一些usb设备选址的话需要通过USB总线来进

第7章:LED驱动的实现原理

本章完成了一个真正意义上的 Linux 驱动.该 Linux 驱动用来控 制开发版上的 4个 LED 小灯.也就是说通过向 Linux 驱动发送数据可以控制 LED 小灯的开关.为 了方便称呼这个驱动,本书及后面的章节都将其称为 LED 驱动. 虽然 LED 驱动并不复杂,只是控制 了 4个 LED,"但 LED 驱动已经包括了 Linux 驱动所有必要的部分 一个完整的 Linux 驱动主要由 内部处理和与硬件交互降部分组成.其中内部处理主要是指 Linux 驱动的装载.卸载.与设备文件 相关

编写LED驱动

1.创建LED驱动的设备文件: (1)使用cdev-init函数初始化cdev (2)指定设备号 设备号的分配有两种指定方法:直接在代码中指定(硬编码) 动态分配 (3)使用cdev-add函数将字符设备添加到内核中的字符设备数组中 (4)使用class-creat宏创建struct class (5)使用device-create函数创建设备文件 2.LED驱动通过两种方式控制LED 通过字符串控制LED 通过I/O命令LED 3.一个完整的linux驱动主要由内部处理和与硬件交互两部分组成.

4、CC2541芯片中级教程-OSAL操作系统(简单AT指令实现+IIC软件和硬件实现驱动MPU6050)

本文根据一周CC2541笔记汇总得来—— 适合概览和知识快速索引—— 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 中级教程-OSAL操作系统(OSAL系统解基本套路) 中级教程-OSAL操作系统(进一步了解-OLED && 普通按键和5方向按键-中断!!!)这个系统驱动层和应用层不一样~ 中级教程-OSAL操作系统(ADC-光敏电阻) OSAL操作系统-实验16 串口波特率扩展 OSAL操作系统-实验1

FL2440驱动添加(4)LED 驱动添加

硬件信息:FL2440板子,s3c2440CPU带四个LED,分别在链接GPB5,GPB6,GPB8,GPB10 内核版本:linux-3.8.0 led驱动代码如下: 值得注意地方地方: 1,定时器的使用:在include/linux/timer.h下定义struct timer_list struct timer_list { /* * All fields that change during normal runtime grouped to the * same cacheline *

驱动学习之LED驱动框架

一:什么是驱动框架  (1)内核中驱动部分维护者针对每个种类的驱动设计一套成熟的.标准的.典型的驱动实现,然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架.  (2)内核维护者在内核中设计了一些统一管控系统资源的体系,这些体系让内核能够对资源在各个驱动之间的使用统一协调和分配,保证整个内核的稳定健康运行.譬如系统中所有的GPIO就属于系统资源,每个驱动模块如果要使用某个GPIO就要先调用特殊的接口先申请,申请到后使用,使用

嵌入式Linux学习笔记之LED驱动

最近在学习嵌入式Linux驱动开发,大致了解了驱动的基本开发流程,本文主要针对字符设备驱动开发做一个简要介绍,也当作是对这几天工作的一个小小总结. 计算机系统是由软硬件相互协调共同完成工作的,作为专用计算机系统的嵌入式系统也不例外,既要有CPU.SDRAM.FLASH.IO等硬件,同时也少不了操作系统和应用软件等软件的支持,而作为应用程序与硬件的桥梁--驱动程序,是整个嵌入式系统开发过程中的关键环节.驱动开发涉及底层,而了解底层作用机制对于整个系统的开发意义重大. Linux内核中有60%以上是

Android系统移植与驱动开发——第七章——LED驱动

LED驱动的实现原理 编写LED驱动: 测试LED驱动之前需要用USB数据线连接开发板,然后打开电源,成功启动之后,执行build.sh脚本文件编译和安装LED驱动,顺利则会自动连接 如果有多个设备文件将会在后来使用,则要通过指针变量cdev.list.prev和cdev.list.next指针变量连接,从而形成双向链接. 在创建设备文件时要注意: Device_count表示建立设备文件的个数 Alloc_chrdv_region函数的第2个参数表示分配的起始设备号. 卸载Linux驱动的设备

Mini2440 led驱动

Mini2440 led驱动 Mini2440_leds.c 代码: #include <linux/miscdevice.h> #include <linux/delay.h>  //延时函数的头文件 #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> //硬件相关的函数 #include <linux/kernel.h>