I gave up on the build-in I2C and used the following code to bit bang the I2C instead in JavaScript (code almost 1 to 1 from I2C article on wikipedia):
function jsI2C(p_Info){
function pinHigh(pin){ pin.mode("input"); }
function pinLow(pin){ pin.mode("opendrain"); }
function I2C_delay(){ wait(1); }
function read_SCL(){ return p_Info.scl.read(); } // Set SCL as input and return current level of line, 0 or 1
function read_SDA(){ return p_Info.sda.read(); } // Set SDA as input and return current level of line, 0 or 1
function set_SCL(){ pinHigh(p_Info.scl); } // Actively drive SCL signal high
function clear_SCL(){ pinLow(p_Info.scl); } // Actively drive SCL signal low
function set_SDA(){ pinHigh(p_Info.sda); } // Actively drive SDA signal high
function clear_SDA(){ pinLow(p_Info.sda); } // Actively drive SDA signal low
function arbitration_lost(){}
function wait(n){ for (var index = 0; index < n; index++){} }
function waitForClockStretch(){ while (!read_SCL()){ wait(1); } }
var started = false; // global data
function i2c_start_cond(){
if (started)
{
// if started, do a restart cond
// set SDA to 1
set_SDA();
I2C_delay();
waitForClockStretch();
// Repeated start setup time, minimum 4.7us
I2C_delay();
}
if (!read_SDA())
{
arbitration_lost();
}
// SCL is high, set SDA from 1 to 0.
clear_SDA();
I2C_delay();
clear_SCL();
started = true;
}
function i2c_stop_cond(){
// set SDA to 0
set_SCL();
clear_SDA();
I2C_delay();
waitForClockStretch();
// Stop bit setup time, minimum 4us
I2C_delay();
// SCL is high, set SDA from 0 to 1
set_SDA();
I2C_delay();
if (!read_SDA()){
arbitration_lost();
}
I2C_delay();
started = false;
}
// Write a bit to I2C bus
function i2c_write_bit(bit){
if (bit)
{
set_SDA();
}
else
{
clear_SDA();
}
// SDA change propagation delay
I2C_delay();
// Set SCL high to indicate a new valid SDA value is available
set_SCL();
// Wait for SDA value to be read by slave, minimum of 4us for standard mode
I2C_delay();
waitForClockStretch();
// SCL is high, now data is valid
// If SDA is high, check that nobody else is driving SDA
if (bit && !read_SDA()){
arbitration_lost();
}
// Clear the SCL to low in preparation for next change
clear_SCL();
}
// Read a bit from I2C bus
function i2c_read_bit(){
var bit;
// Let the slave drive data
set_SDA();
// Wait for SDA value to be written by slave, minimum of 4us for standard mode
I2C_delay();
// Set SCL high to indicate a new valid SDA value is available
set_SCL();
waitForClockStretch();
// Wait for SDA value to be read by slave, minimum of 4us for standard mode
I2C_delay();
// SCL is high, read out bit
bit = read_SDA();
// Set SCL low in preparation for next operation
clear_SCL();
return bit;
}
// Write a byte to I2C bus. Return 0 if ack by the slave.
function i2c_write_byte(send_start, send_stop, byte){
var nack = false;
if (send_start){
i2c_start_cond();
}
for (var bit = 0; bit < 8; bit++){
i2c_write_bit((byte & 0x80) !== 0);
byte <<= 1;
}
nack = i2c_read_bit();
if (send_stop){
i2c_stop_cond();
}
return nack;
}
// Read a byte from I2C bus
function i2c_read_byte(nack, send_stop){
var byte = 0;
for (var bit = 0; bit < 8; bit++ )
{
byte = ( byte << 1 ) | i2c_read_bit();
}
i2c_write_bit(nack);
if (send_stop){
i2c_stop_cond();
}
return byte;
}
return {
readRegister: function(address, register, bytes){
var result = [];
// write address and write mode
if (!i2c_write_byte(true, false, (address << 1) | 0)){
// write the data to write in this case the register and write a stop
if (!i2c_write_byte(false, true, register)){
// write the address and read mode
if (!i2c_write_byte(true, false, (address << 1) | 1)){
// read the bytes
while (bytes){
bytes--;
// if no bytes remaining send nack followed by stop
result.push(i2c_read_byte(bytes === 0, bytes === 0));
}
}
}
}
return result;
},
write: function(address, p_Bytes){
if (!i2c_write_byte(true, false, (address << 1) | 0)){
// write the data to write in this case the register and write a stop
for (var index = 0; index < p_Bytes.length; index++){
if (i2c_write_byte(false, index == p_Bytes.length - 1, p_Bytes[index])){
break;
}
}
}
}
};
}
This code actually works with the radio module and I managed to tune the FM station and got it to play :-).
Here is the radio code that uses my bit-banging I2C for anyone who is interested :
// create the radio interface
// specify SDA and SCL pins to use, will default to 4 and 5 if not set, neither pin can be 0
function Radio(p_SDA, p_SCL){
"use strict";
var i2c = jsI2C({ sda: p_SDA || D4, scl: p_SCL || D5});
var i2c_write_address = 0x10;
var i2c_read_address = 0x11;
var out_buffer = [];
// constants :
var rda_CHIP_ID = 0x58, // CHIP ID
// Tuning Band //
rda_87_108MHz = 0b0000000000000000, // us band
rda_76_91MHz = 0b0000000000000100, // japan band
rda_76_108MHz = 0b0000000000001000, // world band we use this
rda_65_76MHz = 0b0000000000001100, // east europe
// Tuning Steps //
rda_100kHz = 0b0000000000000000,
rda_200kHz = 0b0000000000000001, // not US band compatible **//
rda_50kHz = 0b0000000000000010,
rda_25kHz = 0b0000000000000011, // we use this
// ** 200kHz spacing works but frequencies are always out by 100kHz from US frequency slots.
// It's not possible to tune to a US station with 200kHz spacing you must use 100kHz instead.
// De-emphasis //
rda_50us = 0b0000100000000000, // eu standard
rda_75us = 0b0000000000000000, // us standard
// REG 0x02
rda_DHIZ = 0b1000000000000000,
rda_DMUTE = 0b0100000000000000,
rda_MONO = 0b0010000000000000,
rda_BASS = 0b0001000000000000,
rda_RCLK = 0b0000100000000000,
rda_RCKL_DIM = 0b0000010000000000,
rda_SEEKUP = 0b0000001000000000,
rda_SEEK = 0b0000000100000000,
rda_SKMODE = 0b0000000010000000, // default is to wrap around
// Timing XTAL = rda_CLK_MODE //
// we are not changing the defaults so remark
rda_32_768kHz = 0b0000000000000000,
rda_12MHz = 0b0000000000010000,
rda_24MHz = 0b0000000001010000,
rda_13MHz = 0b0000000000100000,
rda_26MHz = 0b0000000001100000,
rda_19_2MHz = 0b0000000000110000,
rda_38_4MHz = 0b0000000001110000,
rda_CLK_MODE = 0b0000000001110000, // default is 32.768kHz which is the build in crystal
rda_RDS_EN = 0b0000000000001000,
rda_NEW_METHOD = 0b0000000000000100,
rda_SOFT_RESET = 0b0000000000000010,
rda_ENABLE = 0b0000000000000001,
// REG 0x03
rda_CHAN = 0b1111111111000000,
rda_DIRECT_MODE = 0b0000000000100000, // only used with test
rda_TUNE = 0b0000000000010000,
rda_BAND = 0b0000000000001100,
rda_SPACE = 0b0000000000000011,
// REG 0x04
rda_DE = 0b0000100000000000,
rda_SOFTMUTE_EN = 0b0000001000000000,
rda_AFCD = 0b0000000100000000,
// REG 0x05
rda_INT_MODE = 0b1000000000000000,
rda_SEEKTH = 0b0000111100000000,
rda_VOLUME = 0b0000000000001111,
// REG 0x06
rda_OPEN_MODE = 0b0110000000000000,
// REG 0x07
rda_BLEND_TH = 0b0111110000000000,
rda_65_50M_MODE = 0b0000001000000000,
rda_SEEK_TH_OLD = 0b0000000011111100,
rda_BLEND_EN = 0b0000000000000010,
rda_FREQ_MODE = 0b0000000000000001,
// REG 0x0A
rda_RDSR = 0b1000000000000000,
rda_STC = 0b0100000000000000,
rda_SF = 0b0010000000000000,
rda_RDSS = 0b0001000000000000,
rda_BLK_E = 0b0000100000000000,
rda_ST = 0b0000010000000000,
rda_READCHAN = 0b0000001111111111,
// REG 0x0B
rda_RSSI = 0b1111110000000000,
rda_FM_TRUE = 0b0000001000000000,
rda_FM_READY = 0b0000000100000000,
rda_ABCD_E = 0b0000000000010000,
rda_BLERA = 0b0000000000001100,
rda_BLERB = 0b0000000000000011;
//
function read_chip(offset) {
var data = i2c.readRegister(i2c_read_address, offset, 2);
return ((data[0] << 8) & 0xff00) | (data[1] & 0x00ff);
}
function write_chip(size) {
i2c.write(i2c_write_address, out_buffer.slice(0, size));
}
function tune(p_On){
var data = ((out_buffer[2]<<8)|out_buffer[3])|rda_TUNE;
if (!p_On) { data = data^rda_TUNE; }
out_buffer[2] = (data >> 8) & 0xff;
out_buffer[3] = data & 0xff;
}
function readLoopChip(){
for (var loop = 2; loop < 8; loop++) {
var data = read_chip(loop);
out_buffer[(loop*2)-4] = (data >> 8) & 0xff;
out_buffer[(loop*2)-3] = data & 0xff;
}
tune(false); // disable tuning
}
function init_chip() {
var data = read_chip(0);
var found = 0;
var result;
if ((data&0xff) == rda_CHIP_ID) {
found = 1;
}
data = (data >> 8)&0xff;
if (data == rda_CHIP_ID) {
found = 1;
}
if (found === 0) {
}
else if ((read_chip(13) == 0x5804) && (read_chip(15) == 0x5804)) {
result = 0;
} // not set up
else {
result = 1;
} // already used
return result;
}
function isInitialized(){
var state = init_chip();
return state !== 0;
}
// radio on
function on(p_Callback){
// REG 02
// normal output, enable mute, stereo, no bass boost, clock = 32.768kHz, RDS enabled, new demod method, power on
var data = rda_DHIZ|(rda_DMUTE&0)|(rda_MONO&0)|(rda_BASS&0)|rda_RDS_EN|rda_NEW_METHOD|rda_ENABLE;
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data & 0xff;
// REG 03 - no auto tune, 76-108 band, 0.1 spacing
data = (rda_TUNE&0)|rda_76_108MHz|rda_100kHz;
out_buffer[2] = (data >> 8) & 0xff;
out_buffer[3] = data & 0xff;
// REG 04 - audio 50us, no soft mute, disable AFC
data = rda_50us|(rda_SOFTMUTE_EN&0)|rda_AFCD;
out_buffer[4] = (data >> 8) & 0xff;
out_buffer[5] = data & 0xff;
// REG 05 - max volume
data = rda_INT_MODE|0x0480|rda_VOLUME;
out_buffer[6] = (data >> 8) & 0xff;
out_buffer[7] = data & 0xff;
// REG 06 - reserved
out_buffer[8] = 0;
out_buffer[9] = 0;
// REG 07
var blend_threshold = 0b0011110000000000; // mix L+R with falling signal strength
data = blend_threshold|rda_65_50M_MODE|0x80|0x40|rda_BLEND_EN|(rda_FREQ_MODE&0);
out_buffer[10] = (data >> 8) & 0xff;
out_buffer[11] = data &0xff;
write_chip(12);
setTimeout(p_Callback, 200);
}
function off(){
if (isInitialized()){
var data = (read_chip(2)|rda_ENABLE)^rda_ENABLE;
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data &0xff;
write_chip(2);
}
}
function forceMono(p_On){
if (isInitialized()){
var data = (read_chip(2)|rda_MONO);
if (!p_On) { data = data^rda_MONO; }
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data & 0xff;
write_chip(2);
}
}
function bassBoost(p_On){
if (isInitialized()){
var data = (read_chip(2)|rda_BASS);
if (!p_On) { data = data^rda_BASS; }
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data & 0xff;
write_chip(2);
}
}
function mute(p_On){
if (isInitialized()){
var data = (read_chip(2)|rda_DMUTE);
if (p_On) { data = data^rda_DMUTE; }
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data & 0xff;
write_chip(2);
}
}
function checkSeek(p_Callback){
var regA = read_chip(0x0A);
if (!!(regA & rda_STC)){
p_Callback(!(regA & rda_SF));
}
else {
setTimeout(function(){ checkSeek(p_Callback); }, 500);
}
}
function seekUp(p_Callback){
if (isInitialized()){
var data = (read_chip(2)|rda_SEEKUP|rda_SEEK);
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data & 0xff;
tune(true);
write_chip(2);
checkSeek(p_Callback);
}
}
function seekDown(p_Callback){
if (isInitialized()){
var data = (read_chip(2)|rda_SEEKUP|rda_SEEK)^rda_SEEKUP;
out_buffer[0] = (data >> 8) & 0xff;
out_buffer[1] = data & 0xff;
tune(true);
write_chip(2);
checkSeek(p_Callback);
}
}
function deemphasis75(p_On){
if (isInitialized()){
readLoopChip();
var data = (read_chip(4)|rda_DE)^rda_DE;
data = (p_On) ? (data | rda_75us) : (data | rda_50us);
out_buffer[4] = (data >> 8) & 0xff;
out_buffer[5] = data & 0xff;
write_chip(6);
}
}
function volume(p_Val){
if (isInitialized()){
readLoopChip();
var data = (read_chip(5)|rda_VOLUME)^rda_VOLUME;
if (p_Val > rda_VOLUME) { p_Val = rda_VOLUME; }
data = data|p_Val;
out_buffer[6] = (data >> 8) & 0xff;
out_buffer[7] = data & 0xff;
write_chip(8);
}
}
function setFrequency(p_Mhz){
if (isInitialized()){
mute(false);
var channel = ((((p_Mhz - 76)/0.1) | 0) & 0x3FF) << 6;
var data = (read_chip(3)|rda_CHAN)^rda_CHAN;
data = data|channel;
out_buffer[2] = (data >> 8) & 0xff;
out_buffer[3] = data & 0xff;
tune(true);
write_chip(4);
}
}
function getRDS() {
// check register A
var data = read_chip(0x0A);
var rds = [0,0,0,0, false, false];
// block E type
rds[4] = !!(data & rda_BLK_E);
// check for new RDS data available
if (rds[4] || data & rda_RDSR) {
rds[0] = read_chip(0x0C) & 0xffff;
rds[1] = read_chip(0x0D) & 0xffff;
rds[2] = read_chip(0x0E) & 0xffff;
rds[3] = read_chip(0x0F) & 0xffff;
rds[5] = true;
}
return rds;
}
function getState(){
var reg2 = read_chip(2);
var regA = read_chip(0x0A);
var regB = read_chip(0x0B);
return {
isInitialized: isInitialized(),
muteOn: !(reg2 & rda_DMUTE),
forceMonoOn: !!(reg2 & rda_MONO),
bassBoostOn: !!(reg2 & rda_BASS),
rdsOn: !!(reg2 & rda_RDS_EN),
de: !read_chip(4) & rda_DE,
volume: read_chip(5) & rda_VOLUME,
stereo: !!(regA & rda_ST),
signalStrength: ((regB & rda_RSSI)>>10),
channel: ((regA & 0x03ff) + 760) / 10,
isStation: !!(regB & rda_FM_TRUE),
isReady: !!(regB & rda_FM_READY),
seekComplete: !!(regA & rda_STC),
seekFail: !!(regA & rda_SF),
hasRDS: !!(regA & rda_BLK_E) || !!(regA & rda_RDSR)
};
}
return {
read_chip: read_chip,
write_chip: write_chip,
on: on,
off: off,
forceMono: forceMono,
bassBoost: bassBoost,
mute: mute,
deemphasis75: deemphasis75,
volume: volume,
setFrequency: setFrequency,
getState: getState,
getRDS: getRDS,
seekUp: seekUp,
seekDown: seekDown
};
}
var radio = Radio(D4, D5);
radio.on(function(){
radio.setFrequency(104.5);
});
Post a reply
Bold
Italics
Link
Image
List
Quote
code
Preview
Formatting Help
Don't worry about formatting, just type in the text and we'll take care of making sense of it. We will auto-convert links, and if you put asterisks around words we will make them bold.
Tips:
Create headers by underlining text with ==== or ----
To *italicise* text put one asterisk each side of the word
To **bold** text put two asterisks each side of the word
Embed images by entering: ![](https://www.google.co.uk/images/srpr/logo4w.png) That's the hard one: exclamation, square brackets and then the URL to the image in brackets.
* Create lists by starting lines with asterisks
1. Create numbered lists by starting lines with a number and a dot
> Quote text by starting lines with >
Mention another user by @username
For syntax highlighting, surround the code block with three backticks:
```
Your code goes here
```
Just like Github, a blank line must precede a code block.
If you upload more than 5 files we will display all attachments as thumbnails.
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
I gave up on the build-in I2C and used the following code to bit bang the I2C instead in JavaScript (code almost 1 to 1 from I2C article on wikipedia):
This code actually works with the radio module and I managed to tune the FM station and got it to play :-).
Here is the radio code that uses my bit-banging I2C for anyone who is interested :