Switch FRAM FeatherWing

This project is a simple Adafruit FeatherWing that includes things you might want to include on a board or wing you design:

  • DIP switch
  • BCD rotary switch
  • FRAM (ferro-electric non-volatile memory)

You might use this for configuration of your project, for example.

While the Eagle CAD files and BOM (bill of materials) are included here so you can build your own board exactly like this one, it’s really intended to be ideas for things you can include in your own designs.

The other handy thing is that all of the features connected by I2C (pins D0 and D1), so it requires no additional GPIO!

The project files are here: https://github.com/rickkas7/SwitchFramFeatherRK

Switches

Sometimes you’ll want to include physical switches on your board. This demo includes two kinds:

  • 4 on-off SPST DIP switches
  • A rotary selector that supports values 0-9 by turning the knob

One problem with these types of switches is that they require a lot of GPIO pins. Each requires 4, so rather than tie up 8 GPIO pins on the Argon/Boron/Xenon, an 8-port I2C GPIO expander is used.

The MCP23008 I2C GPIO expander is great for situations like this. It attaches to the I2C bus (pins D0 and D1) and you can include up to 8 of these chips on a single device.

On the left side of the FeatherWing (as pictured above) you’ll notice 3 small solder jumpers labeled A0, A1, and A2. By shorting out the solder jumper you can change the I2C address of the MCP23008 chip.

To make this board flexible, the solder jumpers connect to VCC (shorting the jumper sets value 1) and there are 10K pull-down resistors on the address lines. On your own boards you may want to just hardcode the address lines and eliminate the resistors and solder jumpers.

A2 A1 A0 Address
open open open 0x20
open open closed 0x21
open closed open 0x22
open closed closed 0x23
closed open open 0x24
closed open closed 0x25
closed closed open 0x26
closed closed closed 0x27

The MCP23008-RK library is used to read the GPIO lines connected to the chip.

FRAM

The FRAM (Ferroelectric RAM) is a non-volatile storage device, meaning it keeps the values when power is removed. Unlike flash memory, it’s fast to write to and does not wear out, so it’s ideal for saving values frequently.

The chip included here is a Fujitsu MB85RC64TAPNF-G-BDERE1, IC FRAM 64K I2C 3.4MHZ 8SOP. It costs $ 1.58 at single quantities so it’s affordable to add to your board. It’s also tiny, in an 8-SOP package, and connects by I2C, so it doesn’t need extra GPIO. That 64 Kbits, so it can store 8 Kbytes of data.

On the right side of the FeatherWing (as pictured above) you’ll notice 3 small solder jumpers labeled A0, A1, and A2. By shorting out the solder jumper you can change the I2C address of the FRAM chip.

A2 A1 A0 Address
open open open 0x50
open open closed 0x51
open closed open 0x52
open closed closed 0x53
closed open open 0x54
closed open closed 0x55
closed closed open 0x56
closed closed closed 0x57

The MB85RC256V-FRAM-RK library makes it easy to use this chip. It provides an API just like the Particle EEPROM API to load and save values in the FRAM.

Sample Code

The sample program prints out the the status of the switches at startup and when the values change. The library takes care of debouncing the switches and reporting changes automatically.

The sample code also stores a counter in FRAM, and the counter is updated once per second.

0000005678 [app] INFO: onDipSwitchChange oooo
0000005680 [app] INFO: onBcdSwitchChange 6
0000005684 [app] INFO: counter=908
...
0000015683 [app] INFO: counter=918
0000016093 [app] INFO: onBcdSwitchChange 7
0000016683 [app] INFO: counter=919
0000017683 [app] INFO: counter=920
0000018026 [app] INFO: onBcdSwitchChange 8
0000018684 [app] INFO: counter=921
0000019684 [app] INFO: counter=922
0000019883 [app] INFO: onBcdSwitchChange 7
0000020684 [app] INFO: counter=923
0000021157 [app] INFO: onBcdSwitchChange 6
...
0000028683 [app] INFO: counter=931
0000029289 [app] INFO: onDipSwitchChange |ooo
0000029684 [app] INFO: counter=932
0000030684 [app] INFO: counter=933
0000031684 [app] INFO: counter=934
0000032684 [app] INFO: counter=935
0000033684 [app] INFO: counter=936
0000034217 [app] INFO: onDipSwitchChange oooo

You must call from setup:

	switchFramFeather.setup();

And from loop:

	switchFramFeather.loop();

This provides time to the library to process the switches.

If you’re not familiar with C++11 lambda syntax, this might look peculiar, but it’s not that complicated:

	switchFramFeather.onBcdSwitchChange([](SwitchFramFeatherRK&, uint8_t value) {
		Log.info("onBcdSwitchChange %d", value);
	});

What this does is register a function to be called when the BCD switch value changes. It’s just that it’s declared in-line using a lambda, rather than having to declare a separate function.

The Log.info statement will execute and log to debug serial whenever the switch changes (even after setup exits).

The sample code to use the FRAM looks like this:

if (millis() - lastCounterUpdate >= 1000) {
		lastCounterUpdate = millis();

		size_t counter;
		switchFramFeather.get(0, counter);
		counter++;
		switchFramFeather.put(0, counter);

		Log.info("counter=%u", counter);
	}

BOM and Assembly

Quantity Description Example
8 10K resistor 0603 Panasonic ERJ-PA3J103V
1 DIP switch SPST 4 pos CTS Electrocomponents 209-4MS
1 DIP Switch BCD 10 Position CTS Electrocomponents 220ADC10
1 MCP23008 Microchip MCP23008T-E-SO
1 IC FRAM 64K I2C 3.4MHZ 8SOP Fujitsu MB85RC64TAPNF-G-BDERE1
Male header pins 0.1" Sullins PRPC040SAAN-RC

The Eagle CAD files are included in the Github repository. I fabricated my boards at OshPark. It costs $9 to get 3 FeatherWing boards manufactured (standard speed), with free shipping (in the United States, at least). The other handy thing is that you can upload the .brd file directly and don’t have to generate Gerber files.

And while theoretically possible to build this board using solder paste and a soldering iron with a very steady hand, it’s intended to be reflowed. I use an inexpensive T962 Reflow Oven.

16 Likes

Thanks for sharing all this! :spark:

@rickkas7 For those who would like to have this board fabricated and assembled but do not have Eagle CAD to generate the extended gerber files and BoM, could you possibly generate them and attach them to you Github repository?

@armor You can download Eagle for free for all platforms. This board fits well within the limits of the free version. :slight_smile:

That wasn’t my point - if you just want to get some PCBs made then it is a bit of effort to get Eagle installed and import the board design and then run through generating the CAM files - when this would have already been done and put in a zip file to send to OshPark when the boards were ordered.

2 Likes

Speaking of this, I’d be interested in buying this FeatherWing as a product.

The reason there aren’t Gerber files is that OshPark just takes the Eagle .brd file directly and doesn’t require creation of the Gerbers, so I didn’t want to include Gerber files that had never been tested before. Also some board manufacturers use their own .cam file to produce specifically what they want. I may start including them as I’ve been using the generic Eagle Gerber generation with JCLPCB and it seems to work fine.

1 Like

Really? I have something new to try today!

1 Like

@rickkas7,

I’m adapting your FRAM library to another FRAM part (MR44V100A) and I’m wondering if you could help me understand a little about your get / put functions.

Specifically, I’d like to put an array of 32bit values into a struct and save them with fram.put

struct ScheduleData
{
  uint32_t schedule[50]; 
};

ScheduleData schedule;

fram.put(100, schedule);
fram.get(100, schedule);

Is this going to be impossible with the 32 byte limit in place by I2C, or is there some sort of workaround you can think of?

My current workaround plan is to do something more along the lines of:

uint32_t array[50];

for(int i = 0; i < 50; i++) {
   fram.put(100 + i, array[i]);
}

for(int i = 0; i < 50; i++) {
   fram.get(100 + i, array[i]);
}

Finally, I’ve read that “write after read” on FRAM chips, as reads can be destructive. Does it make to add a method for this, or is this only in the case of very high read cycles?

Just handle it in your FRAM library like it’s handled in the MB85RC256V library:

It breaks up the writes into a size that fits in an I2C write within the readData() and writeData() methods.

This is the library: LINK

I’m having an issue with it on the Boron (1.3.1). When the boron is first reset or power cycled it will communicate with the FRAM. Then after 10 seconds or so, it will stop communicating with the FRAM.

What’s weird is that after putting Wire.end() and Wire.begin() into the simple example, the Boron will reconnect to the FRAM after I see the charge LED blink a few times in a row. Is there some issue with I2C and the PMIC?

Logs showing going offline:

[Connected]
System version: 1.3.1
begin 6428, 0; pmic: 69, 0
d1=547
addr=4, intVal=546, sizeof(int)=4
addr=8, doubleVal=54.600000, sizeof(doubleVal)=8
addr=16, str=African lungfis, sizeof(stringBuf)=16
next fish name=Aholehole
addr=32, a=1100 b=11.000062 c=0, sizeof(data)=12

--------


end 6450, 6430
begin 6930, 6430; pmic: 69, 0
d1=548
addr=4, intVal=547, sizeof(int)=4
addr=8, doubleVal=54.700000, sizeof(doubleVal)=8
addr=16, str=Aholehole, sizeof(stringBuf)=16
next fish name=Airbreathing catfish
addr=32, a=1102 b=11.020062 c=1, sizeof(data)=12

--------


end 6953, 6931
begin 7431, 6931; pmic: 69, 0
d1=549
addr=4, intVal=548, sizeof(int)=4
addr=8, doubleVal=54.800000, sizeof(doubleVal)=8
addr=16, str=Airbreathing ca, sizeof(stringBuf)=16
next fish name=Airsac catfish
addr=32, a=1104 b=11.040063 c=0, sizeof(data)=12

--------


end 7453, 7433
begin 7933, 7433; pmic: 69, 0
   --- read set address failed 2
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 8048, 7934
begin 8434, 7934; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 8547, 8435
begin 8935, 8435; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

Logs showing going online then back offline:

-------


end 23078, 22965
begin 23465, 22965; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 23579, 23466
begin 23966, 23466; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 24081, 23967
[Disconnected]
[Connected]
begin 248915, 248415; pmic: 69, 0
d1=601
addr=4, intVal=600, sizeof(int)=4
addr=8, doubleVal=60.000000, sizeof(doubleVal)=8
addr=16, str=Alewife, sizeof(stringBuf)=16
next fish name=Alfonsino
addr=32, a=1208 b=12.080087 c=0, sizeof(data)=12

--------


end 248937, 248917
begin 249417, 248917; pmic: 69, 0
d1=602
addr=4, intVal=601, sizeof(int)=4
addr=8, doubleVal=60.100000, sizeof(doubleVal)=8
addr=16, str=Alfonsino, sizeof(stringBuf)=16
next fish name=Algae eater
addr=32, a=1210 b=12.100087 c=1, sizeof(data)=12

--------


end 249439, 249418
begin 249918, 249418; pmic: 69, 0
d1=603
addr=4, intVal=602, sizeof(int)=4
addr=8, doubleVal=60.200000, sizeof(doubleVal)=8
addr=16, str=Algae eater, sizeof(stringBuf)=16
next fish name=Alligatorfish
addr=32, a=1212 b=12.120088 c=0, sizeof(data)=12

--------


end 249940, 249920
begin 250420, 249920; pmic: 69, 0
d1=604
addr=4, intVal=603, sizeof(int)=4
addr=8, doubleVal=60.300000, sizeof(doubleVal)=8
addr=16, str=Alligatorfish, sizeof(stringBuf)=16
next fish name=Alligator gar
addr=32, a=1214 b=12.140088 c=1, sizeof(data)=12

--------


end 250442, 250421
begin 250921, 250421; pmic: 69, 0
d1=605
addr=4, intVal=604, sizeof(int)=4
addr=8, doubleVal=60.400000, sizeof(doubleVal)=8
addr=16, str=Alligator gar, sizeof(stringBuf)=16
next fish name=American sole
addr=32, a=1216 b=12.160089 c=0, sizeof(data)=12

--------


end 250942, 250923
begin 251423, 250923; pmic: 69, 0
d1=606
addr=4, intVal=605, sizeof(int)=4
addr=8, doubleVal=60.500000, sizeof(doubleVal)=8
addr=16, str=American sole, sizeof(stringBuf)=16
next fish name=Amur pike
addr=32, a=1218 b=12.180089 c=1, sizeof(data)=12

--------


end 251444, 251424
begin 251924, 251424; pmic: 69, 0
d1=607
addr=4, intVal=606, sizeof(int)=4
addr=8, doubleVal=60.600000, sizeof(doubleVal)=8
addr=16, str=Amur pike, sizeof(stringBuf)=16
next fish name=Anchovy
addr=32, a=1220 b=12.200089 c=0, sizeof(data)=12

--------


end 251945, 251925
begin 252425, 251925; pmic: 69, 0
d1=608
addr=4, intVal=607, sizeof(int)=4
addr=8, doubleVal=60.700000, sizeof(doubleVal)=8
addr=16, str=Anchovy, sizeof(stringBuf)=16
next fish name=Aeneus corydoras
addr=32, a=1222 b=12.220090 c=1, sizeof(data)=12

--------


end 252448, 252426
begin 252926, 252426; pmic: 69, 0
d1=609
addr=4, intVal=608, sizeof(int)=4
addr=8, doubleVal=60.800000, sizeof(doubleVal)=8
addr=16, str=Aeneus corydora, sizeof(stringBuf)=16
next fish name=African glass catfish
addr=32, a=1224 b=12.240090 c=0, sizeof(data)=12

--------


end 252948, 252928
begin 253428, 252928; pmic: 69, 0
d1=610
addr=4, intVal=609, sizeof(int)=4
addr=8, doubleVal=60.900000, sizeof(doubleVal)=8
addr=16, str=African glass c, sizeof(stringBuf)=16
next fish name=African lungfish
addr=32, a=1226 b=12.260091 c=1, sizeof(data)=12

--------


end 253450, 253430
begin 253930, 253430; pmic: 69, 0
d1=0
   --- write failed 3
addr=4, intVal=0, sizeof(int)=4
   --- write failed 3
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 3
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 3
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 3

--------


end 253946, 253932
begin 254432, 253932; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 254546, 254433
begin 254933, 254433; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 255048, 254935
begin 255435, 254935; pmic: 69, 0
d1=0
   --- write failed 1
   --- read set address failed 1
addr=4, intVal=0, sizeof(int)=4
   --- write failed 1
   --- read set address failed 1
addr=8, doubleVal=0.000000, sizeof(doubleVal)=8
   --- write failed 1
   --- read set address failed 1
addr=16, str=, sizeof(stringBuf)=16
next fish name=African glass catfish
   --- write failed 1
   --- read set address failed 1
addr=32, a=0 b=0.000000 c=0, sizeof(data)=12
   --- write failed 1

--------


end 255551, 255437
[Disconnected]

After some additional testing, I’ve found that my issue is related to the Boron I was testing on. I’ve swapped borons and I no longer get these errors. The adapted library works fine.

I haven’t tested in depth on this, but my suspicion is the 3v3 regulator on the Boron is somehow damaged.

I think I have a similar problem do you know how to check if the regulator is broken and is there a way to fix it?

When you attach the LiPo or USB you should see a stable 3.3V on the 3v3 pin (best checked with an oscilloscope).