implemented own I2C protocol
This commit is contained in:
parent
03dfdc0737
commit
791a7820c3
235
pic/MDR/I2C.c
235
pic/MDR/I2C.c
|
@ -1,159 +1,126 @@
|
||||||
/* software bit banging implementation
|
#define __16f1847
|
||||||
* based on https://en.wikipedia.org/wiki/I2c example code
|
#include <pic16f1847.h>
|
||||||
*
|
#include <stdint.h>
|
||||||
* there is no external pull-up on SCL, and the internal one is too weak
|
|
||||||
* SCL must be driven
|
|
||||||
* but that is ok as we are the only master
|
|
||||||
* there is an external pull-up on SDA, and the internal one is too weak
|
|
||||||
* SDA goes to 3V instead of 5V due to the external pull-up
|
|
||||||
* but the 24LC256 still seems to work
|
|
||||||
*/
|
|
||||||
#include "I2C.h"
|
#include "I2C.h"
|
||||||
|
|
||||||
// Hardware-specific support functions that MUST be customized:
|
/* set clock high (default state) */
|
||||||
/* even when doing nothing, the max speed will be 100kHz, while the chip supports 400kHz */
|
#define release_SCL() LATB |= SCL
|
||||||
#define I2C_delay()
|
/* set clock low */
|
||||||
|
#define hold_SCL() LATB &= ~SCL
|
||||||
|
/* set data high (default state) */
|
||||||
|
#define release_SDA() LATB |= SDA
|
||||||
|
/* set data low */
|
||||||
|
#define hold_SDA() LATB &= ~SDA
|
||||||
|
|
||||||
/* Set SCL as input and return current level of line, 0 or 1 */
|
/* a small delay for better clock */
|
||||||
bool read_SCL(void)
|
#define WAIT 1
|
||||||
|
/*
|
||||||
|
void delay(void)
|
||||||
{
|
{
|
||||||
LATB |= SCL; /* set high (must be driven because no pull-up */
|
uint8_t wait=WAIT;
|
||||||
return 1;
|
while (wait--);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#define delay() __asm\
|
||||||
|
nop\
|
||||||
|
nop\
|
||||||
|
nop\
|
||||||
|
nop\
|
||||||
|
__endasm
|
||||||
|
|
||||||
|
/* read SDA
|
||||||
|
* don't forget to set SDA as input in the begining
|
||||||
|
* are set SDA as output in the end
|
||||||
|
*/
|
||||||
|
uint8_t read_SDA(void)
|
||||||
|
{
|
||||||
|
hold_SCL(); /* set clock to low for SDA to change */
|
||||||
|
delay(); /* wait for SDA to change */
|
||||||
|
delay();
|
||||||
|
delay();
|
||||||
|
release_SCL(); /* SDA should be valid when clock is high */
|
||||||
|
delay();
|
||||||
|
/* we don't verify the SCL state as only we can drive it */
|
||||||
|
if (PORTB&SDA) { /* read bit */
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set SDA as input and return current level of line, 0 or 1 */
|
uint8_t read_ack(void)
|
||||||
bool read_SDA(void)
|
|
||||||
{
|
{
|
||||||
|
uint8_t ack = 0;
|
||||||
TRISB |= SDA; /* set as input */
|
TRISB |= SDA; /* set as input */
|
||||||
return (PORTB&SDA)!=0;
|
ack = read_SDA();
|
||||||
|
TRISB &= ~SDA; /* set back to output */
|
||||||
|
hold_SCL();
|
||||||
|
return ack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actively drive SCL signal low */
|
/* start bit is when SDA goes low while SCL is high */
|
||||||
void clear_SCL(void)
|
void send_start(void)
|
||||||
{
|
{
|
||||||
LATB &= ~SCL; /* set low */
|
release_SCL();
|
||||||
|
delay();
|
||||||
|
release_SDA(); /* ensure SDA is high on the beginning */
|
||||||
|
delay();
|
||||||
|
hold_SDA();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actively drive SDA signal low */
|
void send_stop(void)
|
||||||
void clear_SDA(void)
|
|
||||||
{
|
{
|
||||||
TRISB &= ~SDA; /* set as output */
|
hold_SDA(); /* be sure SDA is low */
|
||||||
LATB &= ~SDA; /* set low */
|
release_SCL();
|
||||||
|
delay();
|
||||||
|
release_SDA(); /* set SDA high while SCL is high */
|
||||||
}
|
}
|
||||||
|
|
||||||
void arbitration_lost(void)
|
void send_1(void)
|
||||||
{
|
{
|
||||||
/* do nothing, since we are the only master */
|
hold_SCL(); /* value can change when SCL is low */
|
||||||
|
delay();
|
||||||
|
release_SDA(); /* set the right state */
|
||||||
|
delay();
|
||||||
|
release_SCL(); /* value will be read when SCL goes high */
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
void send_0(void)
|
||||||
}
|
|
||||||
// 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;
|
hold_SCL(); /* value can change when SCL is low */
|
||||||
bool nack;
|
delay();
|
||||||
if (send_start) {
|
hold_SDA(); /* set the right state */
|
||||||
i2c_start_cond();
|
delay();
|
||||||
}
|
release_SCL(); /* value will be read when SCL goes high */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send byte and get ack */
|
||||||
|
uint8_t send_byte(uint8_t byte)
|
||||||
|
{
|
||||||
|
uint8_t bit;
|
||||||
|
/* send every bit, MSb first */
|
||||||
for (bit = 0; bit < 8; bit++) {
|
for (bit = 0; bit < 8; bit++) {
|
||||||
i2c_write_bit((byte & 0x80) != 0);
|
if (byte&0x80) {
|
||||||
|
send_1();
|
||||||
|
} else {
|
||||||
|
send_0();
|
||||||
|
}
|
||||||
byte <<= 1;
|
byte <<= 1;
|
||||||
}
|
}
|
||||||
nack = i2c_read_bit();
|
return read_ack();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t read_byte(void)
|
||||||
|
{
|
||||||
|
uint8_t byte = 0;
|
||||||
|
uint8_t bit;
|
||||||
|
TRISB |= SDA; /* set as input */
|
||||||
|
/* read 8 bits */
|
||||||
|
for (bit=0; bit<8; bit++) {
|
||||||
|
byte += read_SDA();
|
||||||
|
byte <<= 1;
|
||||||
|
}
|
||||||
|
TRISB &= ~SDA; /* set back to output */
|
||||||
|
send_0(); /* send ack */
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
|
@ -2,15 +2,11 @@
|
||||||
#include <pic16f1847.h>
|
#include <pic16f1847.h>
|
||||||
#include <stdint.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 SCL _RB6 /* pin 12, external 24LC256 I2C EEPROM memory */
|
||||||
#define SDA _RB7 /* pin 13, 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.
|
void send_start(void);
|
||||||
bool i2c_write_byte(bool send_start, bool send_stop, unsigned char byte);
|
void send_stop(void);
|
||||||
|
uint8_t send_byte(uint8_t byte);
|
||||||
// Read a byte from I2C bus
|
uint8_t read_byte(void);
|
||||||
unsigned char i2c_read_byte(bool nack, bool send_stop);
|
|
||||||
|
|
|
@ -116,8 +116,8 @@ void init (void) {
|
||||||
* this PIC16F1847 offers hardware I2C, but not on those pins
|
* this PIC16F1847 offers hardware I2C, but not on those pins
|
||||||
* so we also have to use software bit banging I2C
|
* so we also have to use software bit banging I2C
|
||||||
*/
|
*/
|
||||||
TRISB &= ~SCL; /* set as output (it must be driven because there is no pull-up */
|
TRISB &= ~(SCL|SDA); /* set as output (it must be driven because there is no pull-up */
|
||||||
LATB |= SCL; /* set clock as high (default pull-up state) */
|
LATB |= SCL|SDA; /* set as high (default pull-up state) */
|
||||||
|
|
||||||
PEIE = 1; /* enable peripheral interrupt (for timer 2) */
|
PEIE = 1; /* enable peripheral interrupt (for timer 2) */
|
||||||
GIE = 1; /* golablly enable interrupts */
|
GIE = 1; /* golablly enable interrupts */
|
||||||
|
@ -161,11 +161,14 @@ static void interrupt(void) __interrupt 0
|
||||||
if (switches&SWITCH1) { /* switch 1 changed */
|
if (switches&SWITCH1) { /* switch 1 changed */
|
||||||
if (PORTB&SWITCH1) { /* switch 1 released */
|
if (PORTB&SWITCH1) { /* switch 1 released */
|
||||||
led_off(); /* reset LED */
|
led_off(); /* reset LED */
|
||||||
i2c_write_byte(1,0,0xa0);
|
send_start();
|
||||||
i2c_write_byte(0,1,0xa0);
|
send_byte(0xa0);
|
||||||
// i2c_write_byte(0,1,0x17);
|
send_byte(0x1c);
|
||||||
// i2c_write_byte(1,0,0xa1);
|
send_byte(0x17);
|
||||||
// i2c_read_byte(0,0);
|
send_start();
|
||||||
|
send_byte(0xa1);
|
||||||
|
read_byte();
|
||||||
|
send_stop();
|
||||||
} else { /* switch 1 pressed */
|
} else { /* switch 1 pressed */
|
||||||
led_on(); /* test LED */
|
led_on(); /* test LED */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue