This weekend I spent my Friday afternoon and Saturday at the NW Hobby Expo; talk about an intense desire to get another hobby.
Oh wait… oops; don’t tell me wife 😉 Should be here tomorrow. A little small guy to get me started.
I was out helping a friend sell 3D printers and interest people into joining our Makerspace.. mission success? I don’t know we’ll see at the next open house.
The makerspace has kept me busy every moment I haven’t been spending with my little man.. thankfully my makerspace activities have been more to my desires.. I developed a little code to run WS2812 LED drivers for a PIC… why we used a PIC (my fav) I’m unsure.. these maker-folks are all Arduino… I borrow a board and took a look. It’s cute.. micros with training wheels. Here is some basic code to generate colors.. maybe it’ll cut your dev time down on a real project.
It’s very very dirty and there is some un-used stuff… but it was a quick hack based on old code and I didn’t optimize the delays at all.
… This is work in progress for a resistor color code clock. I’ll obviously need to switch to a crystal, etc.
I also threw together some basic test code for the 9DOF BNO055 IMU. I bought one just for kicks in case I want to add it to my SRS Pop Can Challenge competition robot. I volunteered for the job left over on our makerspace robot group project… because power systems are a bit of a snooze, and I’m pretty much done, I decided to make my own. It won’t win (because I’m not going for all the points) but it’ll be another robot on the field. Here is some cBode to get you started if you’re interested… I used the Adafruit BOB.. pretty cool and they broke out just enough stuff it seems. I tried it with the UART interface first for kicks.. but then switched to I2C.
/*
* File: main.c
* Author: Charles M Ihler
* Contact at: http://iradan.com
*
* Created on February 8, 2014, 11:39 AM
*
* Target Device:
* 18F14K22 on Tautic 20 pin dev board
*
* Project: IMU for PCC
*
*
* Version:
* 0.1 IO Confoiguration RS232/TX/I2C
* 0.2 Test BNO055 by UART
* 0.3 Dump UART interface and get cal status via I2C and send to UART
*
*/
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 16000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/16000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/16000.0)))
#endif
#include
#include
#include
#include
//config bits
#pragma config FOSC=IRC, WDTEN=OFF, PWRTEN=OFF, MCLRE=ON, CP0=OFF, CP1=OFF, BOREN=ON
#pragma config STVREN=ON, LVP=OFF, HFOFST=OFF, IESO=OFF, FCMEN=OFF
/*
* Variables
*/
#define r_device_address 0x51 // BNO055 Default Address
#define w_device_address 0x50 // BNO055 Default Address
unsigned int ACK_bit;
int i;
unsigned char byte, tempbyte1, tempbyte2;
int w0,w1,w2,w3,w4; //confirguration words
int present; //device present
//int itxdata; //int RS232 tx data
//char buf[10]; //buff for iota
volatile unsigned int uart_data; // use 'volatile' qualifer as this is changed in ISR
/*
* Functions
*/
void interrupt ISR() {
if (PIR1bits.RCIF) // see if interrupt caused by incoming data
{
uart_data = RCREG; // read the incoming data
if (uart_data == 0xBB)
{
LATAbits.LA0 = 1;
present = 1;
}
PIR1bits.RCIF = 0; // clear interrupt flag
//
}
if (INTCONbits.T0IF)
{
//LATAbits.LATA0 = 1;
INTCONbits.T0IF = 0;
}
}
void __delay_10ms(unsigned char n) //__delay functions built-in can't be used for much at this speed... so!
{
while (n-- != 0) {
__delay_ms(10);
}
}
void uart_tx(unsigned int mydata_byte) {
while(!TXSTAbits.TRMT); // make sure buffer full bit is high before transmitting
TXREG = mydata_byte; // transmit data
}
void write_uart(const char *txt){
while(*txt != 0) uart_tx(*txt++); //this send a string to the TX buffer
//one character at a time
}
void serial_init(void)
{
// calculate values of SPBRGL and SPBRGH based on the desired baud rate
//
// For 8 bit Async mode with BRGH=0: Desired Baud rate = Fosc/64([SPBRGH:SPBRGL]+1)
// For 8 bit Async mode with BRGH=1: Desired Baud rate = Fosc/16([SPBRGH:SPBRGL]+1)
TXSTAbits.BRGH=1; //
TXSTAbits.TX9=0; // select 8 data bits
TXSTAbits.TXEN = 1; // enable transmit
RCSTAbits.SPEN=1; // serial port is enabled
RCSTAbits.RX9=0; // select 8 data bits
RCSTAbits.CREN=1; // receive enabled
SPBRG=103; //
PIR1bits.RCIF=0; // make sure receive interrupt flag is clear
PIE1bits.RCIE=1; // enable UART Receive interrupt
INTCONbits.PEIE = 1; // Enable peripheral interrupt
INTCONbits.GIE = 1; // enable global interrupt
INTCONbits.T0IE = 0;
__delay_10ms(5); // give time for voltage levels on board to settle
}
void init_io(void) {
TRISAbits.TRISA0 = 0; // output
TRISAbits.TRISA1 = 1; // input
TRISAbits.TRISA2 = 1; //
TRISAbits.TRISA4 = 1; //
TRISAbits.TRISA5 = 1; //
ANSEL = 0x00; // no A/D
ANSELH = 0x00;
TRISBbits.TRISB4 = 1; // RB4 I2C SDA, has to be set as an input
TRISBbits.TRISB5 = 1; // RB5 = nc
TRISBbits.TRISB6 = 1; // RB6 I2C SCLK, has to be set as an input
TRISBbits.TRISB7 = 0; // RS232 TX
WPUBbits.WPUB4 = 0x01; //PORT B WEAK PULL UPS
WPUBbits.WPUB6 = 0x01;
TRISCbits.TRISC0 = 1; //
TRISCbits.TRISC1 = 1; // input
TRISCbits.TRISC2 = 1; //
TRISCbits.TRISC3 = 0; //
TRISCbits.TRISC4 = 0; //
TRISCbits.TRISC5 = 0; //
TRISCbits.TRISC6 = 1; // input
TRISCbits.TRISC7 = 1; // input
__delay_10ms(10); //let voltage settle
//write_uart("RESET\n"); // transmit some data
}
void I2C_ACK(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPCON2bits.ACKDT=0; // clear the Acknowledge Data Bit - this means we are sending an Acknowledge or 'ACK'
SSPCON2bits.ACKEN=1; // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
while(!PIR1bits.SSPIF); // Wait for interrupt flag to go high indicating transmission is complete
}
void Send_I2C_Data(unsigned int databyte)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPBUF = databyte; // send databyte
while(!PIR1bits.SSPIF); // Wait for interrupt flag to go high indicating transmission is complete
}
unsigned char RX_I2C_Data (void)
{
RCEN = 1; //
while( RCEN ) continue;
while( !BF ) continue;
byte = SSPBUF;
return byte;
}
void I2C_Control_Write(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPBUF = w_device_address; // send the control byte (90 TCN75, EF BMP085)
while(!PIR1bits.SSPIF) // Wait for interrupt flag to go high indicating transmission is complete
{
i = 1;
// place to add a breakpoint if needed
}
PIR1bits.SSPIF=0;
}
void I2C_Control_Read(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPBUF = r_device_address; // send the control byte (90 TCN75, EF BMP085)
while(!PIR1bits.SSPIF) // Wait for interrupt flag to go high indicating transmission is complete
{
i = 1;
// place to add a breakpoint if needed
}
PIR1bits.SSPIF=0;
}
void I2C_Start_Bit(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPCON2bits.SEN=1; // send start bit
while(!PIR1bits.SSPIF) // Wait for the SSPIF bit to go back high before we load the data buffer
{
i = 1;
}
PIR1bits.SSPIF=0;
}
void I2C_check_idle()
{
unsigned char byte1; // R/W status: Is a transfer in progress?
unsigned char byte2; // Lower 5 bits: Acknowledge Sequence, Receive, STOP, Repeated START, START
do
{
byte1 = SSPSTAT & 0x04;
byte2 = SSPCON2 & 0x1F;
} while( byte1 | byte2 );
}
/*
* Send the repeated start message and wait repeated start to finish.
*/
void I2C_restart()
{
I2C_check_idle();
RSEN = 1; // Reinitiate start
while( RSEN ) continue;
}
void I2C_Stop_Bit(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPCON2bits.PEN=1; // send stop bit
while(!PIR1bits.SSPIF)
{
i = 1;
// Wait for interrupt flag to go high indicating transmission is complete
}
}
void I2C_NAK(void)
{
PIR1bits.SSPIF=0; // clear SSP interrupt bit
SSPCON2bits.ACKDT=1; // set the Acknowledge Data Bit- this means we are sending a No-Ack or 'NAK'
SSPCON2bits.ACKEN=1; // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
while(!PIR1bits.SSPIF) // Wait for interrupt flag to go high indicating transmission is complete
{
i = 1;
}
}
void read_imu_status(void){
I2C_Start_Bit(); // send start bit
I2C_Control_Write(); // send control byte with read set
Send_I2C_Data(0x00); // register
I2C_restart(); // restart
I2C_Control_Read();
RX_I2C_Data(); // read high
tempbyte1=byte;
// I2C_ACK(); // ACK
// RX_I2C_Data(); // read low
// tempbyte2=byte;
I2C_NAK(); // NAK
//I2C_restart();
I2C_Stop_Bit(); // Send Stop Bit
uart_tx(tempbyte1); //send data off raw by UART
}
void read_imu_cal_status(void){
I2C_Start_Bit(); // send start bit
I2C_Control_Write(); // send control byte with read set
Send_I2C_Data(0x35); // register
I2C_restart(); // restart
I2C_Control_Read();
RX_I2C_Data(); // read high
tempbyte1=byte;
// I2C_ACK(); // ACK
// RX_I2C_Data(); // read low
// tempbyte2=byte;
I2C_NAK(); // NAK
//I2C_restart();
I2C_Stop_Bit(); // Send Stop Bit
uart_tx(tempbyte1); //send data off raw by UART
}
void write_imu_9DOF(void)
{
I2C_Start_Bit(); // send start bit
I2C_Control_Write(); // send control byte
Send_I2C_Data(0x3D); // address
Send_I2C_Data(0x1C); // enable 9dof
I2C_Stop_Bit();
}
void i2c_muck(void)
{
I2C_Start_Bit(); // send start bit
I2C_Control_Write(); // send control byte with read set
//if (!SSP1CON2bits.ACKSTAT)
//LATCbits.LATC1 = 0; //device /ACked?
Send_I2C_Data(0x01); //pointer
Send_I2C_Data(0xE1); //1 shot, 12bit res
I2C_Stop_Bit();
__delay_10ms(50); //wait for conversion
I2C_Start_Bit(); // send start bit
I2C_Control_Write(); // send control byte with read set
// if (!SSP1CON2bits.ACKSTAT)
// LATCbits.LATC1 = 0;
Send_I2C_Data(0x00); //pointer
I2C_restart(); //restart
I2C_Control_Read();
RX_I2C_Data(); //read high
tempbyte1=byte;
I2C_ACK(); //ACK
RX_I2C_Data(); //read low
tempbyte2=byte;
I2C_NAK(); //NAK
//I2C_restart();
I2C_Stop_Bit(); // Send Stop Bit
uart_tx(tempbyte1); //send data off raw by UART
uart_tx(tempbyte2);
}
int main(void) {
// set up oscillator control register, using internal OSC at 16MHz.
OSCCONbits.IRCF = 0x07; //set OSCCON IRCF bits to select OSC frequency 16MHz
OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
//OSCTUNEbits.PLLEN = 0x01; // PLL ENABLE FOSC -> 64MHz
//OSCTUNEbits.TUN = 0x63; ///trim up that frequency to get closed to 115200
init_io();
serial_init();
SSPCON1bits.SSPM=0x08; // I2C Master mode, clock = Fosc/(4 * (SSPADD+1))
SSPCON1bits.SSPEN=1; // enable MSSP port
SSPADD = 0x09; // 100KHz
// ***********************************************************************************
__delay_10ms(100);
//init -- check and wait for IMU
// test
tempbyte1 = 0xFF;
uart_tx(tempbyte1); //send data off raw by UART
uart_tx(0xAA);
__delay_10ms(100);
read_imu_status();
//set IMU for 9DOF
__delay_10ms(10);
write_imu_9DOF();
// UART method
// uart_tx(0xAA); //Start
// uart_tx(0x00); //read
// uart_tx(0x3D); //address
// uart_tx(0x01); //length
// uart_tx(0x1C); //data 9DOF
__delay_10ms(50);
while (1) {
__delay_10ms(10); //delay for debugging
LATAbits.LA0 = 0; //good rx flag off
//LATCbits.LATC3 = 0;
//__delay_ms(149);
//LATCbits.LATC3 = 1; //debugging
read_imu_cal_status();
LATAbits.LA0 = 1; //good rx flag on
__delay_10ms(150); //delay for debugging
} //... wash, rinse, repeat
return;
}
So yeah, again also sloppy but maybe you can use some of it. Price is right anyways.
Okay well maybe you might have notices a TINY gap in posting… it seems I greatly underestimated the amount of time a baby consumes in your life. I have a 9 month old son now.. he is running around trying to chew on USB cables (his favorite)… he’s just now realizing a bit of independence and with a recent purchase of a nice Lenovo Yoga 900 to replace my couple month old dead Sony Vaio I now can develop from the couch.. though I have a porta-crib in the workshop 🙂
I came across a stash of iButton T-sense 1-wire sensors.. so I grabbed a couple and decided to check out 1-wire.
Maxim makes a 1-wire device called the DS18B20. It’s a 9-12 bit temperature sensor with the possibility of being powered by parasitic power from the data line. This cuts the signal path down to a single DQ line and a return. A company called iButtonLink produces a nice little wrapper around this device called a T-Sense. There are a couple pieces of software out there that will allow you to hook these up to monitoring systems, I don’t have any though. These devices come with a 64-bit address code and can be daisy-chained which makes having many of these devices monitored very nice.
At first I thought, ugh.. lame I have to send, and parse 64-bit codes in a little 8 bit micro.. doesn’t sound like a ton of fun for just fooling around on a day off.. thank fully they have a “Skip ROM” feature/command which works similar to a broadcast but can only be used when you have one device on the bus. If there is one thing left in this project I might consider finishing it’d be to add the addressing in and daisy-chain a few of these.
For my circuit I hooked up 5VDC (but later ran it on 3.3V just fine) and the 5VDC return on pins 1 & 2. Then the DQ link and return on pins 4&5. The signaling is interesting as the 1-wire bus needs a weak pull and works with an open collector circuits. The master starts all signaling, writes and reads. The 1’s and 0’s are based on how long the master or slave sinks the DQ line. To accomplish this in the PIC microcontroller I switched the port from an output to a three state input when I needed the port to be in weak-pull up mode (which is also hand when I need to sample the port for a response from a slave). The pull up on the resistor in my circuit is 10Kohm but I’ve seen 4.7KOhm and I’m sure anywhere in the neighborhood is fine. Finally if you do some digging you’ll notice I run this in low speed mode, if I remember correctly the “high speed” mode is 4x faster. I don’t think speed of data transfer is really relevant when you’re waiting for 750ms for a temperature conversation though.
T-sense sensor and 18F14k22 PIC Microcontroller on the breadboard
I initially started with just determining if there was a 1-wire device on the bus. If you perform a “reset” (master sinks the bus low for 480us then releases to hi-z for 70us and then performed a sample.. any (all) device(s) will sink the line slow to prove their presence…then another 410us of delay before continuing. I got this one first try.. better luck than my first time with I2C! I then wrote the code (including sampling tidbits of Microchip AN1199 code to optimize) to do an actual temperature conversion and request it (by commanding a “read scratch pad”). The device dumps all 9 bytes of it’s registers. On that note I just remembered I should mention I did NOTHING with the CRC byte.. that’s all you if you care.
My temperature conversion code looks like this: (bus control m = master or PIC, s=slave or sensor)
m RESET
s PRESENCE flag
m SKIP ROM (0xCC)
m CONV TEMP (0x44)
m HOLD DQ (Hold line high 750ms for 12bit conversion .. I am guessing we hold it high for the parasitic power supply)
m RESET
s PRESENCE flag
m SKIP ROM (0xCC)
m READ SCRATCHPAD (0xBE)
s 9 Bytes of Data
and that looks like…
1-wire data capture on the logic analyzer
The 12-bit conversion is basically 0.0625 deg C for every bit from 0C, The LSB holds a temperature sign.
The output of my program looks like..
Final Results!
There are tons of details on what a “1” is an what a “0” is, the ROM code (READ ROM, MATCH ROM), changing the configuration to 9bit for 94 ms conversions over 12bit 750ms conversions. This is all stuff you can grab out of the DS18B20 specification sheet or AN1199.
I hope you get some use out of this.. I still have enough of these T-Sense modules.. maybe if someone really wants one I’ll drop it in the mail… or perhaps we can set up a big box of electronics to ship around and I can dump some in there.
Well, it’s not finished but this is what I’m working on:
Balancing platform on a breadboard
Ta-da.. yet another balancing something-or-rather.. except this one isn’t so great at it’s balance. This was a big old fail. I went wrong with steppers… I should have just used DC motors and maybe the next version I will. I used Pololu A4988 stepper motor drivers (A4988-based) to control my steppers while attempting to use variable speed step resolution control. This worked okay but really while trying to drop holding current while balanced was a nightmare. Also the current usage was above what I consider useful… either backing the current limiting off to save some power (and your driver) then loosing control or you’re just cooking your motor driver and the current isn’t useful other than for a cute demonstration or a non-battery powered application.
I’m not dropping code, you can request it directly if you want it but it’s nothing magical. It has my own hacked up version of a PID loop. I didn’t use derivative. I think far too many cycles are wasted on it when PI will probably work for you. I haven’t fine tuned it because I’m unhappy with my results.
I was a little bummed out on past-Chas’ selection of an MMA7361 accelerometer; I had gotten it from Sparkfun who-knows-how-long-ago. The link leads to the retired product. It’s just plain voltage output per axis. I must have still been shying away from I2C at the point? The board shown below:
Accelerometer break-out board
For a micro I used a PIC 18F14K22 on the TAUTIC dev board with some AtomSoft breadboard goodies for troubleshooting, power and the serial interface (Breadboard Buddy). <– that link will probably die soon. Tindie updates a lot and products are replaced with newer versions.. if you sniff around you’ll find it.
I’ll move along to another project now, perhaps order some parts and revisit this one again. If you run along this and you’ve done this with steppers I’d be interested in knowing how it worked out for you.
All software and code I created is released under the MIT license...Enjoy!
All hardware I have designed and released on this website is released under the TAPR Open Hardware License v1.0 . Please consult individual manufacturers of modules, development kits and other pieces of hardware for their individual licensing requirements.