IDC

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

作者:admin 2021-06-27 我要评论

想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区 https://harmonyos.51cto.com/#zz 项目最新更新,可以查看码仓 https://gitee.com/liangz...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

项目最新更新,可以查看码仓 https://gitee.com/liangzili/harmony-raspberry

1、切换启动模式

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

kernel\liteos_a\arch\arm\arm\src\startup\reset_vector_up.S 在115行左右,reset_vector:下面添加

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

2、修改串口驱动

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

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

  1. /*---------自定义函数----------*/ 
  2. #define RPI_BASE_UART_REGISTER (0x3f201000) //HI3516:0x120A0000 rpi2:0x3F201000 
  3. #define AMBA_UART_DR (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER + 0x00)) 
  4. #define AMBA_UART_FR (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER + 0x18)) 
  5. ​ 
  6. #define RPI_BASE_UART_REGISTER1 IO_DEVICE_ADDR(0x3F201000) //HI3516:0x120A0000 rpi2:0x3F201000 
  7. #define AMBA_UART_DR1 (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER1 + 0x00)) 
  8. #define AMBA_UART_FR1 (*(volatile unsigned char *)(RPI_BASE_UART_REGISTER1 + 0x18)) 
  9. /*---------------------------*/ 
  10. ​ 
  11. void uart_putc_phy(unsigned char c) 
  12.     //UART_Type *uartRegs = (UART_Type *)UART4_REG_PBASE; 
  13.     //while ((uartRegs->USART_ISR & (1<<5)) == 0); 
  14.     //uartRegs->USART_TDR = c; 
  15.     while (AMBA_UART_FR & (1 << 5)); 
  16.     AMBA_UART_DR = c; 
  17. ​ 
  18. void uart_putc_virt(unsigned char c) 
  19.     //UART_Type *uartRegs = (UART_Type *)UART_REG_BASE; 
  20.     //while ((uartRegs->USART_ISR & (1<<5)) == 0); 
  21.     //uartRegs->USART_TDR = c; 
  22.     while (AMBA_UART_FR1 & (1 << 5)); 
  23.     AMBA_UART_DR1 = c; 

例如:kernel\liteos_a\arch\arm\arm\src\startup\reset_vector_up.S

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

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

  1. vendor\broadcom\BCM2836\driver\uart\uart_hardware.c 

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

  1. static irqreturn_t BCM2836_uart_irq(int irq, void *data) 
  2.     char buf[FIFO_SIZE]; 
  3.     unsigned int count = 0; 
  4.     struct BCM2836_port *port = NULL
  5.     struct uart_driver_data *udd = (struct uart_driver_data *)data; 
  6.     UART_Type *uartRegs; 
  7.     uint32_t status; 
  8.      
  9.     if (udd == NULL) { 
  10.         uart_error("udd is null!\n"); 
  11.         return IRQ_HANDLED; 
  12.     } 
  13.     port = (struct BCM2836_port *)udd->private; 
  14.     uartRegs = (UART_Type *)port->phys_base; 
  15.     READ_UINT32(status, UART_REG_BASE + UART_FR); 
  16.     if ((UARTREG(UART_REG_BASE,UART_FR)&(1<<4)) == 0) { 
  17.         do { 
  18.             buf[count++] = UARTREG(UART_REG_BASE,UART_DR);//*(volatile UINT32 *)((UINTPTR)(UART_REG_BASE + UART_DR)); //去读取硬件得到数据 
  19.             if (udd->num != CONSOLE_UART) { 
  20.                 continue
  21.             } 
  22.             if (CheckMagicKey(buf[count - 1])) { //数据放在buf里 
  23.                 goto end
  24.             } 
  25. ​ 
  26.             if (buf[count-1] == '\r') //对windows和liteos回车换行的处理 
  27.                 buf[count-1] = '\n'
  28.         } while (UARTREG(UART_REG_BASE,UART_DR)); 
  29.         udd->recv(udd, buf, count); //调用udd里的recv函数把数据发送给上一级 
  30.     } 
  31.     UARTREG(UART_REG_BASE, UART_ICR) = 0x3ff; 
  32. end
  33.     /* clear all interrupt */ 
  34.     return 0; 

2.2.2、串口的初始化函数

  1. static int BCM2836_startup(struct uart_driver_data *udd)  
  2.     int ret = 0; 
  3.     struct BCM2836_port *port = NULL
  4.     if (udd == NULL) { 
  5.         uart_error("udd is null!\n"); 
  6.         return -EFAULT; 
  7.     } 
  8.     port = (struct BCM2836_port *)udd->private;//*private是一个指针,指向 struct {enable,phys_base,irq_num,*udd} 
  9.     if (!port) { 
  10.         uart_error("port is null!"); 
  11.         return -EFAULT; 
  12.     } 
  13.     /* enable the clock */ 
  14.     LOS_TaskLock(); 
  15.     LOS_TaskUnlock(); 
  16. ​ 
  17.     ret = request_irq(port->irq_num, (irq_handler_t)BCM2836_uart_irq,0, "uart_dw", udd);  //去注册一个串口的接收中断函数 
  18.     /* 1.uart interrupt priority should be the highest in interrupt preemption mode */ 
  19.     //ret = LOS_HwiCreate(NUM_HAL_INTERRUPT_UART, 0, 0, (HWI_PROC_FUNC)uart_handler, NULL); 
  20. ​ 
  21.     /* 2.clear all irqs */ 
  22.     UARTREG(UART_REG_BASE, UART_ICR) = 0x3ff; 
  23.     //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201044)) = 0x3ff; 
  24. ​ 
  25.     /* disable FIFO mode */ 
  26.     //uartRegs->USART_CR1 &= ~(1<<29); 
  27.     //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F20102C)) = 0x60; 
  28.     UARTREG(UART_REG_BASE, UART_LCR_H) = (1 << 6 | 1 << 5| 1 << 4);  
  29. ​ 
  30.     /* 3.set fifo trigger level */ 
  31.     //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201034)) = 0x0; 
  32.     UARTREG(UART_REG_BASE, UART_IFLS) = 0; 
  33. ​ 
  34.     /* 4.enable rx interrupt 开启串口接收中断,第4位*/ 
  35.     UARTREG(UART_REG_BASE, UART_IMSC) = (1 << 4 | 1 << 6); //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201038)) = 0x10; 
  36. ​ 
  37.     /* 5.enable receive */ 
  38.     UARTREG(UART_REG_BASE, UART_CR) |= (1 << 9); //*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F201030)) = 0x301; 
  39. ​ 
  40.     //HalIrqUnmask(NUM_HAL_INTERRUPT_UART);//6.  
  41.     *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B214)) = 0x02000000;//Unmask接收25号中断 
  42. ​ 
  43.     BCM2836_config_in(udd); 
  44. ​ 
  45.     return ret; 

2.2.3、串口写函数

  1. static int BCM2836_start_tx(struct uart_driver_data *udd, const char *buf, size_t count
  2.     unsigned int tx_len = count
  3.     struct BCM2836_port *port = NULL
  4.     char value; 
  5.     unsigned int i; 
  6.     int ret = 0; 
  7. ​ 
  8.     if (udd == NULL) { 
  9.         uart_error("udd is null!\n"); 
  10.         return -EFAULT; 
  11.     } 
  12.     port = (struct BCM2836_port *)udd->private; 
  13.     if (!port) { 
  14.         uart_error("port is null!"); 
  15.         return -EFAULT; 
  16.     } 
  17.     /* UART_WITH_LOCK: there is a spinlock in the function to write reg in order. */ 
  18.     for (i = 0; i < tx_len; i++ ){ 
  19.         ret = LOS_CopyToKernel((void *)&value, sizeof(char),(void *)(buf++), sizeof(char)); 
  20.         if (ret) { 
  21.             return i; 
  22.         } 
  23.         (void)UartPutsReg(port->phys_base, &value, 1, UART_WITH_LOCK); 
  24.     } 
  25.     return count

2、系统时钟初始化

2.1、main函数的各种调用,验证参数

kernel\liteos_a\platform\main.c->main()

kernel\liteos_a\kernel\common\los_config.c->OsMain()

kernel\liteos_a\arch\arm\arm\src\los_hw_tick.c->OsTickInit()

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

 2.2、先获取当前时钟频率,注册中断

kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c

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

2.3、时钟中断的执行函数OsTickEntry()

kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c

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

2.3、启动时钟

main() => OsStart(VOID) => OsTickStart() => HalClockStart(VOID)

kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c => HalClockStart(VOID)

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

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

  1. HalIrqUnmask(OS_TICK_INT_NUM); 
  2. HalIrqUnmask(29); 
  3. GIC_REG_32(GICD_ISENABLER(29 >> 5)) = 1U << (29 % 32); 
  4. ​ 
  5. (GICD_ISENABLER(29 >> 5))拆开 
  6. GIC_REG_32(GICD_OFFSET + 0x100 + (29 >> 5) * 4) = 1U << (29 % 32);/* 中断使能 Registers */ 
  7. ​ 
  8. GIC_REG_32拆开,(29 % 32)=1D 
  9. GIC_BASE_ADDR + (GICD_OFFSET + 0x100 + (29 >> 5) * 4) = 1U << (29 % 32) 
  10. ​ 
  11. #define GIC_BASE_ADDR             IO_DEVICE_ADDR(0x3F00A100) 
  12. #define GICD_OFFSET               0x1000     /* interrupt distributor offset */ 

2.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》

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

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

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

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

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

2.4、代码移植

Z:\bright\harmony-100ask\kernel\liteos_a\platform\hw\arm\interrupt\gic\gic_v2.c

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

Z:\bright\harmony-100ask\kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c

  1. STATIC_INLINE VOID TimerCtlWrite(UINT32 cntpCtl) 
  2.     //WRITE_TIMER_REG32(TIMER_REG_CTL, cntpCtl);//替换 
  3.     if(cntpCtl == 0){ 
  4.         *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B408)) = 0x003E0000; 
  5.         } 
  6.     else 
  7.     { 
  8.         *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B408)) = 0x003E00A2; 
  9.     } 
  10. ​STATIC_INLINE VOID TimerTvalWrite(UINT32 tval) 
  11.     //WRITE_TIMER_REG32(TIMER_REG_TVAL, tval);//替换 
  12.     *(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B400)) = tval;  //设置倒计时时间,鸿蒙是10ms     

=======完整内容======

#2020征文-开发板# 鸿蒙 移植 树莓派(上)搭建环境下载源码

#2020征文-开发板# 鸿蒙 移植 树莓派(中)添加单板

#2020征文-开发板# 鸿蒙 移植 树莓派(下)修改源码

©著作权归作者和HarmonyOS技术社区共同所有,如需转载,请注明出处,否则将追究法律责任.

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz


本文转载自网络,原文链接:https://harmonyos.51cto.com/#zz

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • 鸿蒙移植树莓派(下)修改源码

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

  • 如何在 ASP.Net Core 中使用条件中间件

    如何在 ASP.Net Core 中使用条件中间件

  • 一篇文章带你了解JavaScript变量

    一篇文章带你了解JavaScript变量

  • 掌握6大模块、7个核心概念!帮你搞定My

    掌握6大模块、7个核心概念!帮你搞定My

腾讯云代理商
海外云服务器