• 鸿蒙 移植 树莓派(下)修改源码


    目录:

    1、切换启动模式

    2、修改串口驱动

    3、系统时钟初始化

    4、源码下载地址

    1、切换启动模式

    树莓派默认启动在HYP模式,我们需要在内核启动前改为SVC模式

    kernelliteos_aarcharmarmsrcstartup eset_vector_up.S 在115行左右,reset_vector:下面添加

        mrs r0,cpsr         //读取CPU模式寄存器
        bic r0,r0,#0x1F     //清除CPU模式位(如果处于催眠模式,它将是1A)保留所有其他  
        orr r0,r0,#0x13     //设置CPU_MODE为SVC_MODE (0x13),而ORR仍然保留所有其他位
        msr spsr_cxsf,r0    //将其写入spsr_cxsf寄存器,以便在调用交换机时加载该寄存器。
        add r0,pc,#4        //从pc计算要进入SVC_MODE的地址(后面的两个操作码很长)
        msr ELR_hyp,r0      //将地址值写入ELR_hyp寄存器
        eret                //执行了回车指令

    2、修改串口驱动

    2.1、为了方便调试,先设置一个字符打印函数

    kernelliteos_aplatformuartamba_pl011amba_pl011.c在46行左右处添加下面的代码,uart_putc_phy使用物理地址打印字符,uart_putc_virt使用虚拟地址打印。当内核代码启动MMU之后,需用使用uart_putc_virt来打印字符。

    /*---------自定义函数----------*/
    #define RPI_BASE_UART_REGISTER (0x3f201000) //HI3516:0x120A0000 rpi2:0x3F201000
    #define AMBA_UART_DR (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER + 0x00))
    #define AMBA_UART_FR (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER + 0x18))
    ​
    #define RPI_BASE_UART_REGISTER1 IO_DEVICE_ADDR(0x3F201000) //HI3516:0x120A0000 rpi2:0x3F201000
    #define AMBA_UART_DR1 (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER1 + 0x00))
    #define AMBA_UART_FR1 (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER1 + 0x18))
    /*---------------------------*/
    ​
    void uart_putc_phy(unsigned char c)
    {
        //UART_Type *uartRegs = (UART_Type *)UART4_REG_PBASE;
        //while ((uartRegs->USART_ISR & (1<<5)) == 0);
        //uartRegs->USART_TDR = c;
        while (AMBA_UART_FR & (1 << 5));
        AMBA_UART_DR = c;
    }
    ​
    void uart_putc_virt(unsigned char c)
    {
        //UART_Type *uartRegs = (UART_Type *)UART_REG_BASE;
        //while ((uartRegs->USART_ISR & (1<<5)) == 0);
        //uartRegs->USART_TDR = c;
        while (AMBA_UART_FR1 & (1 << 5));
        AMBA_UART_DR1 = c;
    }

    例如:kernelliteos_aarcharmarmsrcstartup eset_vector_up.S

        ldr sp, =0x00000000 + 0x5000000  //调用C函数前,得先设置栈,树莓派物理内存从0x0开始
        mov r0, #'m'
        bl uart_putc_phy                //在MMU启动之前使用的是物理地址打印
        
        bl      mmu_setup                           /* set up the mmu */
        
        mov r0, #'M'
        bl uart_putc_virt               //在MMU启动之后使用的是虚拟地址打印

    2.2、添加串口中断,串口输入代码

    vendorbroadcomBCM2836driveruartuart_hardware.c

    2.2.1、 串口的中断函数,产生中断时,这个函数调用

    static irqreturn_t BCM2836_uart_irq(int irq, void *data)
    {
        char buf[FIFO_SIZE];
        unsigned int count = 0;
        struct BCM2836_port *port = NULL;
        struct uart_driver_data *udd = (struct uart_driver_data *)data;
        UART_Type *uartRegs;
        uint32_t status;
        
        if (udd == NULL) {
            uart_error("udd is null!
    ");
            return IRQ_HANDLED;
        }
        port = (struct BCM2836_port *)udd->private;
        uartRegs = (UART_Type *)port->phys_base;
        READ_UINT32(status, UART_REG_BASE + UART_FR);
        if ((UARTREG(UART_REG_BASE,UART_FR)&(1<<4)) == 0) {
            do {
                buf[count++] = UARTREG(UART_REG_BASE,UART_DR);//*(volatile UINT32 *)((UINTPTR)(UART_REG_BASE + UART_DR)); //去读取硬件得到数据
                if (udd->num != CONSOLE_UART) {
                    continue;
                }
                if (CheckMagicKey(buf[count - 1])) { //数据放在buf里
                    goto end;
                }
    ​
                if (buf[count-1] == '
    ') //对windows和liteos回车换行的处理
                    buf[count-1] = '
    ';
            } while (UARTREG(UART_REG_BASE,UART_DR));
            udd->recv(udd, buf, count); //调用udd里的recv函数把数据发送给上一级
        }
        UARTREG(UART_REG_BASE, UART_ICR) = 0x3ff;
    end:
        /* clear all interrupt */
        return 0;
    }

    2.2.2、串口的初始化函数

    static int BCM2836_startup(struct uart_driver_data *udd) 
    {
        int ret = 0;
        struct BCM2836_port *port = NULL;
        if (udd == NULL) {
            uart_error("udd is null!
    ");
            return -EFAULT;
        }
        port = (struct BCM2836_port *)udd->private;//*private是一个指针,指向 struct {enable,phys_base,irq_num,*udd}
        if (!port) {
            uart_error("port is null!");
            return -EFAULT;
        }
        /* enable the clock */
        LOS_TaskLock();
        LOS_TaskUnlock();
    ​
        ret = request_irq(port->irq_num, (irq_handler_t)BCM2836_uart_irq,0, "uart_dw", udd);  //去注册一个串口的接收中断函数
        /* 1.uart interrupt priority should be the highest in interrupt preemption mode */
        //ret = LOS_HwiCreate(NUM_HAL_INTERRUPT_UART, 0, 0, (HWI_PROC_FUNC)uart_handler, NULL);
    ​
        /* 2.clear all irqs */
        UARTREG(UART_REG_BASE, UART_ICR) = 0x3ff;
        //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201044)) = 0x3ff;
    ​
        /* disable FIFO mode */
        //uartRegs->USART_CR1 &= ~(1<<29);
        //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F20102C)) = 0x60;
        UARTREG(UART_REG_BASE, UART_LCR_H) = (1 << 6 | 1 << 5| 1 << 4); 
    ​
        /* 3.set fifo trigger level */
        //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201034)) = 0x0;
        UARTREG(UART_REG_BASE, UART_IFLS) = 0;
    ​
        /* 4.enable rx interrupt 开启串口接收中断,第4位*/
        UARTREG(UART_REG_BASE, UART_IMSC) = (1 << 4 | 1 << 6); //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201038)) = 0x10;
    ​
        /* 5.enable receive */
        UARTREG(UART_REG_BASE, UART_CR) |= (1 << 9); //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201030)) = 0x301;
    ​
        //HalIrqUnmask(NUM_HAL_INTERRUPT_UART);//6. 
        *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B214)) = 0x02000000;//Unmask接收25号中断
    ​
        BCM2836_config_in(udd);
    ​
        return ret;
    }

    2.2.3、串口写函数

    static int BCM2836_start_tx(struct uart_driver_data *udd, const char *buf, size_t count)
    {
        unsigned int tx_len = count;
        struct BCM2836_port *port = NULL;
        char value;
        unsigned int i;
        int ret = 0;
    ​
        if (udd == NULL) {
            uart_error("udd is null!
    ");
            return -EFAULT;
        }
        port = (struct BCM2836_port *)udd->private;
        if (!port) {
            uart_error("port is null!");
            return -EFAULT;
        }
        /* UART_WITH_LOCK: there is a spinlock in the function to write reg in order. */
        for (i = 0; i < tx_len; i++ ){
            ret = LOS_CopyToKernel((void *)&value, sizeof(char),(void *)(buf++), sizeof(char));
            if (ret) {
                return i;
            }
            (void)UartPutsReg(port->phys_base, &value, 1, UART_WITH_LOCK);
        }
        return count;
    }

    3、系统时钟初始化
    3.1、main函数的各种调用,验证参数
    kernelliteos_aplatformmain.c->main()

     kernelliteos_akernelcommonlos_config.c->OsMain()

     kernelliteos_aarcharmarmsrclos_hw_tick.c->OsTickInit()

    systemClock     //vendor里设置的是50000000
    tickPerSecond   //鸿蒙默认设置的是100
    LITE_OS_SEC_TEXT_INIT UINT32 OsTickInit(UINT32 systemClock, UINT32 tickPerSecond)
    {    //只是验证了下传入的这两个参数,并未使用
        HalClockInit();
        return LOS_OK;
    }

    3.2、先获取当前时钟频率,注册中断
    kernelliteos_aplatformhwarm imerarm_genericarm_generic_timer.c

    OS_TICK_INT_NUM//中断号,在vendor******oardincludeasmhal_platform_ints.h下定义,查手册确定
    MIN_INTERRUPT_PRIORITY//优先级
    OsTickEntry//中断函数
    LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
    {   ...
        g_sysClock = HalClockFreqRead(); //先获取当前时钟频率
        
        //调用LOS_HwiCreate函数新建中断,系统中断由它注册
        ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickEntry, 0);//参数1:中断号、参数4:执行函数
        //这个函数就不深入了,大体就是将中断号好和对应的执行函数放到一个数组
        //比如这里就是,当发生OS_TICK_INT_NUM这个中断时,执行OsTickEntry()函数
        ...
    }

    3.3、时钟中断的执行函数OsTickEntry()
    kernelliteos_aplatformhwarm imerarm_genericarm_generic_timer.c

    不过此时这是注册了这个函数,时钟并未启动,得执行了(三.启动时钟)之后才会调用这个函数
    LITE_OS_SEC_TEXT VOID OsTickEntry(VOID)
    {
        TimerCtlWrite(0);
        OsTickHandler();
        TimerCvalWrite(TimerCvalRead() + OS_CYCLE_PER_TICK);
        TimerCtlWrite(1);
        //使用最后一个cval生成下一个tick的时间是绝对和准确的。不要使用tval来驱动一般时间,在这种情况下tick会变慢。
    }

    3.3、启动时钟
    main() => OsStart(VOID) => OsTickStart() => HalClockStart(VOID)

    kernelliteos_aplatformhwarm imerarm_genericarm_generic_timer.c => HalClockStart(VOID)

    //树莓派2没有GIC所以这个函数要爆改
    LITE_OS_SEC_TEXT_INIT VOID HalClockStart(VOID)
    {
        HalIrqUnmask(OS_TICK_INT_NUM);  //wendor里定义的 OS_TICK_INT_NUM = 29
        TimerCtlWrite(0);
        TimerTvalWrite(OS_CYCLE_PER_TICK);
        TimerCtlWrite(1);
    }

    3.3.1、HalIrqUnmask; //接收中断(通过设置寄存器,允许CPU响应该中断)

    HalIrqUnmask(OS_TICK_INT_NUM);
    HalIrqUnmask(29);
    GIC_REG_32(GICD_ISENABLER(29 >> 5)) = 1U << (29 % 32);
    (GICD_ISENABLER(29 >> 5))拆开
    GIC_REG_32(GICD_OFFSET + 0x100 + (29 >> 5) * 4) = 1U << (29 % 32);/* 中断使能 Registers */
    GIC_REG_32拆开,(29 % 32)=1D
    GIC_BASE_ADDR + (GICD_OFFSET + 0x100 + (29 >> 5) * 4) = 1U << (29 % 32)
    #define GIC_BASE_ADDR             IO_DEVICE_ADDR(0x3F00A100)
    #define GICD_OFFSET               0x1000     /* interrupt distributor offset */

    3.3.2、TimerCtlWrite(0); //关闭Timer
    参考:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf

    《B3.17 Organization of the CP15 registers in a VMSA implementation》

    WRITE_TIMER_REG32(TIMER_REG_CTL, 0);
    ARM_SYSREG_WRITE(TIMER_REG_CTL, 0)
    ARM_SYSREG_WRITE(TIMER_REG(_CTL), 0)
    ARM_SYSREG_WRITE(CP15_REG(c14, 0, c2, 1)), 0)
    "mcr " (CP15_REG(c14, 0, c2, 1) :: "r" (val)
    反汇编
    r8 0
    mcr p15, #0, r8, c14, c2, #1    CNTP_CTL,PL1物理定时器控制寄存器

    3.3.3、TimerTvalWrite(OS_CYCLE_PER_TICK); //设置Tval

    反汇编
    r0 192000
    mcr p15, #0, r0, c14, c2, #0    CNTP_TVAL,PL1物理时间值寄存器

    3.3.4、TimerCtlWrite(1); //再开启Timer

    反汇编
    r5 1
    mcr p15, #0, r5, c14, c2, #1    CNTP_CTL,PL1物理定时器控制寄存器

    3.4、代码移植
    Z:rightharmony-100askkernelliteos_aplatformhwarminterruptgicgic_v2.c

    VOID HalIrqUnmask(UINT32 vector)
    {
        if ((vector > OS_USER_HWI_MAX) || (vector < OS_USER_HWI_MIN)) {
            return;
        }
        //GIC_REG_32(GICD_ISENABLER(vector >> 5)) = 1U << (vector % 32);  //替换
        *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B218)) = 1; //使能ARM Timer IRQ    
    ​}

    Z:rightharmony-100askkernelliteos_aplatformhwarm imerarm_genericarm_generic_timer.c

    STATIC_INLINE VOID TimerCtlWrite(UINT32 cntpCtl)
    {
        //WRITE_TIMER_REG32(TIMER_REG_CTL, cntpCtl);//替换
        if(cntpCtl == 0){
            *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B408)) = 0x003E0000;
            }
        else
        {
            *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B408)) = 0x003E00A2;
        }
    }
    ​STATIC_INLINE VOID TimerTvalWrite(UINT32 tval)
    {
        //WRITE_TIMER_REG32(TIMER_REG_TVAL, tval);//替换
        *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B400)) = tval;  //设置倒计时时间,鸿蒙是10ms    
    }

    作者: 亮子力

    想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

  • 相关阅读:
    使用yarn来替代npm
    React及Nextjs相关知识点小结
    appstore-react v2.0—redux-actions和redux-saga的应用
    开机SystemServer到ActivityManagerService启动过程分析
    java 读取气象专业格式NetCDF文件
    maven项目对于maven远程仓库没有资源的解决办法
    leaflet 使用kriging.js实现前端自定义插值
    leaflet 使用高德地图实例
    uni-app上使用leaflet地图的解决方案
    MySQL创建新用户并且赋予权限
  • 原文地址:https://www.cnblogs.com/HarmonyOS/p/14312105.html
Copyright © 2020-2023  润新知