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

本文详细介绍了如何在树莓派上修改内核启动模式,使其从HYP模式切换到SVC模式,并添加了串口驱动,包括物理地址和虚拟地址的字符打印函数,以及串口中断处理。此外,还阐述了鸿蒙系统时钟初始化的过程,包括时钟频率获取、中断注册和时钟启动的步骤。内容涵盖了硬件中断、MMU设置和驱动程序开发等关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、切换启动模式

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

kernel\liteos_a\arch\arm\arm\src\startup\reset_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、为了方便调试,先设置一个字符打印函数

kernel\liteos_a\platform\uart\amba_pl011\amba_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 <

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 <

AMBA_UART_DR1 = c;

}

例如:kernel\liteos_a\arch\arm\arm\src\startup\reset_vector_up.Sldr 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、添加串口中断,串口输入代码vendor\broadcom\BCM2836\driver\uart\uart_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!\n");

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] == '\r') //对windows和liteos回车换行的处理

buf[count-1] = '\n';

} 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!\n");

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 <

/* 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 <

/* 5.enable receive */

UARTREG(UART_REG_BASE, UART_CR) |= (1 <

//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!\n");

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 

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;

}

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()systemClock     //vendor里设置的是50000000

tickPerSecond   //鸿蒙默认设置的是100

LITE_OS_SEC_TEXT_INIT UINT32 OsTickInit(UINT32 systemClock, UINT32 tickPerSecond)

{    //只是验证了下传入的这两个参数,并未使用

HalClockInit();

return LOS_OK;

}

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

kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.cOS_TICK_INT_NUM//中断号,在vendor\***\***\board\include\asm\hal_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()函数

...

}

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

kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_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会变慢。

}

2.3、启动时钟

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

kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_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);

}

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

HalIrqUnmask(29);

GIC_REG_32(GICD_ISENABLER(29 >> 5)) = 1U <

(GICD_ISENABLER(29 >> 5))拆开

GIC_REG_32(GICD_OFFSET + 0x100 + (29 >> 5) * 4) = 1U <

GIC_REG_32拆开,(29 % 32)=1D

GIC_BASE_ADDR + (GICD_OFFSET + 0x100 + (29 >> 5) * 4) = 1U <

#define GIC_BASE_ADDR             IO_DEVICE_ADDR(0x3F00A100)

#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》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物理定时器控制寄存器

2.3.3、TimerTvalWrite(OS_CYCLE_PER_TICK); //设置Tval反汇编

r0 192000

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

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

r5 1

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.cVOID HalIrqUnmask(UINT32 vector)

{

if ((vector > OS_USER_HWI_MAX) || (vector 

return;

}

//GIC_REG_32(GICD_ISENABLER(vector >> 5)) = 1U <

*(volatile UINT32 *)((UINTPTR)IO_DEVICE_ADDR(0x3F00B218)) = 1; //使能ARM Timer IRQ

}

Z:\bright\harmony-100ask\kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.cSTATIC_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

}

...

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值