diff options
Diffstat (limited to 'platforms/avr/drivers/i2c_slave.c')
-rw-r--r-- | platforms/avr/drivers/i2c_slave.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/platforms/avr/drivers/i2c_slave.c b/platforms/avr/drivers/i2c_slave.c new file mode 100644 index 000000000..2907f164c --- /dev/null +++ b/platforms/avr/drivers/i2c_slave.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* Copyright (C) 2019 Elia Ritterbusch | ||
2 | + | ||
3 | * This program is free software: you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation, either version 3 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | /* Library made by: g4lvanix | ||
17 | * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib | ||
18 | */ | ||
19 | |||
20 | #include <stddef.h> | ||
21 | #include <avr/io.h> | ||
22 | #include <util/twi.h> | ||
23 | #include <avr/interrupt.h> | ||
24 | #include <stdbool.h> | ||
25 | |||
26 | #include "i2c_slave.h" | ||
27 | |||
28 | #if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
29 | # include "transactions.h" | ||
30 | |||
31 | static volatile bool is_callback_executor = false; | ||
32 | #endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
33 | |||
34 | volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; | ||
35 | |||
36 | static volatile uint8_t buffer_address; | ||
37 | static volatile bool slave_has_register_set = false; | ||
38 | |||
39 | void i2c_slave_init(uint8_t address) { | ||
40 | // load address into TWI address register | ||
41 | TWAR = address; | ||
42 | // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt | ||
43 | TWCR = (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWEN); | ||
44 | } | ||
45 | |||
46 | void i2c_slave_stop(void) { | ||
47 | // clear acknowledge and enable bits | ||
48 | TWCR &= ~((1 << TWEA) | (1 << TWEN)); | ||
49 | } | ||
50 | |||
51 | ISR(TWI_vect) { | ||
52 | uint8_t ack = 1; | ||
53 | |||
54 | switch (TW_STATUS) { | ||
55 | case TW_SR_SLA_ACK: | ||
56 | // The device is now a slave receiver | ||
57 | slave_has_register_set = false; | ||
58 | #if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
59 | is_callback_executor = false; | ||
60 | #endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
61 | break; | ||
62 | |||
63 | case TW_SR_DATA_ACK: | ||
64 | // This device is a slave receiver and has received data | ||
65 | // First byte is the location then the bytes will be writen in buffer with auto-increment | ||
66 | if (!slave_has_register_set) { | ||
67 | buffer_address = TWDR; | ||
68 | |||
69 | if (buffer_address >= I2C_SLAVE_REG_COUNT) { // address out of bounds dont ack | ||
70 | ack = 0; | ||
71 | buffer_address = 0; | ||
72 | } | ||
73 | slave_has_register_set = true; // address has been received now fill in buffer | ||
74 | |||
75 | #if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
76 | // Work out if we're attempting to execute a callback | ||
77 | is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset; | ||
78 | #endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
79 | } else { | ||
80 | i2c_slave_reg[buffer_address] = TWDR; | ||
81 | buffer_address++; | ||
82 | |||
83 | #if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
84 | // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID | ||
85 | if (is_callback_executor) { | ||
86 | split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id]; | ||
87 | if (trans->slave_callback) { | ||
88 | trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); | ||
89 | } | ||
90 | } | ||
91 | #endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) | ||
92 | } | ||
93 | break; | ||
94 | |||
95 | case TW_ST_SLA_ACK: | ||
96 | case TW_ST_DATA_ACK: | ||
97 | // This device is a slave transmitter and master has requested data | ||
98 | TWDR = i2c_slave_reg[buffer_address]; | ||
99 | buffer_address++; | ||
100 | break; | ||
101 | |||
102 | case TW_BUS_ERROR: | ||
103 | // We got an error, reset i2c | ||
104 | TWCR = 0; | ||
105 | default: | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | // Reset i2c state machine to be ready for next interrupt | ||
110 | TWCR |= (1 << TWIE) | (1 << TWINT) | (ack << TWEA) | (1 << TWEN); | ||
111 | } | ||