分享到plurk 分享到twitter 分享到facebook

版本 1bccbebacd0a153f1078e3f8944516ea3f125a7e

embedded/nuttx

Changes from 1bccbebacd0a153f1078e3f8944516ea3f125a7e to 8778fed90eaec98a26f1dde3983857c41bc712b9

---
title: nuttx
categories: embedded, arm, stm32, stm32f429
toc: no
...

組員
----
* 翁振育
* 黃亮穎
* 蘇偉嘉
* 李英碩

hackpad : https://stm32f429.hackpad.com/NuttX--hqco2YSgAcJ

Nuttx 參考應用
---------------
* `PX4 autopilot<http://pixhawk.org/dev/nuttx/start>`_

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_open:連結task與named semaphore,如果semaphore不存在,呼叫sem_init
 -  sem_init:初始化一個unnamed semaphore
 -  sem_wait:當一個task想要semaphore的時候會呼叫這個函數,它會先將counter - 1,如果counter = 0,可以執行,呼叫sem_addholder,若counter < 0,代表已經有別的task在執行,那麼它必須等待到semaphore被放出來為止
 -  sem_post:當一個task執行完的時候會呼叫,它會先將counter + 1,如果counter <= 0,代表有別的task還在等待,呼叫waiting list 的head
 - sem_unlink:移除named semaphore,如果還有task connect to semaphore,不會作用
 - sem_close:從記憶體當中將已經unlink的semaphore殺掉

 | Mutex : 與semaphore用同一組資料結構,最主要的差別在於semaphore處理task,mutex處理pthread
 | Mutex : 與semaphore用同一組資料結構,mutex處理pthread

 - pthread_mutexinit:create mutex
 - pthread_mutexlock: 當pthread要執行的時候會呼叫,
 
 - pthread_mutexlock: 當pthread要執行的時候會呼叫,如果thread已經hold semaphore,檢查是否是recursive,是,mutex->nlocks++,否,error。如果 thread還沒hold semaphore,呼叫pthread_takesemaphore
 - pthread_mutexunlock:當pthread跑完的時候會呼叫,它會先檢查thread是否hold semaphore,再檢查是否是recursive & 還有沒有別的pthread在等,若是,mutex->nlocks - -,否,呼叫post 


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。

Timer interrupt
----------------------

| /nuttx/arch/arm/src/stm32/stm32_vectors.S 
| 
| processor exception


.. code-block:: c 

	.word	IDLE_STACK			/* Vector  0: Reset stack pointer */
	.word	__start				/* Vector  1: Reset vector */
	.word	stm32_nmi			/* Vector  2: Non-Maskable Interrupt (NMI) */
	.word	stm32_hardfault		/* Vector  3: Hard fault */
	.word	stm32_mpu			/* Vector  4: Memory management (MPU) */
	.word	stm32_busfault		/* Vector  5: Bus fault */
	.word	stm32_usagefault	/* Vector  6: Usage fault */
	.word	stm32_reserved		/* Vector  7: Reserved */
	.word	stm32_reserved		/* Vector  8: Reserved */
	.word	stm32_reserved		/* Vector  9: Reserved */
	.word	stm32_reserved		/* Vector 10: Reserved */
	.word	stm32_svcall		/* Vector 11: SVC call */
	.word	stm32_dbgmonitor	/* Vector 12: Debug monitor */
	.word	stm32_reserved		/* Vector 13: Reserved */
	.word	stm32_pendsv		/* Vector 14: Pendable system service request */
	.word	stm32_systick		/* Vector 15: System tick */

/nuttx/arch/arm/src/stm32/stm32_timerisr.c (in line 109 )

.. code-block:: c 

 int up_timerisr(int irq, uint32_t *regs)
 {
     /* Process timer interrupt */
     sched_process_timer();
     return 0;
  }


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)
 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_INTERVALMSEC_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


 
測試環境架設
------------
 
硬體驅動原理
------------
* `UART<embedded/USART>`_
* FPU (Floating Point Unit)

效能表現
--------

參考資料
--------
* https://github.com/deadbeefcafe/nuttx-stm32f4disc-bb
* http://bibliodigital.itcr.ac.cr/xmlui/bitstream/handle/2238/3051/FinalReport.pdf