091030

|


菜園状況。ニンジンの間引きをしました。根の伸びがいい気がする。



ロガー続き。GPSまわり。GPS-52モジュールからはNMEA-0183フォーマットでシ リアルに送られてくる。これをパースして必要なデータだけパックしてファイ ルに書き込みたい。まずはパース。パースは苦手だ。なんかいつも場当たり的 なコードになってしまう。
ここはマイOSだ。富豪的にファイバを使おう!ということにしたのだけど、やっ ぱりこれは自分で自分の足を撃ち抜くような機構だ。デバッグするのに脳ミソ もつれるかと思った。やれるからといってやってはいけない悪夢のようなコー ド。とはいえおもしろい.
ここでfiber_yield, fiber_return_parentは
これはARM
FUNC (fiber_switch)
	//
	// save context. (from)
	stmia	r0!,	{r4-r14}
	// subroutine return value/1st arg
	mov	r0,	r2
	ldmia	r1,	{r4-r14}
	mov	pc,	lr
これはH8
	.globl _fiber_switch
_fiber_switch:
	; save current context.
	; no need to store caller saved and ccr.
	mov.l	er7,	@(0x0c, er0) ; sp (contain return address)
	mov.l	er6,	@(0x08, er0) ; callee saved
	mov.l	er5,	@(0x04, er0) ; callee saved
	mov.l	er4,	@(0x00, er0) ; callee saved
	; load next context.
	mov.l	@(0x0c, er1), er7 ; sp
	mov.l	@(0x08, er1), er6 ; callee saved
	mov.l	@(0x04, er1), er5 ; callee saved
	mov.l	@(0x00, er1), er4 ; callee saved
	mov.l	er2,	er0	  ; subroutine return value / 1st arg.
	rts
これはx86
	.code32
FUNC (fiber_switch)
	//
	// save context. (from)
	movl	%esp, 0x10 (%eax)
	movl	%ebp, 0x0c (%eax)
	movl	%edi, 0x08 (%eax)
	movl	%esi, 0x04 (%eax)
	movl	%ebx, 0x00 (%eax)
	// load next context (to)
	movl	0x10 (%edx), %esp
	movl	0x0c (%edx), %ebp
	movl	0x08 (%edx), %edi
	movl	0x04 (%edx), %esi
	movl	0x00 (%edx), %ebx
	// subroutine return value/1st arg
	movl	%ecx,	%eax
	ret

データを見てみると受信がかなり不安定だし、受信データもバラつき大きいし (ノイズか?)今回のチャレンジは失敗に終わりそう。
#include <system.h>
#include <console.h>
#include <thread.h>
#include <fiber.h>
#include <delay.h>
#include <reg.h>
#include <string.h>

#include "local.h"
#include "timer.h"

STATIC uint8_t gps_tls[THREAD_STACK_SIZE (GPS_STACK_SIZE)]
__attribute ((aligned (4)));
STATIC void gps_thread (uint32_t);
STATIC int gps_thread_ready;

#define	FIFO_SIZE	64
#define	GPS_FIBER_STACK_SIZE	256

rbuf_nolock_t __recv_buf;
STATIC uint8_t buf_intr[RBUF_NOLOCK_SIZE (FIFO_SIZE)];
STATIC uint8_t buf_thread[FIFO_SIZE];
volatile size_t buf_thread_read_size;
uint8_t *buf_thread_read_p = buf_thread;

thread_t gps_th;

fiber_t fiber[3];
uint8_t fls[2][FIBER_STACK_SIZE (GPS_FIBER_STACK_SIZE)];
fiber_func gps_parser;
fiber_func gps_parser_2;

__BEGIN_DECLS
void uart3_intr (void);
uint8_t gps_getc (fiber_t);
__END_DECLS

GPS用のスレッドを作成します。
thread_t
gps_init ()
{
  thread_t th;

  th = thread_create (gps_tls, GPS_STACK_SIZE, "gps", gps_thread, 0);
  thread_start (th);

  while (!gps_thread_ready)
    thread_sleep (current_thread);

  return th;
}

void
gps_thread (uint32_t arg __attribute__((unused)))
{
  struct fiber myself;
  gps_th = current_thread;

  // PCLK 18.0000MHz 9615bps error 0.16%
  struct uart_clock_conf uart3 = { 0, 78, 1 | (2 << 4), CCLK4 };
  uart_init (UART3, &uart3, TRUE);
UART3の割り込みハンドラとスレッドコンテキストとのデータのやりとりに
リングバッファを用意します。
  // Interrupt context ring-buffer.
  __recv_buf = rbuf_nolock_init (buf_intr, FIFO_SIZE);

パースのためのファイバを用意します。fiber[0]はこのスレッド自分自身。
  // Create parser fiber.
  fiber[0] = fiber_init (&myself);
  fiber[1] = fiber_create (&myself, fls[0], GPS_FIBER_STACK_SIZE, gps_parser);
  fiber[2] = fiber_create (&myself, fls[1], GPS_FIBER_STACK_SIZE, gps_parser_2);
fiber_yieldした時の連結順番を設定します。
  fiber_twist (&myself, 2, fiber[1], fiber[2]);
ファイバをスタートします。
  fiber_start (&myself, fiber[1]);
  fiber_start (&myself, fiber[2]);

  // Now OK to log.
  gps_thread_ready = TRUE;
  thread_wakeup (shell_th);
  while (/*CONSTCOND*/1)
    {
      thread_sleep (current_thread);
受信割り込みから起こされるので、リングバッファにある内容をコピーします。
      cpu_status_t s = intr_suspend ();
      buf_thread_read_size = rbuf_nolock_read (__recv_buf, buf_thread, FIFO_SIZE);
      intr_resume (s);
今回読み込んだデータが処理し終わるまでファイバを廻します。
      buf_thread_read_p = buf_thread;
      while (buf_thread_read_size)
	{
	  fiber_yield (&myself, 0);
	}
    }
  // NOTREACHED
}

void
uart3_intr ()
{
  uint8_t c = *URBR (UART3_BASE);

  if (rbuf_nolock_write (__recv_buf, &c, 1) != 1)
    {
      iprintf ("UART3 overflow\n");
    }

割り込みが来たら内容をリングバッファに入れて、後処理スレッドを起こしま
す。ある程度遅延をかければその遅延の間に入った割り込みのデータをバッファ
に追加して後処理スレッドで一気に処理できます。
  timer_schedule_func ((void (*)(void *))thread_wakeup_once, gps_th, 100);
}


uint8_t gps_buf[64];

void FIBER_FUNC_ATTRIBUTE
gps_parser (fiber_t myself)
{
ここは親がfiber_startした時に処理されます。
  fiber_return_parent (myself, 0);
ここからは次のfiber_yieldから処理されます。
  // Next yield start here.
  uint8_t c;
  uint8_t *p = gps_buf;
  while (/*CONSTCOND*/1)
    {
      c = gps_getc (myself);
      *p++ = c;
      if ((c == ',') || (c == '\r') || (c == '\n'))
	{
	  *--p = '\0';
	  p = gps_buf;
区切り文字が来たら、そのバッファを次のファイバに送ります。
	  fiber_yield (myself, 0);
	}
    }
  // NOTREACHED
}

uint8_t
gps_getc (fiber_t self)
{
バッファがなくなれば、そのまま親に帰ります。また親にバッファがあればこ
こから初まるので、この関数の呼び出し先は連続してバッファが供給されるよ
うに見えます。
  if (buf_thread_read_size == 0)
    {
この場合、予定された次のファイバではなく、親に直接戻ってデータを待ちま
す。
      fiber_return_parent (self, 0);
    }
  assert (buf_thread_read_size);
  --buf_thread_read_size;

  return *buf_thread_read_p++;
}


void FIBER_FUNC_ATTRIBUTE
gps_parser_2 (fiber_t myself)
{
  bool target = FALSE;
  int gga_cnt = 0;
  fiber_return_parent (myself, 0);
  // Next yield start here.

  while (/*CONSTCOND*/1)
    {
このファイバにはgps_bufにトークンが用意された状態でまわってきます。
$GPGGAの、東経、北緯、受信状況、高度だけ表示してます。
      if (gps_buf[0] == '$')
	{
	  if ((target = strcmp ((const char *)gps_buf, "$GPGGA") == 0))
	    {
	      iprintf ("\n");
	      gga_cnt = 0;
	    }
	}
      if (target)
	{
	  if ((gga_cnt == 2)/*latitude*/ || (gga_cnt == 4) /*longitude*/||
	      (gga_cnt == 6)/*valid*/ || (gga_cnt == 9)/*altitude*/)
	    {
	      iprintf ("%s ", gps_buf);
	    }
	  gga_cnt++;
	}
これは親に戻ります。
      fiber_yield (myself, 0);
    }
  // NOTREACHED
}

/*
GPGSA
 GNSS DOP and Active Satellites

$GPGSA,A,3,23,20,11,13,,,,,,,,,07.3,04.2,05.9*0D

  Satellites in View
$GPGSV,3,1,09,17,83,311,00,20,42,046,42,04,39,294,26,23,38,099,41*76
$GPGSV,3,2,09,13,27,143,38,28,23,203,00,11,18,092,41,32,17,043,*76
$GPGSV,3,3,09,02,09,280,00*41

  Recommended Minimum Specific GNSS Data
$GPRMC,084249.695,A,3539.4058,N,13939.2779,E,0000.00,344.21,251009,,*3A

  Course Over Ground and Ground Speed
$GPVTG,344.21,T,,M,0000.00,N,0000.00,K*50

  Time & Date
$GPZDA,084250.695,25,10,2009,,*5A

  Global Positioning System Fix Data
$GPGGA,084250.694,3539.4058,N,13939.2779,E,1,04,04.2,00039.2,M,0039.4,M,000.0,0000*49

$GPGGA,
084250.694, UTC
3539.4058,N,
13939.2779,E,
1,		Quality (0: can't receive)
04,		# of Satellites
04.2,		HDOP
00039.2,M,	altitude
0039.4,M,	altitude WGS-84
000.0,		Data age
0000		DPGS ID
*49		checksum


*/