add I2C library
This commit is contained in:
parent
d6cefb1308
commit
df5e3f7521
|
@ -0,0 +1,161 @@
|
|||
/* software bit banging implementation
|
||||
* based on https://en.wikipedia.org/wiki/I2c example code
|
||||
*/
|
||||
#include "I2C.h"
|
||||
|
||||
// Hardware-specific support functions that MUST be customized:
|
||||
#define I2CSPEED 100
|
||||
void I2C_delay()
|
||||
{
|
||||
volatile int v = 0;
|
||||
int i;
|
||||
for (i=0; i < I2CSPEED/2; i++) {
|
||||
v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set SCL as input and return current level of line, 0 or 1 */
|
||||
bool read_SCL(void)
|
||||
{
|
||||
PORTB |= SCL; /* set as input */
|
||||
WPUB |= SCL; /* enable pull-up */
|
||||
return (PORTB&SCL);
|
||||
}
|
||||
|
||||
/* Set SDA as input and return current level of line, 0 or 1 */
|
||||
bool read_SDA(void)
|
||||
{
|
||||
PORTB |= SDA; /* set as input */
|
||||
WPUB |= SDA; /* enable pull-up */
|
||||
return (PORTB&SDA);
|
||||
}
|
||||
|
||||
/* Actively drive SCL signal low */
|
||||
void clear_SCL(void)
|
||||
{
|
||||
PORTB &= ~SCL; /* set as output */
|
||||
PORTB &= ~SDA; /* set low */
|
||||
}
|
||||
|
||||
/* Actively drive SDA signal low */
|
||||
void clear_SDA(void)
|
||||
{
|
||||
PORTB &= ~SCL; /* set as output */
|
||||
PORTB &= ~SDA; /* set low */
|
||||
}
|
||||
|
||||
void arbitration_lost(void)
|
||||
{
|
||||
/* do nothing, since we are the only master */
|
||||
}
|
||||
|
||||
bool started = false; // global data
|
||||
void i2c_start_cond(void) {
|
||||
if (started) { // if started, do a restart cond
|
||||
// set SDA to 1
|
||||
read_SDA();
|
||||
I2C_delay();
|
||||
while (read_SCL() == 0) { // Clock stretching
|
||||
// You should add timeout to this loop
|
||||
}
|
||||
// Repeated start setup time, minimum 4.7us
|
||||
I2C_delay();
|
||||
}
|
||||
if (read_SDA() == 0) {
|
||||
arbitration_lost();
|
||||
}
|
||||
// SCL is high, set SDA from 1 to 0.
|
||||
clear_SDA();
|
||||
I2C_delay();
|
||||
clear_SCL();
|
||||
started = true;
|
||||
}
|
||||
|
||||
void i2c_stop_cond(void){
|
||||
// set SDA to 0
|
||||
clear_SDA();
|
||||
I2C_delay();
|
||||
// Clock stretching
|
||||
while (read_SCL() == 0) {
|
||||
// add timeout to this loop.
|
||||
}
|
||||
// Stop bit setup time, minimum 4us
|
||||
I2C_delay();
|
||||
// SCL is high, set SDA from 0 to 1
|
||||
if (read_SDA() == 0) {
|
||||
arbitration_lost();
|
||||
}
|
||||
I2C_delay();
|
||||
started = false;
|
||||
}
|
||||
|
||||
// Write a bit to I2C bus
|
||||
void i2c_write_bit(bool bit) {
|
||||
if (bit) {
|
||||
read_SDA();
|
||||
} else {
|
||||
clear_SDA();
|
||||
}
|
||||
I2C_delay();
|
||||
while (read_SCL() == 0) { // Clock stretching
|
||||
// You should add timeout to this loop
|
||||
}
|
||||
// SCL is high, now data is valid
|
||||
// If SDA is high, check that nobody else is driving SDA
|
||||
if (bit && read_SDA() == 0) {
|
||||
arbitration_lost();
|
||||
}
|
||||
I2C_delay();
|
||||
clear_SCL();
|
||||
}
|
||||
|
||||
// Read a bit from I2C bus
|
||||
bool i2c_read_bit(void) {
|
||||
bool bit;
|
||||
// Let the slave drive data
|
||||
read_SDA();
|
||||
I2C_delay();
|
||||
while (read_SCL() == 0) { // Clock stretching
|
||||
// You should add timeout to this loop
|
||||
}
|
||||
// SCL is high, now data is valid
|
||||
bit = read_SDA();
|
||||
I2C_delay();
|
||||
clear_SCL();
|
||||
return bit;
|
||||
}
|
||||
|
||||
// Write a byte to I2C bus. Return 0 if ack by the slave.
|
||||
bool i2c_write_byte(bool send_start,
|
||||
bool send_stop,
|
||||
unsigned char byte) {
|
||||
unsigned bit;
|
||||
bool nack;
|
||||
if (send_start) {
|
||||
i2c_start_cond();
|
||||
}
|
||||
for (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
|
||||
unsigned char i2c_read_byte(bool nack, bool send_stop) {
|
||||
unsigned char byte = 0;
|
||||
unsigned bit;
|
||||
for (bit = 0; bit < 8; bit++) {
|
||||
byte = (byte << 1) | i2c_read_bit();
|
||||
}
|
||||
i2c_write_bit(nack);
|
||||
if (send_stop) {
|
||||
i2c_stop_cond();
|
||||
}
|
||||
return byte;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#define __16f1847
|
||||
#include <pic16f1847.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define bool uint8_t
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#define SCL _RB6 /* pin 12, external 24LC256 I2C EEPROM memory */
|
||||
#define SDA _RB7 /* pin 13, external 24LC256 I2C EEPROM memory */
|
||||
|
||||
// Write a byte to I2C bus. Return 0 if ack by the slave.
|
||||
bool i2c_write_byte(bool send_start, bool send_stop, unsigned char byte);
|
||||
|
||||
// Read a byte from I2C bus
|
||||
unsigned char i2c_read_byte(bool nack, bool send_stop);
|
|
@ -19,6 +19,7 @@
|
|||
#define __16f1847
|
||||
#include <pic16f1847.h>
|
||||
#include <stdint.h>
|
||||
#include "I2C.h"
|
||||
|
||||
/* the peripherals connected to the pins */
|
||||
#define RELAY1 _RA2 /* pin 1 */
|
||||
|
@ -32,8 +33,8 @@
|
|||
/* pin 9 is for a secret function, set by jumper WJ1, Vdd per default */
|
||||
/* pin 10 is not connected */
|
||||
/* pin 11 in not connected */
|
||||
#define MEMORY_CLK _RB6 /* pin 12, external 24LC256 EEPROM */
|
||||
#define MEMORY_DATA _RB7 /* pin 13, external 24LC256 EEPROM */
|
||||
/* pin 12, external 24LC256 EEPROM */
|
||||
/* pin 13, external 24LC256 EEPROM */
|
||||
/* pin 14 is Vdd (5V) */
|
||||
/* pin 15 is clonnected to external 4MHz ceramic resonator */
|
||||
/* pin 16 is clonnected to external 4MHz ceramic resonator */
|
||||
|
@ -72,12 +73,13 @@ uint16_t __at(_CONFIG2) __CONFIG2 = _LVP_ON & /* enable low voltage programming
|
|||
/* initialize micro-conroller */
|
||||
void init (void) {
|
||||
/* configure IO */
|
||||
NOT_WPUEN = 0; /* enable individual pull-ups */
|
||||
ANSELA = 0; /* all pins are digital */
|
||||
ANSELB = 0; /* all pins are digital */
|
||||
TRISA |= RADIO; /* radio signal is an input */
|
||||
TRISA &= ~(RELAY1|RELAY2); /* relays are outputs */
|
||||
TRISB |= SWITCH1|SWITCH2; /* switches are inputs (with external pull-ups) */
|
||||
TRISB &= ~(LED|MEMORY_CLK|MEMORY_DATA); /* LED is I2C memory are outputs */
|
||||
TRISB &= ~(LED); /* LED is an output (used a sink) */
|
||||
led_off(); /* starting state */
|
||||
|
||||
/* configure switches input */
|
||||
|
@ -109,6 +111,14 @@ void init (void) {
|
|||
TMR2IE = 1; /* enable timer 2 interrupt */
|
||||
TMR2IF = 0; /* clear timer 2 interrupt */
|
||||
|
||||
/* the MDR originally uses a PIC16C54A
|
||||
* this does does not provide hardware I2C function
|
||||
* they implement I2C in software (it's even a bit buggy)
|
||||
* it even does not used open collectors (with pull-ups), but drives the signals
|
||||
* this PIC16F1847 offers hardware I2C, but not on those pins
|
||||
* so we also have to use software bit banging I2C
|
||||
*/
|
||||
|
||||
PEIE = 1; /* enable peripheral interrupt (for timer 2) */
|
||||
GIE = 1; /* golablly enable interrupts */
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue