0。前言一般我们在购买开发板时,厂家都会提供相应的电路图文件,我们通过搜索相应的名称就可以找到相应的外设。对于驱动工程师来说,我们只需要知道外设与SOC交互的一些数据线和信号线即可。用主控芯片控制这些外设的一般步骤:看电路原理图,搞清楚主控芯片和外设是怎么连接的。对于驱动工程师来说,主要是看一些clk,data管脚,control管脚是如何连接的;外设一般接在SOC的一个或多个控制器上,如i2c、spi、gpio等,有的是数据线,有的是信号线、中断线等;主控芯片设置,外设设置往往通过寄存器操作实现;编写相应的代码来实现功能,不同类型的外设,代码结构是不一样的,比如按键,我们可以通过轮询的方式来读取按键信息,也可以通过中断来读取。下面以华清远景的fs4412开发板为例,看看如何编写LED裸机程序。SOCexynos4412datahseet下载地址:https://download.csdn.net/download/daocaokafei/125334381.LED灯电路图先看led电路图:LED电路图板上有4个LED,是发光的二极管,电流为蓝色;LED接上拉电阻;三极管的基极连接到SOC的GPIO引脚;例如GPX1_0,当该引脚为高电平时,三极管的pn结导通,所以有电位差达到时,LED3点亮。若该脚为低电平,pn结截止,LED3两边无电位差,LED3熄灭。下面是CPU核心访问GPIO控制器的数据路径:AHB:高速总线APBBridge:APB总线桥APB:外设总线,低速总线GPIO挂载在APB总线上GPIO和SOC一样可以从上图可以看出,CPU访问GPIO寄存器需要经过的路径。2.GPIOGPIO(GeneralPurposeI/OPorts)意为通用输入/输出端口。通俗地说,就是一些可以输出高电平和低电平的管脚,或者可以通过它们读取管脚的状态——是高电平还是低电平。用户可以通过GPIO口(如UART)与硬件进行交互,控制硬件工作(如LED、蜂鸣器等),读取硬件工作状态信号(如中断信号)等,GPIO口应用广泛。1.GPIO的优点低功耗:GPIO具有较低的功耗(约1μA,μC的工作电流为100μA)。集成I2C从接口:GPIO内置I2C从接口,即使在待机模式下也能全速工作。小封装:GPIO设备提供最小的封装尺寸——3mmx3mmQFN!低成本:您无需为不使用的功能付费!快速上市:无需额外代码、无需文档、无需维护!灵活的光控:内置多个高分辨率PWM输出。可预定响应时间:缩短或确定外部事件和中断之间的响应时间。更好的照明效果:匹配的电流输出确保均匀的显示亮度。接线简单:只需要??2条I2C总线或3条SPI总线。2.exynos4412GPIO特性172个外部中断32个外部唤醒中断252个多功能输入/输出端口睡眠模式下也可以控制GPIO引脚,但不包括GPX0、GPX1、GPX2和GPX33。6通用输入/输出(GPIO)ControlExynos4412SCP包括304个多功能输入/输出端口引脚和164个内存端口引脚。一共有37个端口组和两个内存端口组。下图是GPIO模块图:GPIOBlockDiagram3.如何操作GPIO?主要是通过寄存器操作GPIO管脚。GPxCON用于选择引脚功能,GPxDAT用于读/写引脚数据;另外,GPxUP用于判断是否使用内部上拉电阻。其中x为A、B.....H、J等。1.GPxCON寄存器从寄存器的名字可以看出,它是用来配置(Configure)——选择引脚功能的。LED3接GPX1_0,引脚说明如下:GPX1CON如上图所示,GPX1CON地址为0x1100C20;LED3是输出设备,所以需要设置GPX1CON[3:0]为0x1,但可以修改其他位。2.GPxDAT寄存器GPxDAT用于读/写管脚;当引脚设置为输入时,读取该寄存器可知对应引脚的电平状态是高电平还是低电平;当该引脚设置为输出时,写入该寄存器的相应位即可使该引脚输出高电平或低电平。GPX1DATGPX1DAT地址为0x1100C24LED3对应的输出引脚为GPX1DAT[0]。只需要将此引脚设置为1即可开灯,设置bite0为0则关灯。3.GPxUP寄存器GPxUP:当某位为1时,对应的引脚没有内部上拉电阻;为0时,对应管脚使用内部上拉电阻。上拉电阻的作用是:当GPIO引脚处于第三状态(即不输出高电平也不输出低电平,而是处于高阻态时,即相当于未连接芯片),其电平状态由上拉和下拉电阻决定。本例无需设置。4.驱动程序编写接下来,我们使用汇编语言和C语言编写LED的驱动程序。1、汇编代码如果你已经掌握了我之前讲解的汇编指令的知识点,那么这段代码就很容易理解了:.globl_start.arm_start:LDRR0,=0x11000C20@将配置寄存器GPX1CON的地址写入R0LDRR1,[R0]@读取寄存器GPX1CON的值并保存到R1BICR1,R1,#0x0000000f@将R1的3:0位清0,目的是不覆盖其他位ORRR1,R1,#0x00000001@设置3:0ofR1Position1STRR1,[R0]@将R1的值写回寄存器GPX1CONloop:LDRR0,=0x11000C24@将数据寄存器GPX1DAT的地址写入R0LDRR1,[R0]@读取寄存器GPX1DAT的值并保存ittoR1ORRR1,R1,#0x01@将R1bite0的值设置为1,即拉高,打开STRR1,[R0]@将R1的值写回寄存器GPX1DATBLdelay@调用延时函数LDRR1,[R0]BICR1,R1,#0x01@写入R1的值设置bite0为0,即下拉,关灯STRR1,[R0]BLdelayBloopdelay:@delay延时函数LDRR2,=0xffffffffloop1:SUBR2,R2,#0x1CMPR2,#0x0BNEloop1MOVPC,LR@return.endMakefileTARGET=gcdall:arm-none-linux-gnueabi-gcc-O0-g-c-o$(TARGET).o$(TARGET).sarm-none-linux-gnueabi-ld$(TARGET).o-Ttext0x40008000-N-o$(TARGET).elfarm-none-linux-gnueabi-objcopy-Obinary-S$(TARGET).elf$(TARGET).binclean:rm-rf*.o*.elf*.dis*.bin程序的功能很简单,就是让LED3显示一个闪烁的效果。执行make,最后生成gcd.bin文件。2、C语言实现如果要进入C语言执行环境,就必须设置栈空间,函数调用参数和返回值都会被压入栈中。开始。){GPX1.CON=GPX1.CON&(~(0x0000000f))|0x00000001;}voidled_on(intn){GPX1.DAT=GPX1.DAT|0x01;}voidled_off(){GPX1.DAT=GPX1.DAT&(~(0x01)));}voiddelay_ms(unsignedintnum){inti,j;for(i=num;i>0;i--)for(j=1000;j>0;j--);}intmain(void){led_init();while(1){led_on();delay_ms(500);led_off();delay_ms(500);}while(1);return0;}map.ldsOUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")OUTPUT_ARCH(arm)ENTRY(_start)SECTIONS{.=0x40008000;;从这个地址开始.=ALIGN(4);.text:;指定代码段{gcd.o(.text);code第一部分一定不能错*(.text)}.=ALIGN(4);.rodata:;只读数据段{*(.rodata)}.=ALIGN(4);.data:;读写数据部分{*(.data)}.=ALIGN(4);.bss:{*(.bss)}}MakefileTARGET=gcdTARGETC=mainall:arm-none-eabi-gcc-O0-g-c-o$(TARGETC).o$(目标C).carm-none-eabi-gcc-O0-g-c-o$(TARGET).o$(TARGET).sarm-none-eabi-gcc-O0-g-S-o$(TARGETC).s$(TARGETC).carm-none-eabi-ld$(目标).o$(TARGET).o-Tmap.lds-o$(TARGET).elfarm-none-eabi-objcopy-Obinary-S$(TARGET).elf$(TARGET).binclean:rm-rf*.o*.elf*.dis*.bin执行make命令,最终生成的gcd.bin文件在这段代码中,读者可能不理解下面的定义:typedefstruct{unsignedintCON;unsignedintDAT;unsignedintPUD;unsignedintDRV;}gpx1;#defineGPX1(*(volatilegpx1*)0x11000C20)GPX1宏定义如上图所示:(volatilegpx1*)0x11000C20):将常量0x11000C20转换为structgpx1类型指针(*(volatilegpx1*)0x11000C20):找到对应的内存到指针Driver,它对应整个结构体变量,结构体变量的地址是0x11000C20#defineGPX1(*(volatilegpx1*)0x11000C20):GPX1相当于地址为0x11000C20的结构体变量这样如果我们想操作G的寄存器PX1,我们可以像结构体变量一样操作。3、测试使用UBOOT自带的loadb命令,通过串口以波特率下载二进制(.bin)到SDRAM中的某个地址,然后使用go命令从某个地址开始执行程序。该命令使用kermit协议,嵌入式系统通常使用该协议与pc进行文件传输。操作步骤如下:将串口连接到开发板。开发板启动后,倒计时阶段立即回车,进入uboot命令界面,执行loadb40008000【地址与Makefile和map.lds文件中的地址一致】选择菜单transfer->sendKermit,然后选择我们编译的gcd.bin文件,点击确定,出现“Staringkermittransfer”字样。出现,执行go40008000,运行程序,运行裸机程序。执行结果:可以看到LED闪烁现象。5.注意这个测试方法需要bootloader选择uboot,串口工具需要支持Kermit协议。一口君使用的版本是SecureCRT7.3.3【其他低版本可能不支持该协议】,软件下载安装方法【安装方法有点繁琐】可以后台回复【SecureCRT】。SecureCRT版本
