--- title: nuttx categories: embedded, arm, stm32, stm32f429 toc: no ... 組員 ---- * 翁振育 * 黃亮穎 * 蘇偉嘉 * 李英碩 | hackpad : https://stm32f429.hackpad.com/NuttX--hqco2YSgAcJ | github : https://github.com/gototop/nuttx-stm32f4disc-bb Nuttx 參考應用 --------------- * `PX4 autopilot`_ NuttX 架構 ------------ 與平台相關 ------------ Run Nuttx on STM32F429-Disco ---------------------------------------------- | (1) 從 https://github.com/deadbeefcafe/nuttx-stm32f4disc-bb 複製Nuttx到workspace | (2) cd workspace/nuttx-stm32f4disc-bb/nuttx/tools | (3) ./configure.sh stm32f429i-disco/nsh | (4) cd .. | (5) make CROSSDEV=arm-none-eabi- | (6) st-flash write nuttx.bin 0x8000000 | (7) sudo screen /dev/ttyUSB0 115200 8n1 設定相關應用程式與功能 ------------------ | at stm32f429i-disco/nsh/defconfig | 120 CONFIG_ARCH_FPU=n | 465 CONFIG_RTC=y | 466 CONFIG_RTC_DATETIME=y | 653 CONFIG_EXAMPLES_HELLO=y | 676 CONFIG_EXAMPLES_OSTEST=y Task Control Interfaces ----------------------- - task_create : 產生一個activates的新task,並回傳系統指派的pid - task_init : 將task的TCB初始化,但並不activate task - task_activate : activate task,否則scheduler沒辦法執行它 - task_delete : 將task關掉,並deallocate它的TCB與stack - task_restart : 將task的children砍掉,deallocate TCB,reset priority - task_exit : 關掉目前在運行的task Task state ------------ .. image:: /task_state3.jpg include/nuttx/sched.h .. code-block:: c enum tstate_e { TSTATE_TASK_INVALID = 0, /* INVALID - The TCB is uninitialized */ TSTATE_TASK_PENDING, /* READY_TO_RUN - Pending preemption unlock */ TSTATE_TASK_READYTORUN, /* READY-TO-RUN - But not running */ TSTATE_TASK_RUNNING, /* READY_TO_RUN - And running */ TSTATE_TASK_INACTIVE, /* BLOCKED - Initialized but not yet activated */ TSTATE_WAIT_SEM, /* BLOCKED - Waiting for a semaphore */ #ifndef CONFIG_DISABLE_SIGNALS TSTATE_WAIT_SIG, /* BLOCKED - Waiting for a signal */ #endif #ifndef CONFIG_DISABLE_MQUEUE TSTATE_WAIT_MQNOTEMPTY, /* BLOCKED - Waiting for a MQ to become not empty. */ TSTATE_WAIT_MQNOTFULL, /* BLOCKED - Waiting for a MQ to become not full. */ #endif #ifdef CONFIG_PAGING TSTATE_WAIT_PAGEFILL, /* BLOCKED - Waiting for page fill */ #endif NUM_TASK_STATES /* Must be last */ }; Register --------- .. image:: /ARM_register.jpg Communication --------------------- | Semaphore | | sem_wait | sem_wait的主程式碼分為兩個部份,如果counter > 0,代表semaphore可以被hold,sem_wait會將task加入holder list | 否則sem_wait會block task,等待sem_hold unblock task,然後sem_wait再將task加入holder list .. code-block:: c if (sem->semcount > 0) { sem->semcount--; sem_addholder(sem); rtcb->waitsem = NULL; ret = OK; } else { sem->semcount--; rtcb->waitsem = sem; errno = 0; up_block_task(rtcb, TSTATE_WAIT_SEM); if (errno != EINTR && errno != ETIMEDOUT) { sem_addholder(sem); ret = OK; } } ------------------------------------------------------------ | sem_post | 一般而言,sem_post會在一個task要離開critical section的時候被呼叫 | 它會先呼叫sem_releaseholder,然後檢查counter是否<= 0,若是,代表還有task在等semaphore,sem_post會找到waiting list的下一個task,unblock它 .. code-block:: c if (sem) { sem_releaseholder(sem); sem->semcount++; if (sem->semcount <= 0) { for (stcb = (FAR struct tcb_s*)g_waitingforsemaphore.head; (stcb && stcb->waitsem != sem); stcb = stcb->flink); if (stcb) { stcb->waitsem = NULL; up_unblock_task(stcb); } } } ---------------------------------------------------------- | Mutex | nuttx的mutex實作是建立在semaphore上面 | 在使用mutex時,會先init一個binary semaphore,然後呼叫pthread_mutex_lock,pthread_mutex_lock會呼叫pthread_takesemaphore | pthread_takesemaphore會跑一個判別式是sem_wait的while loop | 當owner要交出使用權的時候,會呼叫pthread_mutex_unlock,pthread_mutex_unlock會將mutex->pid設成0,然後呼叫pthread_givesemaphore | pthread_givesemaphore會呼叫sem_post | | | pthread_mutexinit.c .. code-block:: c int pthread_mutex_init(FAR pthread_mutex_t *mutex, FAR pthread_mutexattr_t *attr) { : : status = sem_init((sem_t*)&mutex->sem, pshared, 1); : : } ------------------------------------------------ | phread_mutexlock.c .. code-block:: c int pthread_mutex_lock(FAR pthread_mutex_t *mutex) { : : : ret = pthread_takesemaphore((sem_t*)&mutex->sem); if (!ret) { mutex->pid = mypid; } } ------------------------------------------------- | pthread_initalize.c .. code-block:: c int pthread_takesemaphore(sem_t *sem) { if (sem) { while (sem_wait(sem) != OK) { : : : } return OK; } } .. code-block:: c int pthread_givesemaphore(sem_t *sem) { if (sem) { if (sem_post(sem) == OK) { return OK; } else { set_errno(EINVAL); return ERROR; } } else { set_errno(EINVAL); return ERROR; } } --------------------------------------------------------- | pthread_mutexunlock.c .. code-block:: c int pthread_givesemaphore(sem_t *sem) { if (sem) { if (sem_post(sem) == OK) { return OK; } else { set_errno(EINVAL); return ERROR; } } else { set_errno(EINVAL); return ERROR; } } Scheduling ---------------- include/sys/types.h .. code-block:: c /* HPUX-like MIN/MAX value */ #define PRIOR_RR_MIN 0 #define PRIOR_RR_MAX 255 #define PRIOR_FIFO_MIN 0 #define PRIOR_FIFO_MAX 255 #define PRIOR_OTHER_MIN 0 /* Not use */ #define PRIOR_OTHER_MAX 255 /* Not use */ /* Scheduling Priorities. NOTE: Only the idle task can take * the true minimum priority. */ #define SCHED_PRIORITY_MAX 255 #define SCHED_PRIORITY_DEFAULT 100 #define SCHED_PRIORITY_MIN 1 #define SCHED_PRIORITY_IDLE 0 | 1. RR 時間間隔是 20 ticks 做一次 context switch。 | 觀看每個設定time slice的相關檔案其運算是皆為 timeslice = CONFIG_RR_INTERVAL / MSEC_PER_TICK | 在 nuttx/include/nuttx/config.h 中 | CONFIG_RR_INTERVAL = 200 | MSEC_PER_TICK = 10 | 2. 若要禁止 context switch,可用sched_lock() | 此區禁止 context switch | sched_unlock()用在修改scheduler和priority。 | 3. 若要先將 timer interrupt 暫停,可用saved_state = irqsave(); | 此區暫停 timer interrupt | irqrestore(saved_state); | 用在修改 scheduler。 | 注意:timer interrupt 跟 context switch 是不同系統。 | 4. Nuttx 只有兩個 scheduler policy,其 tcb->timeslice = 0 則表示FIFO, 非0則為RR。 Interrupt Handler ------------------------ 在nuttx/arch/arm/include/stm32/irq.h和nuttx/arch/arm/include/stm32/stm32f40xxx_irq.h中分別定義了可用的ISR name和對應的vector name Processor Exceptions (vectors 0-15) 包括Internal和Software Interrupts,設定在nuttx/arch/arm/src/chip/stm32_irq.c:void up_irqinitialize(void) .. code-block:: c //nuttx/arch/arm/include/stm32/irq.h #define STM32_IRQ_RESERVED (0) /* Reserved vector (only used with CONFIG_DEBUG) */ /* Vector 0: Reset stack pointer value */ /* Vector 1: Reset (not handler as an IRQ) */ #define STM32_IRQ_NMI (2) /* Vector 2: Non-Maskable Interrupt (NMI) */ #define STM32_IRQ_HARDFAULT (3) /* Vector 3: Hard fault */ #define STM32_IRQ_MEMFAULT (4) /* Vector 4: Memory management (MPU) */ #define STM32_IRQ_BUSFAULT (5) /* Vector 5: Bus fault */ #define STM32_IRQ_USAGEFAULT (6) /* Vector 6: Usage fault */ #define STM32_IRQ_SVCALL (11) /* Vector 11: SVC call */ #define STM32_IRQ_DBGMONITOR (12) /* Vector 12: Debug Monitor */ /* Vector 13: Reserved */ #define STM32_IRQ_PENDSV (14) /* Vector 14: Pendable system service request */ #define STM32_IRQ_SYSTICK (15) /* Vector 15: System tick */ External interrupts (vectors >= 16) 外部中斷,設定在nuttx/arch/arm/src/stm32/chip/stm32f40xxx_vectors.h .. code-block:: c //nuttx/arch/arm/include/stm32/stm32f40xxx_irq.h #define STM32_IRQ_WWDG (STM32_IRQ_INTERRUPTS+0) /* 0: Window Watchdog interrupt */ #define STM32_IRQ_PVD (STM32_IRQ_INTERRUPTS+1) /* 1: PVD through EXTI Line detection interrupt */ #define STM32_IRQ_TAMPER (STM32_IRQ_INTERRUPTS+2) /* 2: Tamper and time stamp interrupts */ #define STM32_IRQ_TIMESTAMP (STM32_IRQ_INTERRUPTS+2) /* 2: Tamper and time stamp interrupts */ #define STM32_IRQ_RTC_WKUP (STM32_IRQ_INTERRUPTS+3) /* 3: RTC global interrupt */ #define STM32_IRQ_FLASH (STM32_IRQ_INTERRUPTS+4) /* 4: Flash global interrupt */ . . . 如何設定Interrupt Handler? .. code-block:: c //irq_attach(STM32_IRQ_SVCALL, up_svcall); //System Call //nuttx/sched/irq_attach.c int irq_attach(int irq, xcpt_t isr) { state = irqsave(); //disables all interrupts if(isr == NULL) { up_disable_irq(irq); isr = irq_unexpected_isr; } g_irqvector[irq] = isr; //Vector Table irqrestore(state); ret = ok; } Interrupt Handling的分層架構 1. low-level logic, arch/arm/src/armv7-m/up_exception.S, 接收Interrupt並決定IRQ number 2. That low-level logic than calls some MCU-specific, intermediate level function up_doirq(), mask and acknowledge the interrupt, dispatch, unmask 3. That MCU-specific function then calls the NuttX common interrupt dispatching logic irq_dispatch() STM32_IRQ_SYSTICK .. code-block:: c //(void)irq_attach(STM32_IRQ_SYSTICK, (xcpt_t)up_timerisr); //up_exception.S -> up_doirq(int irq, uint32_t *regs)-> irq_dispatch(int irq, FAR void *context) -> up_timerisr(int irq, uint32_t *regs) -> sched_process_timer(void) -> sched_process_timeslice() static void sched_process_timeslice(void) { if((rtcb->flags & TCB_FLAG_ROUND_ROBIN) != 0) { if(rtcb->timeslice <= 1) { if(!rtcb->lockcount) { rtcb->timeslice = CONFIG_RR_INTERVAL/MSEC_PER_TICK; if (rtcb->flink && rtcb->flink->sched_priority >= rtcb->sched_priority) { up_reprioritize_rtr(rtcb, rtcb->sched_priority); } } } else { rtcb->timeslice--; } } } Configuring High Priority Interrupts How do you specify a high priority interrupt? if CONFIG_ARCH_RAMVECTORS=y, then vectors will be kept in RAM and the system will support the interface int up_ramvec_attach(int irq, up_vector_t vector). That interface can be used to attach your C interrupt handler to the vector at run time. .. code-block:: c //up_vector_t g_ram_vectors[ARMV7M_VECTAB_SIZE] __attribute__((section(".ram_vectors"))); //Using a RAM-based vector table //void up_ramvec_initialize(void) //Copy vectors to RAM an configure the NVIC to use the RAM vectors. int up_ramvec_attach(int irq, up_vector_t vector) //Configure the ram vector table so that IRQ number 'irq' will be dipatched by hardware to 'vector' { int ret = ERROR; if ((unsigned)irq < ARMV7M_PERIPHERAL_INTERRUPTS) { irqstate_t flags; flags = irqsave(); if (vector == NULL) { up_disable_irq(irq); vector = exception_common; } g_ram_vectors[irq] = vector; irqrestore(flags); ret = OK; } return ret; } FPU ------- | arch/arm/src/armv7-m/up_fpu.S | 儲存 fpu 的 context switch .. code-block:: c up_savefpu: add r1, r0, #(4*REG_S0) /* R1=Address of FP register storage */ /* Some older GNU assemblers don't support all the newer UAL mnemonics. */ #if 1 /* Use UAL mnemonics */ /* Store all floating point registers. Registers are stored in numeric order, * s0, s1, ... in increasing address order. */ vstmia r1!, {s0-s31} /* Save the full FP context */ /* Store the floating point control and status register. At the end of the * vstmia, r1 will point to the FPCSR storage location. */ vmrs r2, fpscr /* Fetch the FPCSR */ str r2, [r1], #4 /* Save the floating point control and status register */ #else /* Store all floating point registers */ #if 1 /* Use store multiple */ fstmias r1!, {s0-s31} /* Save the full FP context */ #else vmov r2, r3, d0 /* r2, r3 = d0 */ str r2, [r1], #4 /* Save S0 and S1 values */ str r3, [r1], #4 vmov r2, r3, d1 /* r2, r3 = d1 */ str r2, [r1], #4 /* Save S2 and S3 values */ str r3, [r1], #4 vmov r2, r3, d2 /* r2, r3 = d2 */ str r2, [r1], #4 /* Save S4 and S5 values */ str r3, [r1], #4 vmov r2, r3, d3 /* r2, r3 = d3 */ str r2, [r1], #4 /* Save S6 and S7 values */ str r3, [r1], #4 vmov r2, r3, d4 /* r2, r3 = d4 */ str r2, [r1], #4 /* Save S8 and S9 values */ str r3, [r1], #4 vmov r2, r3, d5 /* r2, r3 = d5 */ str r2, [r1], #4 /* Save S10 and S11 values */ str r3, [r1], #4 vmov r2, r3, d6 /* r2, r3 = d6 */ str r2, [r1], #4 /* Save S12 and S13 values */ str r3, [r1], #4 vmov r2, r3, d7 /* r2, r3 = d7 */ str r2, [r1], #4 /* Save S14 and S15 values */ str r3, [r1], #4 vmov r2, r3, d8 /* r2, r3 = d8 */ str r2, [r1], #4 /* Save S16 and S17 values */ str r3, [r1], #4 vmov r2, r3, d9 /* r2, r3 = d9 */ str r2, [r1], #4 /* Save S18 and S19 values */ str r3, [r1], #4 vmov r2, r3, d10 /* r2, r3 = d10 */ str r2, [r1], #4 /* Save S20 and S21 values */ str r3, [r1], #4 vmov r2, r3, d11 /* r2, r3 = d11 */ str r2, [r1], #4 /* Save S22 and S23 values */ str r3, [r1], #4 vmov r2, r3, d12 /* r2, r3 = d12 */ str r2, [r1], #4 /* Save S24 and S25 values */ str r3, [r1], #4 vmov r2, r3, d13 /* r2, r3 = d13 */ str r2, [r1], #4 /* Save S26 and S27 values */ str r3, [r1], #4 vmov r2, r3, d14 /* r2, r3 = d14 */ str r2, [r1], #4 /* Save S28 and S29 values */ str r3, [r1], #4 vmov r2, r3, d15 /* r2, r3 = d15 */ str r2, [r1], #4 /* Save S30 and S31 values */ str r3, [r1], #4 #endif /* Store the floating point control and status register */ fmrx r2, fpscr /* Fetch the FPCSR */ str r2, [r1], #4 /* Save the floating point control and status register */ #endif bx lr .size up_savefpu, .-up_savefpu | 載回 fpu 的 context switch .. code-block:: c up_restorefpu: add r1, r0, #(4*REG_S0) /* R1=Address of FP register storage */ /* Some older GNU assemblers don't support all the newer UAL mnemonics. */ #if 1 /* Use UAL mnemonics */ /* Load all floating point registers. Registers are loaded in numeric order, * s0, s1, ... in increasing address order. */ vldmia r1!, {s0-s31} /* Restore the full FP context */ /* Load the floating point control and status register. At the end of the * vstmia, r1 will point to the FPCSR storage location. */ ldr r2, [r1], #4 /* Fetch the floating point control and status register */ vmsr fpscr, r2 /* Restore the FPCSR */ #else /* Load all floating point registers Registers are loaded in numeric order, * s0, s1, ... in increasing address order. */ #if 1 /* Use load multiple */ fldmias r1!, {s0-s31} /* Restore the full FP context */ #else ldr r2, [r1], #4 /* Fetch S0 and S1 values */ ldr r3, [r1], #4 vmov d0, r2, r3 /* Save as d0 */ ldr r2, [r1], #4 /* Fetch S2 and S3 values */ ldr r3, [r1], #4 vmov d1, r2, r3 /* Save as d1 */ ldr r2, [r1], #4 /* Fetch S4 and S5 values */ ldr r3, [r1], #4 vmov d2, r2, r3 /* Save as d2 */ ldr r2, [r1], #4 /* Fetch S6 and S7 values */ ldr r3, [r1], #4 vmov d3, r2, r3 /* Save as d3 */ ldr r2, [r1], #4 /* Fetch S8 and S9 values */ ldr r3, [r1], #4 vmov d4, r2, r3 /* Save as d4 */ ldr r2, [r1], #4 /* Fetch S10 and S11 values */ ldr r3, [r1], #4 vmov d5, r2, r3 /* Save as d5 */ ldr r2, [r1], #4 /* Fetch S12 and S13 values */ ldr r3, [r1], #4 vmov d6, r2, r3 /* Save as d6 */ ldr r2, [r1], #4 /* Fetch S14 and S15 values */ ldr r3, [r1], #4 vmov d7, r2, r3 /* Save as d7 */ ldr r2, [r1], #4 /* Fetch S16 and S17 values */ ldr r3, [r1], #4 vmov d8, r2, r3 /* Save as d8 */ ldr r2, [r1], #4 /* Fetch S18 and S19 values */ ldr r3, [r1], #4 vmov d9, r2, r3 /* Save as d9 */ ldr r2, [r1], #4 /* Fetch S20 and S21 values */ ldr r3, [r1], #4 vmov d10, r2, r3 /* Save as d10 */ ldr r2, [r1], #4 /* Fetch S22 and S23 values */ ldr r3, [r1], #4 vmov d11, r2, r3 /* Save as d11 */ ldr r2, [r1], #4 /* Fetch S24 and S25 values */ ldr r3, [r1], #4 vmov d12, r2, r3 /* Save as d12 */ ldr r2, [r1], #4 /* Fetch S26 and S27 values */ ldr r3, [r1], #4 vmov d13, r2, r3 /* Save as d13 */ ldr r2, [r1], #4 /* Fetch S28 and S29 values */ ldr r3, [r1], #4 vmov d14, r2, r3 /* Save as d14 */ ldr r2, [r1], #4 /* Fetch S30 and S31 values */ ldr r3, [r1], #4 vmov d15, r2, r3 /* Save as d15 */ #endif /* Load the floating point control and status register. r1 points t * the address of the FPCSR register. */ ldr r2, [r1], #4 /* Fetch the floating point control and status register */ fmxr fpscr, r2 /* Restore the FPCSR */ #endif bx lr .size up_restorefpu, .-up_restorefpu #endif /* CONFIG_ARCH_FPU */ | /nuttx/arch/arm/include/armv7-m/irq.h | struct xcptcontext | /nuttx/arch/arm/include/armv7-m/irq_lazyfpu.h | XCPTCONTEXT_REGS = HW_XCPT_REGS + SW_XCPT_REGS | XCPTCONTEXT_SIZE = 4 * XCPTCONTEXT_REGS | 共會存入51個暫存器 | 其中8個為硬體一般暫存器包含R0 to R3, R12, R14 = LR, R15 = PC, XPSR | 其中軟體暫存器浮點數FP共33個 S0 to S31, FPSCR | 原本10個軟體一般暫存器 R13 = SP, PRIMASK, R4 to R11 | 依序存入XCPT由software registers SP開始到R11,浮點數S0到FPSCR,再來是 hardware registers R0到XPSR 測試環境架設 ------------ 硬體驅動原理 ------------ * `UART`_ * FPU (Floating Point Unit) 效能表現 -------- 參考資料 -------- * https://github.com/deadbeefcafe/nuttx-stm32f4disc-bb * http://bibliodigital.itcr.ac.cr/xmlui/bitstream/handle/2238/3051/FinalReport.pdf