当前位置:  编程技术>移动开发
本页文章导读:
    ▪四、mini2440裸机程序之MMU(上)        四、mini2440裸机程序之MMU(下)1.本实验相关管脚介绍        1)原理图           2)由上图可以看出,4个LED灯所对应的管脚: 名称 对应管脚 管脚功能 对应逻辑 LED1 GPB5 内.........
    ▪ MQX3.8源代码分析:GPIO(四)驱动安装函数 _io_gpio_install()        MQX3.8源代码分析:GPIO(4)驱动安装函数 _io_gpio_install()        关于GPIO的内部基准时钟已经打开,下一步就是给GPIO安装操作函数了,只有安装了操作函数,才能够利用的标准的接口访问GPIO. .........
    ▪ 谈谈过去1个月修改过的BUG-3G模块有关问题(1)       谈谈过去1个月修改过的BUG--3G模块问题(1)    步入11月份了,自从上个月答应老大接手手机项目的3G模块至今,已经有一个月了吧。这一个月来,一直围绕着3G模块的那几个BUG在查原因,在.........

[1]四、mini2440裸机程序之MMU(上)
    来源: 互联网  发布时间: 2014-02-18
四、mini2440裸机程序之MMU(下)

1.本实验相关管脚介绍

       1)原理图

 

        2)由上图可以看出,4个LED灯所对应的管脚:

名称

对应管脚

管脚功能

对应逻辑

LED1

GPB5

内部上拉输出

0:灯亮

1:灯灭

LED2

GPB6

内部上拉输出

0:灯亮

1:灯灭

LED3

GPB7

内部上拉输出

0:灯亮

1:灯灭

LED4

GPB8

内部上拉输出

0:灯亮

1:灯灭

 

2.相关的寄存器:


 

          1)与LED相关:

注释:

①     GPBCON   :   用于配置端口B的各个管脚功能

②     GPBDAT    :   端口B的数据寄存器

③     GPBUP      :   用于配置端口B是否使用上拉功能

 

 

         2)与MMU相关

 

                  ①   CP15 register 1(control register)

相关bit(M,A,S,R,RR,V(可有可无))


注:

这个放在最后连着开启MMU时一次性设置就好了,需要设置的有:使能Icache,Dcache;使能数据地址对齐异常检查;关于覆写算法默认是ramdom,我们选择round-robin;关于异常向量表的起始位置,我们选择放在最低地址0x0就可以了(这个实验我们还不会用到向量表,所以也可以先忽略掉);endianness选择小端就行了,默认也是小端;开启MMU;

         关于R,S这两个值,要与AP关联上,由下图可以看出,当AP=00时,我们这里选择S:R=10:这样在AP=00时,SP为RO,UP为NA;AP=01时,SP为R/W,UP为NA;AP=10,SP为R/W,UP为RO;AP=11,SP为W/R,UP为R/W.

         综上所属,使用配置命令如下:

MRC p15 , 0 , R1 , c1 , c0 , 0  (R1 [29:0]= 000000000000000 1 0 1 00 0 1 0 1111 1 1 1B)

 

 

                  ②CP15 register 2(Translation table base register(TTB))


注:

在启动MMU之前我们需要设置好TTB,使用配置命令如下:

MCR p15 , 0 , R1 , c2 , c0 , 0  (R1=页表基址)

 

                ③CP15 register 3(Domainaccess control register)


以下为在domain access control寄存器里的access control bits的取值及说明:


这里我们根据顺序来设置:D0=00(No access)D1=01(Client)D2=11(Manager)其它都设置为10(reserved),使用命令配置如下:

MCR p15 , 0 , R1 , c3 , c0 , 0           (R1 = 10101010101010101010101010110100B)

 

 

                   ④CP15 register 5(fault status register)

 

    

              ⑤CP15 register 7(cacheoperations register)(write-only)

               这个寄存器用于管理ICache 和 DCache. 提供功能如下:




格式如下:


注:

开启MMU之前我们需要InvalidateCache,并且清除write buffer,所以使用命令配置如下:

MCR p15 , 0 , R1 , c7 , c7 , 0            (R1=SBZ)

MCR p15 , 0 , R1 , c7 , c10 , 4          (R1=SBZ)

     

                 ⑥CP15 register 8(TLB operation register )


可以使无效整个TBL(s),….其中还可以无效单独有一个记录项(通过使用MVA),MVA被提供在Rd里.当需要用上MVA时,register 10 的MVA格式如下:


注:

在开启MMU之前我们需要进行一次InvalidateTLBs。这样在开启MMU之后就可以自动重新设置TLB的内容了。所以使用命令配置如下:

MCR p15 , 0 , Rd , c8 , c7 , 0         (Rd=SBZ)

 

                ⑦CP15 register 10(TLB lockdown register)


格式如下:


复位时为0x0

 

3.程序流程图设计:

 

        1)主程序流程图:

 

        2)建立页表子程序流程图:

 

注:这里需要注意的是:要把物理地址为0~代码的结束地址,不然使用开启MMU指令后并不正确执行后续的指令,我个人的理解是:在开启MMU之后,之后PC机上所看到的指令地址会变成VA,这时如果不进行映射到相同的PA地址,那经过MMU之后就可能映射到不可预测的物理地址了或者出现异常。


        3)拷贝测试代码子程序流程图:

 

        4)初始化MMU子程序流程图:


        

 

 

4.程序设计:

①Makefile

demo_mmu.bin : demo_mmu.o led_display.o
    arm-linux-ld -Tmmu.lds -o demo_mmu_elf demo_mmu.o led_display.o
    arm-linux-objcopy -O binary -S demo_mmu_elf demo_mmu.bin
    arm-linux-objdump -D -m arm demo_mmu_elf > demo_mmu.dis

%.o : %.c
    arm-linux-gcc -Wall  -O2 -c -o $@ $<

%.o : %.S
    arm-linux-gcc -Wall -O2 -c -o $@ $<

clean :
    rm -f demo_mmu.bin demo_mmu_elf demo_mmu.dis *.o



②mmu.lds

SECTIONS {
    first    0x00000000 : {demo_mmu.o}
    second    0xB0005000 : AT(2048) {led_display.o}
}



③demo_mmu.S


#define    WTCON        0x53000000

#define    BWSCON        0x48000000
#define    BANKCON6    0x4800001C
#define    REFRESH        0x48000024
#define    BANKSIZE    0x48000028
#define    MRSRB6        0x4800002C

#define    MMU_TTB        0x30000000
#define    MMU_FPTB    (0x30004000)
#define    NCNB_CTTBTT    (0x00<<2)
#define    WT_CTTBTT    (0x03<<2)
#define MANAGER_DOMAIN  (0x02<<5)
#define    SPUP_WR_SECTIONAP    (0x03<<10)
#define    SECTION_FLAG    (0x01<<4)|(0x02)
#define    FINE_FLAG    (0x13)
#define    SPUP_WR_TINYAP    (0x03<<4)
#define    TINY_FLAG    (0x03)    


.Text
.global    _start

_start:
    /******关闭看门狗**********/
    ldr    r0 , =WTCON
    mov    r1 , #0x0
    str    r1 , [r0]
    /*****END关闭看门狗********/


    
    /***调用初始化内存子程序***/
    bl    memory_init
    /**********END*************/

    /*****调用建立页表子程序***/
    bl    build_page
    /***********END************/



    /*****调用初始化MMU子程序**/
    bl    mmu_init
    /***********END************/

    @启动MMU
    mcr    p15 , 0 , r1 , c1 , c0 , 0         @设置control register.启动MMU

/**********让灯全亮************

    @把LED1-4管脚置为输出
    ldr    r0 , =GPBCON
    ldr    r1 , [r0]        @把GPBCON里的内容加载到r1里
    ldr    r2 , =(0xFF<<10)
    bic    r1 , r1 ,r2    @操作数取反码或上r1,用于清零工作
    ldr    r2 , =(0x55<<10)
    orr    r1 , r1 , r2
    str    r1 , [r0]    
    @灯全亮
    ldr    r0 , =GPBDAT
    ldr    r1 , [r0]    
    ldr    r2 , =(0x0F<<5)
    bic    r1 , r1 , r2
    str    r1 , [r0]
***********END***************/

    /****调用测试代码子程序****/
    bl    copy_from_bootsram_to_sdram
    /***********END************/
    @设置栈顶指针
    ldr    sp , =0xB4000000

/****************灯全灭**************/

    ldr    r0 , =GPBCON
    ldr    r1 , [r0]        /*把GPBCON里的内容加载到r1里*/
    bic    r1 , r1 , #0x3FC00    /*操作数取反码或上r1,用于清零工作*/
    orr    r1 , r1 , #0x15400
    str    r1 , [r0]    
    ldr    r0 , =GPBDAT
;    ldr    r1 , [r0]
;    bic    r1 , r1 , #0x1E0    
;    orr    r1 , r1 , #0x1E0
;    str    r1 , [r0]        /*此时4个LED等全灭*/

/***************END灯全灭***************/

    @跳转到测试代码执行
    ldr    pc , =main

halt_loop:
    b    halt_loop




memory_init:
/*******内存初始化子程序*********/

    @BWSCON[27:24] = 0 0 10B
    ldr    r0 , =BWSCON
    ldr    r1 , [r0]
    ldr    r2 , =(0x0F<<24)
    bic    r1 , r1 , r2
    ldr    r2 , =(0x02<<24)
    orr    r1 , r1 , r2
    str    r1 , [r0]    

    @BANKCON6[16:15]=11B;BANKCON6[3:0]=00 01B
    ldr    r0 , =BANKCON6
    ldr    r1 , [r0]
    ldr    r2 , =(0x03<<15)
    bic    r1 , r1 , r2
    orr    r1 , r1 , r2
    ldr    r2 , =0x0F
    bic    r1 , r1 , r2
    ldr    r2 , = 0x01
    orr    r1 , r1 , r2
    str    r1 , [r0]

    @REFRESH[23:18] = 1 0 00 00B;REFRESH[10:0] = 0x7A3
    ldr    r0 , =REFRESH
    ldr    r1 , [r0]
    ldr    r2 , =(0x3F<<18)
    bic    r1 , r1 , r2
    ldr    r2 , =(0x20<<18)
    orr    r1 , r1 , r2
    ldr    r2 , =0x7FF
    bic    r1 , r1 , r2
    ldr    r2 , = 0x7A3
    orr    r1 , r1 , r2
    str    r1 , [r0]

    @BANKSIZE[7:0] = 1 0 1 1 0 001 B
    ldr    r0 , =BANKSIZE
    ldr    r1 , [r0]
    ldr    r2 , =0xFF
    bic    r1 , r1 , r2
    ldr    r2 , =0xB1
    orr    r1 , r1 , r2
    str    r1 , [r0]    

    @MRSRB6[11:0] = 0 00 011 0 000 B
    ldr    r0 , =MRSRB6
    ldr    r1 , [r0]
    ldr    r2 , =0x3FF
    bic    r1 , r1 , r2
    ldr    r2 , =0x030
    orr    r1 , r1 , r2
    str    r1 , [r0]    

    mov    pc , lr        @函数返回
/******END内存初始化子程序*******/




build_page:
/********建立页表子程序***********/

/*对0地址处的映射*/
    ldr    r1 , =0x00000000        @VA起始地址
    ldr    r2 , =0x00000000        @PA起始地址

     @*(MMU_TTB&0xFFFFC000+VA>>20<<2) = (PA&0xFFF00000)|(SPUP_WR(0x03)<<AP(10))|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(NCNB_CTTBTT(0x00)<<Ctt_Btt(2))|(SECTION_FLAG(0x02))
    ldr    r0 , =(MMU_TTB & 0xFFFFC000)
    mov    r3 , r1 , LSR#20
    add    r0 , r0 , r3  , LSL#2       @r0存入段描述符所在一级页表条目地址
    
    mov    r3 , r2 , LSR#20
    mov    r3 , r3 , LSL#20
    mov    r4 , #0x0
    ldr    r4 , =SPUP_WR_SECTIONAP | MANAGER_DOMAIN | WT_CTTBTT | SECTION_FLAG
    orr    r3 , r3 , r4                @r3存入要放进页表的段描述符

    str    r3 , [r0]                   @往一级描述符地址放入对应的段描述符

/*END对0地址处的映射*/


/*对内存的映射*/
@VA:0xB0000000~0xB3FFFFFF------>PA:0x30000000~0x33FFFFFF
@using section-mapped
@总共64M,需要映射64次,每一次VA跟PA都要自加1M
    ldr    r1 , =0xB0000000        @VA起始地址
    ldr    r2 , =0x30000000        @PA起始地址
    ldr    r5 , =0xB4000000        @循环计数初值
l:      @*(MMU_TTB&0xFFFFC000+VA>>20<<2) = (PA&0xFFF00000)|(SPUP_WR(0x03)<<AP(10))|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(WT(0x03)<<Ctt_Btt(2))|(SECTION_FLAG(0x02))
    ldr    r0 , =(MMU_TTB & 0xFFFFC000)
    mov    r3 , r1 , LSR#20
    add    r0 , r0 , r3 , LSL#2        @r0存入段描述符所在一级页表条目地址
    
    mov    r3 , r2 , LSR#20
    mov    r3 , r3 , LSL#20
    mov    r4 , #0x0
    ldr    r4 , =SPUP_WR_SECTIONAP | MANAGER_DOMAIN | WT_CTTBTT | SECTION_FLAG
    orr    r3 , r3 , r4                @r3存入要放进页表的段描述符

    str    r3 , [r0]                   @往一级描述符地址放入对应的段描述符
    
    add    r1 , r1 , #0x100000
    add    r2 , r2 , #0x100000
    cmp    r1 , r5
    bne    l                          @如果VA没加到结尾就跳转到前一个l执行
/*END对内存的映射*/

/*对IO相关寄存器的映射*/
@VA:0xA0000000~0xA00003FF------>PA:0x56000000~0x560003FF
@using tiny page-mapped
    ldr    r1 , =0xA0000000        @VA起始地址
    ldr    r2 , =0x56000000        @PA起始地址
@因为IO相关寄存器地址范围不超过1K,所以我这里选择tiny page直接对1K进行设置

@Level one addr=(MMU_TTB&0xFFFFC000+VA>>20<<2)
@*(Level one addr)=(MMU_FPTB&0xFFFFF000)|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(FINE_FLAG(0x03))
@Level two addr=(MMU_FPTB&0xFFFFF000+(VA>>8)&0xFFC)
@*(Level two addr)=(PA&0xFFFFFC00)|(SPUP_WR(0x03)<<AP(4))|(NCNB(00B)<<Ctt_Btt(2))|(TINY_FLAG(0x03))
    ldr    r0 , =MMU_TTB & 0xFFFFC000
    mov    r3 , r1 , LSR#20
    add    r0 , r0 , r3 , LSL#2        @r0存入小页描述符所在的一级页表条目地址
    
    ldr    r3 , =(MMU_FPTB & 0xFFFFF000) | MANAGER_DOMAIN | FINE_FLAG
    str    r3 , [r0]                   @往一级页表描述符地址放入对应的小页描述符

    ldr    r0 , =(MMU_FPTB & 0xFFFFF000)
    mov    r3 , r1 , LSR#8
    ldr    r4 , =0xFFC
    and    r3 , r3 , r4                
    add    r0 , r0 , r3                @r0存入微小页描述符所在的二级细页表条目地址
    
    mov    r3 , r2 , LSR#10
    mov    r3 , r3 , LSL#10
    ldr    r4 , =SPUP_WR_TINYAP | NCNB_CTTBTT | TINY_FLAG    
     orr    r3 , r3 , r4                @r3存入微小页描述符
    str    r3 , [r0]                   @往一级页表描述符索引到的地址放入对应的小页描述符
/*END对IO相关寄存器的映射*/

@返回到主程序
    mov    pc , lr        
/********END建立页表子程序********/



copy_from_bootsram_to_sdram:
/*******拷贝测试代码子程序********/
@我们把测试代码加载在0x800=2048处(加载地址)
@拷贝到0x30005000=0x30000000(转换表基址)+0x4000(转换表大小)+0x1000(细页表大小)
    mov    r1 , #0x800        @源地址
    ldr     r2 , =0xB0005000   @目标地址
l1:
    ldr    r3 , [r1] , #0x04
    str    r3 , [r2] , #0x04
    cmp    r1 , #0x1000
    bne    l1
    mov    pc , lr
/******END拷贝测试代码子程序******/


mmu_init:
/*********初始化MMU子程序*********/
    mov    r1 , #0x0
    mcr    p15 , 0 , r1 , c7 , c7 , 0        @Invalidate Caches
    mcr    p15 , 0 , r1 , c7 , c10, 4        @清除write buffer
    mcr    p15 , 0 , r1 , c8 , c7 , 0        @Invalidate TLBs
    ldr    r1 , =MMU_TTB
    mcr    p15 , 0 , r1 , c2 , c0 , 0        @设置TTB register
        ldr    r1 , =0xAAAAAAB4
    mcr    p15 , 0 , r1 , c3 , c0 , 0        @设置domain access control register
    
    mrc    p15 , 0 , r1 , c1 , c0 , 0        @读取cp15 control register放入r1
    ldr    r2 , =0x3FFFFFFF
    bic    r1 , r1 , r2
    ldr    r2 , =0x517F
    orr    r1 , r1 , r2
        @如果现在就启动了MMU,那么下面的返回地址就变成虚拟地址,所以跳转后再启动MMU
    mov    pc , lr       @返回主程序
/******END初始化MMU子程序*********/

/************************************************END************************************************************/


④led_display.c


#define    GPBCON    (*(volatile unsigned long *)0xA0000010)
#define    GPBDAT    (*(volatile unsigned long *)0xA0000014)

/**
*如果不开启-O2以上优化,gcc编译器不提供inline优化,所以写入的inline只在编译时加入了-O2优化选项才会有效,这时for里面的循环参数可以不加volatile
*如果开启了-O2以上优化,gcc提供的inline优化有效,如果for循环里做编译器认为没意义的事,循环参数加上volatile声明。
*/

static inline void  delay(volatile unsigned long dly)
{
    for(; dly > 0; dly--)

}
int main()
{
    unsigned long i = 0;
    GPBCON &= ~(0xFF<<10);
    GPBCON |= (0x55<<10);    //把GPB5~8都置为输出功能
    while(1){
        delay(500000);        
        GPBDAT = (~(i<<5));         // 根据i的值,点亮LED1,2,3,4
        if(++i == 16)
            i = 0;
    }
    return 0;
}



    
[2] MQX3.8源代码分析:GPIO(四)驱动安装函数 _io_gpio_install()
    来源: 互联网  发布时间: 2014-02-18
MQX3.8源代码分析:GPIO(4)驱动安装函数 _io_gpio_install()

        关于GPIO的内部基准时钟已经打开,下一步就是给GPIO安装操作函数了,只有安装了操作函数,才能够利用的标准的接口访问GPIO.

1、文件:Io_gpio.c (source\io\gpio)中

/*FUNCTION*-------------------------------
 * 
 * Function Name    : _io_gpio_install
 * Returned Value   : _mqx_uint a task error code or MQX_OK
 * Comments         :
 *    Install a gpio driver.
 *
 *END*----------------------------------*/
 
 _mqx_uint _io_gpio_install
    (
       /* [IN] A string that identifies the device for fopen */
       /* input values are those identifiers defined in io_gpio.h file */
       char_ptr            identifier
    ) 
 { /* Body */
     if (IO_OK == gpio_cpu_init())
         return _io_dev_install(identifier,  _io_gpio_open, _io_gpio_close, _io_gpio_read, _io_gpio_write, gpio_cpu_ioctl,  NULL);
     return (_mqx_uint)IO_ERROR;
 } /* Endbody */

 分析:

          gpio_cpu_init()函数,我们已经知道是为了实现硬件层gpio操作时钟的使能控制的。那么_io_dev_install(xxxx)函数,就是给gpio模块安装驱动函数的,驱动函数类似于linux方式,包括:打开、关闭、读、写、控制。这些控制函数都在本文件内实现,当我们调用fopen、fclose、fread、fwrite、ioctl等系统统一接口时,mqx会在内部通过映射表,调用gpio模块对应的功能函数。例如:fopen -> _io_gpio_open。


2、文件:Io_inst.c (source\io)中

/*FUNCTION*-------------------------------
 * 
 * Function Name    : _io_dev_install
 * Returned Value   : _mqx_uint a task error code or MQX_OK
 * Comments         :
 *    Install a device dynamically, so tasks can fopen to it.
 *
 *END*----------------------------------*/
 
 _mqx_uint  _io_dev_install
    (
       /* [IN] A string that identifies the device for fopen */
       char_ptr             identifier,
   
       /* [IN] The I/O open function */
       _mqx_int (_CODE_PTR_ io_open)(MQX_FILE_PTR, char _PTR_, char _PTR_),
 
       /* [IN] The I/O close function */
       _mqx_int (_CODE_PTR_ io_close)(MQX_FILE_PTR),
 
       /* [IN] The I/O read function */
       _mqx_int (_CODE_PTR_ io_read)(MQX_FILE_PTR, char _PTR_, _mqx_int),
 
       /* [IN] The I/O write function */
       _mqx_int (_CODE_PTR_ io_write)(MQX_FILE_PTR, char _PTR_, _mqx_int),
 
       /* [IN] The I/O ioctl function */
       _mqx_int (_CODE_PTR_ io_ioctl)(MQX_FILE_PTR, _mqx_uint, pointer),
 
       /* [IN] The I/O initialization data */
       pointer              io_init_data_ptr
    )
 { /* Body */
 
    return (_io_dev_install_ext(identifier, io_open, io_close, io_read, io_write, io_ioctl, (_mqx_int (_CODE_PTR_)(IO_DEVICE_STRUCT_PTR))NULL,   io_init_data_ptr));
 
 } /* Endbody */

分析:

        GPIO的设备驱动安装函数,该函数接收5个函数指针和一个void型指针。调用带函数指针形参的函数时,我们只需要把对应函数实参的名字传递过去就可以了,不用对函数名称取地址&,因为在C语言内部,函数名称指的就是函数的入口地址,即:该函数运行的首地址,关于这个不再多说。最后一个void*指针,是给该函数传递初始化数据的,这里传递了一个NULL。

         该函数经过了一层封装,调用了_io_dev_install_ext(xxx)函数,进行具体的安装工作,与外层函数相比,它多了一个形参:

 (_mqx_int  (_CODE_PTR_) (IO_DEVICE_STRUCT_PTR)) NULL。主体是一个NULL,前面的是一个强制类型转换,转换成了什么,转换成了一个函数指针,该函数返回_mqx_int数值,形参IO_DEVICE_STRUCT_PTR。详细分析进入该函数即可看到。

         可能有人会问:传递一个空数据,干嘛还要再进行一次函数调用?这其中就涉及到函数维护的知识了,可能_io_dev_install_ext()函数出现较早,而那个参数随着版本升级,不再需要,但是为了保持一致性,又不能轻易修改已经成型的函数,这时候怎么办。办法就是再增加一层函数,利用这层函数屏蔽掉这个变化,使上层看不到无效数据。以后,当维护函数数据较多时,肯定会用到这个方法。

对_io_dev_install_ext(xx)函数的分析,估计需要的信息较多,我们留在下一节吧!






    
[3] 谈谈过去1个月修改过的BUG-3G模块有关问题(1)
    来源: 互联网  发布时间: 2014-02-18
谈谈过去1个月修改过的BUG--3G模块问题(1)

    步入11月份了,自从上个月答应老大接手手机项目的3G模块至今,已经有一个月了吧。这一个月来,一直围绕着3G模块的那几个BUG在查原因,在抓LOG,很是辛苦,但后来解决之后,倍感欣慰。


    先简单描述一下这个手机项目,CPU选用三星Exynos4412,和即将在本月27号登场的MX四核手机同一款CPU,3G模块选用XMM6260,然后系统跑的是android 4.0,于是kernel选用linux 3.0。其它模块由于不是本人负责,而且与本篇文章暂时无关,就不描述了。


    本人负责的是3G模块,由于接手前已经由一后来离职的工程师调通,所以基本上我只需要修改BUG。经测试发现,该模块有以下BUG:

1、开机后,只能进入一次深度休眠(以下简称“休眠”),唤醒后就再也无法进入休眠。

2、3G的加入影响了休眠唤醒速度。

    这两个BUG由于涉及内容太多,在刚刚接手时一时不知道该往何处下手。来公司4年多了,基本上很多问题都是跟休眠唤醒有关,不管是以前做过的WINCE,还是现在做的android,无一例外。


    废话不说,先研究一下此3G模块。后来发现6260驱动主要由三个部分构成:模块控制部分、主控USB部分和数据传输部分。模块控制主要配置基本的IO口,完成模块的上电复位等操作,以及注册中断处理函数,为上层提供一系列的控制接口。由于6260属于HSIC(即High Speed Inter Chip简称)设备,通过USB进行传输,故此部分的代码显得至关重要,在实际的调试过程中,确实花费了很多时间与精力来研究它的相关代码。数据传输部分主要为USB转串口的驱动,所有的数据都是通过这个驱动进行数据收发的。


    仔细研究了数据传输部分的代码,实际就cdc_acm.c文件。在acm_resume函数中有一句代码:wake_lock_pm(acm->parent);,作用为申请锁,做过android开发的也许都知道,应用程序一旦申请了休眠锁而不释放,那么系统就无法进入休眠,在此模块中,也不例外。但其实去掉这句代码后,还有其它问题。


    围绕这句代码,我改了好几天,释放锁、在别的地方申请锁及释放等……一直都得不到一个满意的结果。后来比较其它项目的驱动,发现其实cdc_acm.c中并没有用到锁,于是我把手机项目该驱动的锁都屏蔽,结果让人很意外,系统能够正常进入休眠,以及正常唤醒了,测试了好多次都如此!那么,是不是就修改好了第1个问题了呢?按照经验,需要做多次测试后才能下结论。于是把代码发给其它同事帮忙测试,也没发现有什么问题。(在后来的大规模测试中,才发现了由此引发的其它问题,我们后面会讨论)


    再看第二个问题,我把3G驱动卸载,果然发现休眠后唤醒速度提高了许多,不再是之前的按下POWER键需要等待3秒才唤醒了。然后又是漫长的测试阶段……按照经验,系统唤醒后所看到的LOG具有滞后性,也就是说你看到的第一句LOG的时候,系统其实已经运行了N行代码。于是我让硬件工程师帮忙接了两个暂时用不到的IO口出来,在按下POWER键时在相应的地方置该IO口输出高或低,配置示波器,就可以知道代码卡在哪里啦。与休眠唤醒电源相关的代码无非是arch/arm/里面的几个pm.c文件,在里面加了相应的操作,没发现有什么问题。后来又在按键驱动中把IO操作也加进去量,还是没有发现什么问题,按下POWER键的时候,我置高或置低的IO电平是得到期望值的。这么东一改西一改又过去了好几天,一直很郁闷。


    后来又想到一个方法,3G驱动由3部分构成,那么既然现在确定了唤醒时间与该模块驱动有关,可以分步卸载该驱动再测试看看。说做就做,于是把3G的3个部分分步去掉,当然会碰到编译不过的问题,为了这次结果,都暂时把编译不过的地方修改了。果然这么一测,还真让我找到了。当我卸载到只剩下USB驱动后,唤醒时间依旧为3秒钟,那么就应该是USB驱动的问题。


    休眠唤醒都是suspend和resume函数搞的鬼,那么当然重点看这两个函数。我把suspend和resume函数对3G的操作都屏蔽掉,再测试,发现唤醒速度快了许多。这一结果令人振奋!那么就应该是这两个地方了。再一步一步查找,果然让我发现了问题:在suspend中有把mc->in_l3_state置1,但唤醒后没清0!暂且先不管这个变量是做什么用的,休眠前在保存状态,唤醒后恢复状态是亘古不变的真理,于是我在resume中把它置0,奇迹出现了,系统的唤醒速度真的快了许多!!!!


    以上是我调试这两个BUG的一些过程,由于水平有限,不免有错误的地方,欢迎大家提出来并批评指正。

    本以为修改完这两个BUG,3G就不会有问题了,没想到后来经过大规模测试,我们依旧发现了后续的问题,但不管怎样,BUG总会得到解决的。下一篇,我再详细描述由此带来的其它BUG以及解决方法。


    
最新技术文章:
▪Android开发之登录验证实例教程
▪Android开发之注册登录方法示例
▪Android获取手机SIM卡运营商信息的方法
▪Android实现将已发送的短信写入短信数据库的...
▪Android发送短信功能代码
▪Android根据电话号码获得联系人头像实例代码
▪Android中GPS定位的用法实例
▪Android实现退出时关闭所有Activity的方法
▪Android实现文件的分割和组装
▪Android录音应用实例教程
▪Android双击返回键退出程序的实现方法
▪Android实现侦听电池状态显示、电量及充电动...
▪Android获取当前已连接的wifi信号强度的方法
▪Android实现动态显示或隐藏密码输入框的内容
▪根据USER-AGENT判断手机类型并跳转到相应的app...
▪Android Touch事件分发过程详解
▪Android中实现为TextView添加多个可点击的文本
▪Android程序设计之AIDL实例详解
▪Android显式启动与隐式启动Activity的区别介绍
▪Android按钮单击事件的四种常用写法总结
▪Android消息处理机制Looper和Handler详解
▪Android实现Back功能代码片段总结
▪Android实用的代码片段 常用代码总结
数据库 iis7站长之家
▪Android中通过view方式获取当前Activity的屏幕截...
▪Android提高之自定义Menu(TabMenu)实现方法
▪Android提高之多方向抽屉实现方法
▪Android提高之MediaPlayer播放网络音频的实现方法...
▪Android提高之MediaPlayer播放网络视频的实现方法...
▪Android提高之手游转电视游戏的模拟操控
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3