Welcome to AVR32 Linux... Users Groups
- CarlosRibeiro - 01 Feb 2008

NGW100 Interrupts

Here I'll try to show how to decode a bit stream from a weather sensor using interrupts.

Sensor:

WS2300-15 - From La Crosse Technology
ws2300_15.jpg

Connector:

4positionjack.gif:
(male connector front view)
1 - DATA (from anemometer)
2 - VDD supply voltage ( don't know the range but for safety keep it between 2V and 5V )
3 - /ENABLE active low (input)
4 - GND

DATA format

25 bit with ~1.2 ms each bit
Inverted logic (3.3V is 0 and 0v is 1)
Start header (4bits, 00100)
Direction (4 bits, value from 0 to 15, LSB first)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
N NNE NE ENE E ESE SE SSE S SSO SO OSO O ONO NO NNO
Wind Speed counter (12 bits, number of anemometer revolutions in last 2 seconds,LSB first, 0-4096, non accumulating)
CRC (4 bits, calculated as dddd + nnnn + nnnn + nnnn truncated to 4 bits)

Other info

When enabled (pin 3 held at 0V) the sensor will send the stream every 2 seconds.
Complete stream 00100 dddd nnnn nnnn nnnn cccc, where d is direction, n is revolutions and c is CRC
Velocity count appears somewhat nonlinear, the turbine is quite sensitive to small gusts below 4 knots.
Knots = revolutions/8 (not sure of this i needs more detailed experiments)
warning.gif I'm not responsible for this information, this information is provided has he is, it is not official it was reverse engineered!.

Decoding the bit stream

Using IRQ - EXTINT0 PB25, J6 pin 25:


/*
 * Driver for decoding WS2300-15 la crosse ANEMOMETER
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/gpio.h>
#include <asm/system.h>
#include <asm/arch/at32ap7000.h>
#include "chardev.h"

#define PORTB   (void*)0xffe02C00
#define PIO_ISR 0x004c

#define PIN GPIO_PIN_PB(25)
#define PIN_IRQ  gpio_to_irq(PIN)

int8_t dir;
unsigned int count;
const char str_dir[][5]  = { "N","NNE","NE","ENE","E","ESE","SE","SSE",
                             "S","SSO","SO","OSO","O","ONO","NO","NNO" };

#define max_m 15 // number of values to average, 15 = 30 seconds

int8_t index=0;
int8_t       m_dir[max_m];
unsigned int m_count[max_m];
int debug = 0;

module_param(debug,int,0);

//get averaged count value
int get_count(void)
{ int x; unsigned int tmp=0;
  for (x=0;x<=max_m;x++)
  {
   tmp=tmp+m_count[x];
  }
 return (int)(tmp/max_m);
}

// get numerical direction 
int get_ndir(void)
{ int x;
  int count[15] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
  int dir=0;

 for (x=0;x<=max_m;x++)
  {
    count[m_dir[x]]++;
  }
 for (x=0;x<=15;x++)
  {
   if (count[x]>dir) dir=x;
  }
 return dir;
}

// get direction string
const char *get_dir(void)
{
 return str_dir[get_ndir()];
}

// IRQ routine 
static irqreturn_t read_stream(int irq, void *dev_id)
{
 int u;
 unsigned int acc=0;
 unsigned int bn=0;
 int8_t crc;

 dir =0; count=0;

for(u=0; u<=24; u++)
  {
   udelay(600); // delaying inside irq is very bad, the system will "die" during delay ...
     if(gpio_get_value(PIN)==0) 
        { acc |= (1<> 4)%0xf)  + ((count >> 8)&0xf)) & 0xf ;

 if (index>max_m) { 
  index=0; 
 }

 if (debug)
  {
   printk(KERN_ALERT "STREAM dir:%s count:%i\n",pontos[dir],count);
   printk(KERN_ALERT "CRC:%i=%i\n",acc,crc);
  }

  __raw_readl( PORTB + PIO_ISR ); // flush IRQ to prevent loop

 return IRQ_HANDLED;
}

static int __init ws2300_init(void)
{
        unsigned int ret;

        printk(KERN_ALERT "WS2300-15 Module started\n");
        printk(KERN_ALERT "WS2300-15 Requesting GPIO %d\n",PIN);
        printk(KERN_ALERT "WS2300-15 Requesting Irq %d\n",PIN_IRQ);

        ret = gpio_request(PIN, "blah");
        if (ret < 0) {
                printk(KERN_ALERT "error %d: could not request gpio: %d\n", ret,PIN);
                return ret;
        }

        if (request_irq(PIN_IRQ, read_stream, 0, "ws2300-15", NULL)) {
                printk(KERN_ALERT "error %d: could not request irq: %d\n", -EBUSY,PIN);
                return -EBUSY;
        }

   ret = gpio_direction_input(PIN);
   if (ret < 0) {
      printk(KERN_ALERT "could not set gpio direction: %d\n", PIN);
      gpio_free(PIN);
      return ret;
   } 

/*
   init_chardev();
   
I use a char device (/dev/ws2300) to read the values from this module and generate graphics with rrdtool.
Complete station uses, lighthttp, php, sqlite3 and rrdtool
 Use get_count, get_ndir and get_dir to collect data.
*/

 for (ret=0;ret<=max_m;ret++)
  {
   m_dir[ret]=0;
   m_count[ret]=0;
  }

        return 0;
} 

static void __exit ws2300_exit(void)
{
        printk(KERN_ALERT "exit : removing irq: %d\n",PIN_IRQ);
        printk(KERN_ALERT "exit : removing button: %d\n",PIN);
        free_irq(PIN_IRQ,  NULL);
        gpio_free(PIN);
} 

module_init(ws2300_init);
module_exit(ws2300_exit);

MODULE_AUTHOR("Carlos Ribeiro");
MODULE_DESCRIPTION("WS2300-15 Stream Decode using EXTINT0");
MODULE_LICENSE("GPL");

Using TIMER1 - Channel 0 , TIOB as TRIGGER PA25, J15 pin 17:

warning.gif HELP Not FINISHED yet, work in progress!!
#include <linux/module.h>
#include <asm/io.h>
#include <asm/arch/portmux.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/arch/time.h>
#include <asm/arch/at32ap7000.h>
#include <asm/gpio.h>
#include <asm/arch/board.h>
#include <asm/arch/portmux.h>


#define PORTA (void*)0xffe02800
#define PORTB (void*)0xFFE02C00

#define PIO_PER 0x0000
#define PIO_PDR 0x0004
#define ASR 0x0070
#define BSR 0x0074
#define PIO_OER 0x0010
#define PIO_SODR 0x0030
#define PIO_CODR 0x0034

#define PM (void*)0xfff00000
#define PWM_BASE (void*)0xfff01400
#define TC1_BASE (void*)0xfff01000

#define PBBMASK_OFFSET 0x14
#define PWM_CLK_EN_BIT 5
#define TC1_CLK_EN_BIT 4


#define CCR_OFFSET 0x00
#define CMR_OFFSET 0x04
#define CV_OFFSET 0x10
#define RA_OFFSET 0x14
#define RB_OFFSET 0x18
#define RC_OFFSET 0x1C
#define SR_OFFSET 0x20
#define IRQ_ENA_OFFSET 0x24
#define IRQ_DIS_OFFSET 0x28
#define IMR_OFFSET 0x28


#define CMR0_OFFSET 0x200
#define CPRD0_OFFSET 0x208
#define CDTY0_OFFSET 0x204

#define PWM_ENA_OFFSET 0x4
#define PWM_DIS_OFFSET 0x8

#define CLOCK_SOFT_TRIGGER __raw_writel(5, TC1_BASE + CCR_OFFSET)

#define IN_PIN GPIO_PIN_PA(25)

static int bit_count = 0;
static ulong stream =0;

static irqreturn_t timer_int(int irq, void *dev_id)
{ unsigned long mode;

 if(gpio_get_value(IN_PIN)==0)
   stream |= (1<< bit_count);

 if (bit_count==0)
  {
        __raw_writel((1 << 4 ), TC1_BASE + IRQ_ENA_OFFSET);
        mode = (1 << 2) | 1 << 14 | 1 << 15; // disable ext trigger int
        __raw_writel(mode, TC1_BASE + CMR_OFFSET);
  }
 if (bit_count==1)         
   __raw_writel(0X9C0, TC1_BASE + RC_OFFSET); // load RC

  if (bit_count==24)
  {    // stream ended, prepare for new stream and set ext trigger
        __raw_writel(2, TC1_BASE + CCR_OFFSET);    // stop counter
        mode = (1 << 2) | 1 << 8| 1 << 12| 1 << 14 | 1 << 15;
        __raw_writel(mode, TC1_BASE + CMR_OFFSET); // define ext trigger start
        __raw_writel(( 1 << 7 )|(1 << 4 ), TC1_BASE + IRQ_ENA_OFFSET); // irq masq 
        __raw_writel(0XF00, TC1_BASE + RC_OFFSET); // load RC
        __raw_writel(1, TC1_BASE + CCR_OFFSET);    // enable counter

        bit_count=0;
        printk("Stream: %lu",stream); stream=0;
         __raw_readl(TC1_BASE + SR_OFFSET); // clear irq flag
         return IRQ_HANDLED;  
  } 
  bit_count++;

  __raw_readl(TC1_BASE + SR_OFFSET); // clear irq flag
 return IRQ_HANDLED;
}


void tc1_enable(void)
 { u32 mask;
        mask = __raw_readl(PM+PBBMASK_OFFSET);
         mask |= 1 << TC1_CLK_EN_BIT;
        __raw_writel(mask, PM+PBBMASK_OFFSET);
 }

void tc1_disable(void)
 { u32 mask;
        mask = __raw_readl(PM+PBBMASK_OFFSET);
         mask &= ~(1 << TC1_CLK_EN_BIT);
       __raw_writel(mask, PM+PBBMASK_OFFSET );
 }

int init_module(void)
{ unsigned long u,mode;
  int res;

        __raw_writel( (1 << 25) , PORTA + PIO_PDR); // periferico
        __raw_writel( (1 << 25) , PORTA + BSR); // periferico B

   res = gpio_direction_input(IN_PIN);
   if (res < 0) {
      printk(KERN_ALERT "could not set gpio direction: %d\n", IN_PIN);
      gpio_free(IN_PIN);
      return res;
   }

   res = request_irq(23, timer_int, 0, "atmel_timer", NULL);
   if (res) {
      printk (KERN_ALERT "Error request_irq");
      return res;
   }

   tc1_enable();
   /* TC1 section */
   /* CLK is TIMER_CLOCK5, ie, PBB_CLK/32 */
   mode = (1 << 2) | 1 << 8| 1 << 12| 1 << 14 | 1 << 15;
   __raw_writel(mode, TC1_BASE + CMR_OFFSET);
   __raw_writel(0XF00, TC1_BASE + RC_OFFSET); // load RC
   // enable irq on, compare and extint on TIOB0
   __raw_writel(( 1 << 7 )|(1 << 4 ), TC1_BASE + IRQ_ENA_OFFSET);

    /* enable TC1 unit */
    __raw_writel(1, TC1_BASE + CCR_OFFSET);

 return 0;
}

void cleanup_module(void)
{
        __raw_writel(2, TC1_BASE + CCR_OFFSET); // stop timer
        __raw_writel(255, TC1_BASE + IRQ_DIS_OFFSET); // disable timer...
        tc1_disable(); // disable timer clock ...
        free_irq(23, NULL);
}

MODULE_AUTHOR("Carlos Ribeiro");
MODULE_DESCRIPTION("WS2300-15 Stream Decode using TIMER1 chanel 0, TIOB as Trigger");
MODULE_LICENSE("GPL");

Results

wind.png
Samples every 30 seconds averaged to 1 minute, Gust on last 5 min.

Live Link

http://coisas.mine.nu/cgi-bin/ws2300

6 visitor(s) this week :

  File Size Date By Actions
gif 4positionjack.gif
RJ11 Pinout
1.3 K 2008-02-01 - 02:51 CarlosRibeiro props, move
gif warning.gif
 
0.2 K 2008-02-01 - 03:34 CarlosRibeiro props, move
png wind.png
Wind Speed
88.6 K 2008-02-01 - 04:51 CarlosRibeiro props, move
jpg ws2300_15.jpg
WS2300-15
2.6 K 2008-02-01 - 02:51 CarlosRibeiro props, move
r5 - 2008-10-21 - 19:48:39 - CarlosRibeiro
Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Linux® is the registered trademark of Linus Torvalds in the U.S. and other countries.
Atmel®, AVR® and others are registered trademarks or trademarks of Atmel Corporation or its subsidiaries.
All other trademarks are the property of their respective owners.
Syndicate this site RSSATOM