streaming server for AM/FM radio via UDP connection.

Dependencies:   mbed EthernetNetIf

Files at this revision

API Documentation at this revision

Comitter:
soramimi
Date:
Mon Feb 28 11:49:39 2011 +0000
Child:
1:3357273c97f8
Commit message:

Changed in this revision

EthernetNetIf.lib Show annotated file Show diff for this revision Revisions of this file
HTTPServer.lib Show annotated file Show diff for this revision Revisions of this file
RadioServer.cpp Show annotated file Show diff for this revision Revisions of this file
TextLCD.lib Show annotated file Show diff for this revision Revisions of this file
crc32.c Show annotated file Show diff for this revision Revisions of this file
crc32.h Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
ns9542.c Show annotated file Show diff for this revision Revisions of this file
ns9542.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetNetIf.lib	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/donatien/code/EthernetNetIf/#bc7df6da7589
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.lib	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/donatien/code/HTTPServer/#d753966e4d97
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RadioServer.cpp	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,227 @@
+#include "mbed.h"
+#include "EthernetNetIf.h"
+#include "UDPSocket.h"
+#include "TextLCD.h"
+#include "crc32.h"
+#include "ns9542.h"
+#include <vector>
+
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+TextLCD lcd(p21, p22, p23, p24, p25, p26);
+
+Ticker ticker;
+
+SPI spi(p5, p6, p7); // mosi, miso, sclk
+DigitalOut spi_cs(p8);
+
+EthernetNetIf eth(IpAddr(192,168,0,100), //IP Address
+                  IpAddr(255,255,255,0), //Network Mask
+                  IpAddr(192,168,0,1), //Gateway
+                  IpAddr(192,168,0,1)  //DNS
+                 );
+
+UDPSocket udpsocket;
+
+unsigned short sample_l;
+unsigned short sample_r;
+
+unsigned short sample_left()
+{
+    spi_cs = 0;
+    int v = spi.write(0x6800) & 0x03ff;
+    spi_cs = 1;
+    return (v << 6) | (v >> 4);
+}
+
+unsigned short sample_right()
+{
+    spi_cs = 0;
+    int v = spi.write(0x7800) & 0x03ff;
+    spi_cs = 1;
+    return (v << 6) | (v >> 4);
+}
+
+struct host_info_t {
+    Host host;
+    int timer;
+    host_info_t()
+    {
+    }
+    host_info_t(Host h)
+        : host(h)
+        , timer(0)
+    {
+    }
+};
+
+std::vector<host_info_t> hostlist;
+
+void on_udp_socket_event(UDPSocketEvent e)
+{
+    if (e == UDPSOCKET_READABLE) {
+        char buf[64] = {0};
+        Host host;
+        while (int len = udpsocket.recvfrom(buf, 63, &host)) {
+            if (len <= 0) {
+                break;
+            }
+            if (len == 7 && memcmp(buf, "request", len) == 0) {
+                __disable_irq();
+                size_t i, n;
+                n = hostlist.size();
+                for (i = 0; i < n; i++) {
+                    if (memcmp(&host, &hostlist[i].host, sizeof(Host)) == 0) {
+                        hostlist[i].timer = 0;
+                        break;
+                    }
+                }
+                if (i == n) {
+                    hostlist.push_back(host_info_t(host));
+                }
+                __enable_irq();
+                continue;
+            }
+            if (len > 2 && memcmp(buf, "am", 2) == 0) {
+                buf[len] = 0;
+                int f = atoi(buf + 2);
+                ns9542_tune_am9(f);
+                continue;
+            }
+            if (len > 2 && memcmp(buf, "fm", 2) == 0) {
+                buf[len] = 0;
+                int f = atoi(buf + 2);
+                ns9542_tune_fm(f);
+                continue;
+            }
+            if (len > 4 && memcmp(buf, "mute", 4) == 0) {
+                ns9542_mute(true);
+                continue;
+            }
+        }
+    }
+}
+
+static inline void store(unsigned short *p, unsigned short n)
+{
+    ((unsigned char *)p)[0] = n >> 8;
+    ((unsigned char *)p)[1] = n & 0xff;
+}
+
+static inline void store(unsigned long *p, unsigned long n)
+{
+    ((unsigned char *)p)[0] = n >> 24;
+    ((unsigned char *)p)[1] = n >> 16;
+    ((unsigned char *)p)[2] = n >> 8;
+    ((unsigned char *)p)[3] = n & 0xff;
+}
+
+class Sampler {
+private:
+    unsigned long buffer_a[257];
+    unsigned long buffer_b[257];
+    unsigned long *in_ptr;
+    unsigned long *out_ptr;
+    bool output_available;
+    unsigned long crc;
+    int offset;
+public:
+    Sampler()
+    {
+        in_ptr = buffer_a;
+        out_ptr = buffer_b;
+        output_available = false;
+        offset = 0;
+        crc = 0;
+    }
+
+    void on_tick()
+    {
+        if (output_available) {
+            return;
+        }
+        unsigned short *p = (unsigned short *)in_ptr;
+        p += offset * 2;
+        store(p + 0, sample_l);
+        store(p + 1, sample_r);
+        crc = crc32(crc, (unsigned char *)p, 4);
+        offset++;
+        if (offset >= 256) {
+            std::swap(in_ptr, out_ptr);
+            store(&out_ptr[256], crc);
+            output_available = true;
+            offset = 0;
+            crc = 0;
+        }
+    }
+    
+    char const *output_buffer()
+    {
+        return output_available ? (char const *)out_ptr : 0;
+    }
+
+    void output_done()
+    {
+        output_available = false;
+    }
+};
+
+Sampler sampler;
+
+void on_tick()
+{
+    sampler.on_tick();
+}
+
+int main()
+{
+    spi_cs = 1;
+    spi.format(16, 0);
+    spi.frequency(3000000);
+
+    ns9542_init();
+
+    eth.setup();
+    Host host(IpAddr(), 2000);
+    udpsocket.bind(host);
+    udpsocket.setOnEvent(&on_udp_socket_event);
+
+    led1 = 0;
+    ticker.attach(&on_tick, 1.0 / 32000);
+
+    int led1_timer = 0;
+
+    while (1) {
+        sample_l = sample_left();
+        sample_r = sample_right();
+
+        Net::poll();
+
+        char const *p = sampler.output_buffer();
+        if (p) {
+            for (std::vector<host_info_t>::iterator it = hostlist.begin(); it != hostlist.end(); it++) {
+                udpsocket.sendto(p, 1028, (Host *)&it->host);
+            }
+            sampler.output_done();
+
+            __disable_irq();
+            int i = hostlist.size();
+            while (i > 0) {
+                i--;
+                hostlist[i].timer++;
+                if (hostlist[i].timer > 125) {
+                    hostlist.erase(hostlist.begin() + i);
+                }
+            }
+            __enable_irq();
+
+            led1_timer++;
+            if (led1_timer >= 125) {
+                led1 = !led1;
+                led1_timer = 0;
+            }
+        }
+    }
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD.lib	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/simon/code/TextLCD/#44f34c09bd37
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crc32.c	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,80 @@
+
+#include "crc32.h"
+
+static unsigned long crc32_table[] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+unsigned long crc32(unsigned long crc, unsigned char const *ptr, size_t len)
+{
+    crc = ~crc;
+    while (len > 0) {
+        crc = crc32_table[(crc ^ *ptr) & 255] ^ (crc >> 8);
+        ptr++;
+        len--;
+    }
+    return ~crc;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crc32.h	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,19 @@
+
+#ifndef __CRC32_H
+#define __CRC32_H
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+unsigned long crc32(unsigned long crc, unsigned char const *ptr, size_t len);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/9114680c05da
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ns9542.c	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,452 @@
+#include "mbed.h"
+
+//I2C i2c(p9, p10);        // sda, scl
+
+DigitalInOut i2c_sda(p9);
+DigitalInOut i2c_scl(p10);
+
+void wait10us()
+{
+  wait(0.000001);
+}
+
+void wait10ms()
+{
+  wait(0.001);
+}
+
+void wait100ms()
+{
+  wait(0.01);
+}
+
+
+
+#if 1
+
+// i2c
+
+void i2c_cl_0()
+{
+    i2c_scl.output();
+}
+
+void i2c_cl_1()
+{
+    i2c_scl.input();
+}
+
+void i2c_da_0()
+{
+    i2c_sda.output();
+}
+
+void i2c_da_1()
+{
+    i2c_sda.input();
+}
+
+int i2c_get_da()
+{
+  return i2c_sda.read();
+}
+
+void i2c_start()
+{
+  i2c_da_0();
+  wait10us();
+  i2c_cl_0();
+  wait10us();
+}
+
+void i2c_stop()
+{
+  i2c_cl_1();
+  wait10us();
+  i2c_da_1();
+  wait10us();
+}
+
+void i2c_repeat()
+{
+  i2c_cl_1();
+  wait10us();
+  i2c_da_0();
+  wait10us();
+  i2c_cl_0();
+  wait10us();
+}
+
+bool i2c_write(int c)
+{
+  int i;
+  bool nack;
+
+  wait10us();
+
+  for (i = 0; i < 8; i++) {
+    if (c & 0x80) {
+      i2c_da_1();
+    } else {
+      i2c_da_0();
+    }
+    c <<= 1;
+    wait10us();
+    i2c_cl_1();
+    wait10us();
+    i2c_cl_0();
+    wait10us();
+  }
+
+  i2c_da_1();
+  wait10us();
+
+  i2c_cl_1();
+  wait10us();
+  nack = i2c_get_da();
+  i2c_cl_0();
+
+  return nack;
+}
+
+int i2c_read(bool nack)
+{
+  int i, c;
+
+  i2c_da_1();
+  wait10us();
+
+  c = 0;
+
+  for (i = 0; i < 8; i++) {
+    i2c_cl_1();
+    wait10us();
+    c <<= 1;
+    if (i2c_get_da()) {
+      c |= 1;
+    }
+    i2c_cl_0();
+    wait10us();
+  }
+
+  if (nack) {
+    i2c_da_1();
+  } else {
+    i2c_da_0();
+  }
+  wait10us();
+  i2c_cl_1();
+  wait10us();
+  i2c_cl_0();
+  wait10us();
+
+  return c;
+}
+
+#endif
+
+
+// ns9542
+
+void ns9542_write(int a, int c)
+{
+#if 01
+  i2c_start();
+  i2c_write(0xc8);
+  i2c_write(a);
+  i2c_write(c);
+  i2c_stop();
+#else
+    char tmp[2];
+    i2c.start();
+    tmp[0] = a;
+    tmp[1] = c;
+    i2c.write(0xc8, tmp, 2);
+    i2c.stop();
+#endif
+}
+
+int ns9542_read(int a)
+{
+#if 01
+  int c;
+  i2c_start();
+  i2c_write(0xc8);
+  i2c_write(a);
+  i2c_repeat();
+  i2c_write(0xc9);
+  c = i2c_read(true);
+  i2c_stop();
+  return c;
+#else
+    char tmp[2];
+    i2c.start();
+    tmp[0] = a;
+    tmp[1] = 0xc9;
+    i2c.write(0xc8, tmp, 1);
+    i2c.write(0xc8, tmp + 1, 1, true);
+    unsigned char c = i2c.read(1);
+    i2c.stop();
+    return c;
+#endif
+}
+
+void ns9542_imf_adjust()
+{
+  int bF, imf, fhm, g_fhm;
+  bF = 0;
+  g_fhm = 0xf0;
+  ns9542_write(0x15, 0x0e);
+  ns9542_write(0x3d, 0x27);
+  for (fhm = 0; fhm < 4; fhm++) {
+    bF = 0;
+    for (imf = 0; imf < 3; imf++) {
+      ns9542_write(0x37, fhm);
+      ns9542_write(0x16, 22 + imf);
+      wait10ms();
+      if ((ns9542_read(0x70) & 0x0c) == 0x0c) {
+        bF++;
+        if (imf == 1 && g_fhm == 0xf0) {
+          g_fhm = fhm;
+        }
+      }
+    }
+    if (bF == 3) {
+      g_fhm = fhm;
+      break;
+    }
+  }
+  ns9542_write(0x37, 0x80 | g_fhm);
+  ns9542_write(0x16, 23);
+  ns9542_write(0x3d, 0x37);
+  wait100ms();
+}
+
+void ns9542_best_iml(int iml)
+{
+  ns9542_write(0x32, 0x00);
+  while (iml < 16) {
+    ns9542_write(0x17, 0xc0 | iml);
+    wait10ms();
+    if (!(ns9542_read(0x70) & 0x08)) {
+      break;
+    }
+    iml++;
+  }
+  iml--;
+  ns9542_write(0x17, 0xc0 | iml);
+  ns9542_write(0x32, 0x80);
+  wait10ms();
+  ns9542_write(0xfe, 0x0a);
+  wait10ms();
+  wait10ms();
+}
+
+void ns9542_find_pg(int ialgn, int *fine_phase, int *fine_gain, int *result_pg)
+{
+  int i, j;
+  for (i = 0; i < 16; i++) {
+    ns9542_write(0x15, 0x0a | (ialgn << 4));
+    ns9542_write(0x15, 0x0b | (ialgn << 4));
+    if (ns9542_read(0x05) & 0x08) {
+      for (j = 0; j < 20; j++) {
+        if (!(ns9542_read(0x05) & 0x08)) {
+          int g = ns9542_read(0x65);
+          int p = ns9542_read(0x66);
+          if (g >= 103 && g <= 138 && 2 >= p && p <= 14) {
+            *fine_gain = g;
+            *fine_phase = p;
+            *result_pg = 1;
+            return;
+          }
+        }
+        wait10ms();
+      }
+    }
+  }
+  *result_pg = 0;
+}
+
+void ns9542_table_write(int *fine_p, int *fine_g)
+{
+  int i, j, k, result;
+  result = 0;
+  for (i = 0; i < 4; i++) {
+    ns9542_write(0x38, fine_g[i]);
+    ns9542_write(0x39, fine_p[i] << 4);
+    for (j = 0; j < 10; j++) {
+      ns9542_write(0x15, 0x0e | (i << 4));
+      ns9542_write(0x15, 0x03 | (i << 4));
+      if (ns9542_read(0x05) & 0x08) {
+        wait100ms();
+        for (k = 0; k < 10; k++) {
+          if (!(ns9542_read(0x05) & 0x08)) {
+            result++;
+            goto L1;
+          }
+          wait10ms();
+        }
+        break;
+      }
+    }
+L1:;
+    if (result != i + 1) {
+      break;
+    }
+  }
+}
+
+void ns9542_dsp_align_body()
+{
+  int iml, imf, ialgn, cnt, fp, fg;
+  int fine_p[5] = { 0, 0, 0, 0, 0 };
+  int fine_g[5] = { 0, 0, 0, 0, 0 };
+  iml = 5;
+  for (ialgn = 0; ialgn < 4; ialgn++) {
+    ns9542_write(0x15, 0x0a | (ialgn << 4));
+    wait100ms();
+    wait100ms();
+    ns9542_best_iml(iml);
+    imf = 0;
+    cnt = 0;
+    fp = 0;
+    fg = 0;
+    for (cnt = 0; cnt < 5; cnt++) {
+      int fine_phase, fine_gain, result_pg;
+      ns9542_find_pg(ialgn, &fine_phase, &fine_gain, &result_pg);
+      if (result_pg == 0) {
+        return;
+      }
+      fp = fp + fine_phase;
+      fg = fg + fine_gain;
+      if (cnt == 2 && ialgn < 2) {
+        cnt++;
+        break;
+      }
+    }
+    fine_p[ialgn] = fp / cnt;
+    fine_g[ialgn] = fg / cnt;
+  }
+  ns9542_table_write(fine_p, fine_g);
+}
+
+void ns9542_mute(bool mute)
+{
+  if (mute) {
+    ns9542_write(0x00, ns9542_read(0x00) | 0x02);
+  } else {
+    ns9542_write(0x00, ns9542_read(0x00) & ~0x02);
+  }
+}
+
+void ns9542_tune_am9(int freq) // freq = kHz
+{
+  unsigned short psy;
+
+  psy = freq;
+
+  ns9542_write(0x00, 0x23);
+
+  wait10ms();
+  wait10ms();
+
+  ns9542_write(0x04, 0x80);
+  ns9542_write(0x0c, 0xf0);
+
+  ns9542_write(0x10, 0x10);
+
+  ns9542_write(0x02, psy & 0xff);
+  ns9542_write(0x03, psy >> 8);
+
+  ns9542_write(0x00, 0x21);
+}
+
+void ns9542_tune_fm(int freq) // freq = MHz * 100
+{
+  unsigned short psy;
+
+  psy = freq / 5;
+
+  ns9542_write(0x00, 0x03);
+
+  ns9542_write(0x10, 0x10);
+
+  ns9542_write(0x02, psy & 0xff);
+  ns9542_write(0x03, psy >> 8);
+
+  ns9542_write(0x00, 0x01);
+}
+
+void ns9542_reset()
+{
+  ns9542_write(0xfe, 0xaa);
+}
+
+void ns9542_power_on()
+{
+  static unsigned char power_on[] = {
+    0x01, 0x30,
+    0x0c, 0x80,
+    0x0e, 0x34,
+    0x15, 0xc4,
+    0x20, 0x3c,
+    0x21, 0x03,
+    0x22, 0x0a,
+    0x23, 0x0a,
+    0x30, 0xff,
+    0x3d, 0x07,
+    0x40, 0x1a,
+    0x41, 0x9a,
+    0x50, 0xe1,
+    0x54, 0xb0,
+    0x55, 0x36,
+    0x5c, 0xc8,
+    0x5d, 0x61,
+    0x5e, 0x88,
+    0x5f, 0xa5,
+    0x71, 0x2c,
+    0x72, 0x06,
+  };
+  int i;
+  for (i = 0; i < sizeof(power_on); i += 2) {
+    ns9542_write(power_on[i], power_on[i + 1]);
+  }
+
+  ns9542_write(0x00, ns9542_read(0x00) | 0x03);
+}
+
+void ns9542_dsp_alignment()
+{
+  ns9542_write(0x0e, ns9542_read(0x0e) & ~0x60 | 0x40);
+  ns9542_write(0x01, 0x08);
+  ns9542_write(0x15, 0x0c);
+  ns9542_write(0x16, 0x17);
+  ns9542_write(0x37, 0x82);
+  ns9542_write(0x3d, 0x37);
+  wait100ms();
+
+  ns9542_imf_adjust();
+  ns9542_dsp_align_body();
+
+  ns9542_write(0x01, 0x38);
+  ns9542_write(0x0e, ns9542_read(0x0e) & ~0x60 | 0x20);
+  ns9542_write(0x15, 0xc0);
+  ns9542_write(0x17, 0x20);
+  ns9542_write(0x32, 0x00);
+  ns9542_write(0x37, 0x01);
+}
+
+void ns9542_init()
+{                
+    i2c_sda.mode(PullUp);
+    i2c_scl.mode(PullUp);
+    i2c_sda.input();
+    i2c_scl.input();
+    i2c_sda.write(0);
+    i2c_scl.write(0);
+
+    ns9542_reset();
+    ns9542_power_on();
+    ns9542_dsp_alignment();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ns9542.h	Mon Feb 28 11:49:39 2011 +0000
@@ -0,0 +1,9 @@
+#ifndef __NS9542_H
+#define __NS9542_H
+
+void ns9542_init();
+void ns9542_tune_am9(int freq); // freq = kHz
+void ns9542_tune_fm(int freq); // freq = MHz * 100
+void ns9542_mute(bool mute);
+
+#endif