aboutsummaryrefslogtreecommitdiffstats
path: root/keyboards/deltasplit75/i2c.c
blob: 084c890c405fa6f7ebfc1be7a699e3d120e5c482 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <util/twi.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include <stdbool.h>
#include "i2c.h"

#ifdef USE_I2C

// Limits the amount of we wait for any one i2c transaction.
// Since were running SCL line 100kHz (=> 10μs/bit), and each transactions is
// 9 bits, a single transaction will take around 90μs to complete.
//
// (F_CPU/SCL_CLOCK)  =>  # of μC cycles to transfer a bit
// poll loop takes at least 8 clock cycles to execute
#define I2C_LOOP_TIMEOUT (9+1)*(F_CPU/SCL_CLOCK)/8

#define BUFFER_POS_INC() (slave_buffer_pos = (slave_buffer_pos+1)%SLAVE_BUFFER_SIZE)

volatile uint8_t i2c_slave_buffer[SLAVE_BUFFER_SIZE];

static volatile uint8_t slave_buffer_pos;
static volatile bool slave_has_register_set = false;

// Wait for an i2c operation to finish
inline static
void i2c_delay(void) {
  uint16_t lim = 0;
  while(!(TWCR & (1<<TWINT)) && lim < I2C_LOOP_TIMEOUT)
    lim++;

  // easier way, but will wait slightly longer
  // _delay_us(100);
}

// Setup twi to run at 100kHz
void i2c_master_init(void) {
  // no prescaler
  TWSR = 0;
  // Set TWI clock frequency to SCL_CLOCK. Need TWBR>10.
  // Check datasheets for more info.
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;
}

// Start a transaction with the given i2c slave address. The direction of the
// transfer is set with I2C_READ and I2C_WRITE.
// returns: 0 => success
//          1 => error
uint8_t i2c_master_start(uint8_t address) {
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA);

  i2c_delay();

  // check that we started successfully
  if ( (TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START))
    return 1;

  TWDR = address;
  TWCR = (1<<TWINT) | (1<<TWEN);

  i2c_delay();

  if ( (TW_STATUS != TW_MT_SLA_ACK) && (TW_STATUS != TW_MR_SLA_ACK) )
    return 1; // slave did not acknowledge
  else
    return 0; // success
}


// Finish the i2c transaction.
void i2c_master_stop(void) {
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

  uint16_t lim = 0;
  while(!(TWCR & (1<<TWSTO)) && lim < I2C_LOOP_TIMEOUT)
    lim++;
}

// Write one byte to the i2c slave.
// returns 0 => slave ACK
//         1 => slave NACK
uint8_t i2c_master_write(uint8_t data) {
  TWDR = data;
  TWCR = (1<<TWINT) | (1<<TWEN);

  i2c_delay();

  // check if the slave acknowledged us
  return (TW_STATUS == TW_MT_DATA_ACK) ? 0 : 1;
}

// Read one byte from the i2c slave. If ack=1 the slave is acknowledged,
// if ack=0 the acknowledge bit is not set.
// returns: byte read from i2c device
uint8_t i2c_master_read(int ack) {
  TWCR = (1<<TWINT) | (1<<TWEN) | (ack<<TWEA);

  i2c_delay();
  return TWDR;
}

void i2c_reset_state(void) {
  TWCR = 0;
}

void i2c_slave_init(uint8_t address) {
  TWAR = address << 0; // slave i2c address
  // TWEN  - twi enable
  // TWEA  - enable address acknowledgement
  // TWINT - twi interrupt flag
  // TWIE  - enable the twi interrupt
  TWCR = (1<<TWIE) | (1<<TWEA) | (1<<TWINT) | (1<<TWEN);
}

ISR(TWI_vect);

ISR(TWI_vect) {
  uint8_t ack = 1;
  switch(TW_STATUS) {
    case TW_SR_SLA_ACK:
      // this device has been addressed as a slave receiver
      slave_has_register_set = false;
      break;

    case TW_SR_DATA_ACK:
      // this device has received data as a slave receiver
      // The first byte that we receive in this transaction sets the location
      // of the read/write location of the slaves memory that it exposes over
      // i2c.  After that, bytes will be written at slave_buffer_pos, incrementing
      // slave_buffer_pos after each write.
      if(!slave_has_register_set) {
        slave_buffer_pos = TWDR;
        // don't acknowledge the master if this memory loctaion is out of bounds
        if ( slave_buffer_pos >= SLAVE_BUFFER_SIZE ) {
          ack = 0;
          slave_buffer_pos = 0;
        }
        slave_has_register_set = true;
      } else {
        i2c_slave_buffer[slave_buffer_pos] = TWDR;
        BUFFER_POS_INC();
      }
      break;

    case TW_ST_SLA_ACK:
    case TW_ST_DATA_ACK:
      // master has addressed this device as a slave transmitter and is
      // requesting data.
      TWDR = i2c_slave_buffer[slave_buffer_pos];
      BUFFER_POS_INC();
      break;

    case TW_BUS_ERROR: // something went wrong, reset twi state
      TWCR = 0;
    default:
      break;
  }
  // Reset everything, so we are ready for the next TWI interrupt
  TWCR |= (1<<TWIE) | (1<<TWINT) | (ack<<TWEA) | (1<<TWEN);
}
#endif
an> 64; type Cnode_Unsigned is record Val : Unsigned_64; end record; for Cnode_Unsigned'Size use 64; type Cnode_Float is record Val : IEEE_Float_64; end record; for Cnode_Float'Size use 64; type Cnode_Enum is record Id : O_Ident; Val : Uns32; end record; for Cnode_Enum'Size use 64; type Cnode_Addr is record Decl : O_Dnode; Pad : Int32; end record; for Cnode_Addr'Size use 64; type Cnode_Aggr is record Els : Int32; Nbr : Int32; end record; for Cnode_Aggr'Size use 64; type Cnode_Sizeof is record Atype : O_Tnode; Pad : Int32; end record; for Cnode_Sizeof'Size use 64; type Cnode_Union is record El : O_Cnode; Field : O_Fnode; end record; for Cnode_Union'Size use 64; package Cnodes is new GNAT.Table (Table_Component_Type => Cnode_Common, Table_Index_Type => O_Cnode, Table_Low_Bound => 2, Table_Initial => 128, Table_Increment => 100); function Get_Const_Kind (Cst : O_Cnode) return OC_Kind is begin return Cnodes.Table (Cst).Kind; end Get_Const_Kind; function Get_Const_Type (Cst : O_Cnode) return O_Tnode is begin return Cnodes.Table (Cst).Lit_Type; end Get_Const_Type; function Get_Const_U64 (Cst : O_Cnode) return Unsigned_64 is function To_Cnode_Unsigned is new Ada.Unchecked_Conversion (Cnode_Common, Cnode_Unsigned); begin return To_Cnode_Unsigned (Cnodes.Table (Cst + 1)).Val; end Get_Const_U64; function Get_Const_I64 (Cst : O_Cnode) return Integer_64 is function To_Cnode_Signed is new Ada.Unchecked_Conversion (Cnode_Common, Cnode_Signed); begin return To_Cnode_Signed (Cnodes.Table (Cst + 1)).Val; end Get_Const_I64; function Get_Const_F64 (Cst : O_Cnode) return IEEE_Float_64 is function To_Cnode_Float is new Ada.Unchecked_Conversion (Cnode_Common, Cnode_Float); begin return To_Cnode_Float (Cnodes.Table (Cst + 1)).Val; end Get_Const_F64; function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Signed, Target => Cnode_Common); function New_Signed_Literal (Ltype : O_Tnode; Value : Integer_64) return O_Cnode is Res : O_Cnode; begin Cnodes.Append (Cnode_Common'(Kind => OC_Signed, Lit_Type => Ltype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Signed'(Val => Value))); return Res; end New_Signed_Literal; function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Unsigned_64, Target => Cnode_Common); function New_Unsigned_Literal (Ltype : O_Tnode; Value : Unsigned_64) return O_Cnode is Res : O_Cnode; begin Cnodes.Append (Cnode_Common'(Kind => OC_Unsigned, Lit_Type => Ltype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Value)); return Res; end New_Unsigned_Literal; -- function Get_Const_Literal (Cst : O_Cnode) return Uns32 is -- begin -- return Cnodes.Table (Cst).Val; -- end Get_Const_Literal; function To_Uns64 is new Ada.Unchecked_Conversion (Source => Cnode_Common, Target => Uns64); function Get_Const_U32 (Cst : O_Cnode) return Uns32 is begin return Uns32 (To_Uns64 (Cnodes.Table (Cst + 1))); end Get_Const_U32; function Get_Const_R64 (Cst : O_Cnode) return Uns64 is begin return To_Uns64 (Cnodes.Table (Cst + 1)); end Get_Const_R64; function Get_Const_Low (Cst : O_Cnode) return Uns32 is V : Uns64; begin V := Get_Const_R64 (Cst); return Uns32 (V and 16#Ffff_Ffff#); end Get_Const_Low; function Get_Const_High (Cst : O_Cnode) return Uns32 is V : Uns64; begin V := Get_Const_R64 (Cst); return Uns32 (Shift_Right (V, 32) and 16#Ffff_Ffff#); end Get_Const_High; function Get_Const_Low (Cst : O_Cnode) return Int32 is V : Uns64; begin V := Get_Const_R64 (Cst); return To_Int32 (Uns32 (V and 16#Ffff_Ffff#)); end Get_Const_Low; function Get_Const_High (Cst : O_Cnode) return Int32 is V : Uns64; begin V := Get_Const_R64 (Cst); return To_Int32 (Uns32 (Shift_Right (V, 32) and 16#Ffff_Ffff#)); end Get_Const_High; function New_Float_Literal (Ltype : O_Tnode; Value : IEEE_Float_64) return O_Cnode is Res : O_Cnode; function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Float, Target => Cnode_Common); begin Cnodes.Append (Cnode_Common'(Kind => OC_Float, Lit_Type => Ltype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Float'(Val => Value))); return Res; end New_Float_Literal; function New_Null_Access (Ltype : O_Tnode) return O_Cnode is begin Cnodes.Append (Cnode_Common'(Kind => OC_Null, Lit_Type => Ltype)); return Cnodes.Last; end New_Null_Access; function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Addr, Target => Cnode_Common); function To_Cnode_Addr is new Ada.Unchecked_Conversion (Source => Cnode_Common, Target => Cnode_Addr); function New_Global_Unchecked_Address (Decl : O_Dnode; Atype : O_Tnode) return O_Cnode is Res : O_Cnode; begin Cnodes.Append (Cnode_Common'(Kind => OC_Address, Lit_Type => Atype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Addr'(Decl => Decl, Pad => 0))); return Res; end New_Global_Unchecked_Address; function New_Global_Address (Decl : O_Dnode; Atype : O_Tnode) return O_Cnode is Res : O_Cnode; begin Cnodes.Append (Cnode_Common'(Kind => OC_Address, Lit_Type => Atype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Addr'(Decl => Decl, Pad => 0))); return Res; end New_Global_Address; function New_Subprogram_Address (Subprg : O_Dnode; Atype : O_Tnode) return O_Cnode is Res : O_Cnode; begin Cnodes.Append (Cnode_Common'(Kind => OC_Subprg_Address, Lit_Type => Atype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Addr'(Decl => Subprg, Pad => 0))); return Res; end New_Subprogram_Address; function Get_Const_Decl (Cst : O_Cnode) return O_Dnode is begin return To_Cnode_Addr (Cnodes.Table (Cst + 1)).Decl; end Get_Const_Decl; function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Enum, Target => Cnode_Common); function To_Cnode_Enum is new Ada.Unchecked_Conversion (Source => Cnode_Common, Target => Cnode_Enum); --function Get_Named_Literal_Id (Lit : O_Cnode) return O_Ident is --begin -- return To_Cnode_Enum (Cnodes.Table (Lit + 1)).Id; --end Get_Named_Literal_Id; function New_Named_Literal (Atype : O_Tnode; Id : O_Ident; Val : Uns32; Prev : O_Cnode) return O_Cnode is Res : O_Cnode; begin Cnodes.Append (Cnode_Common'(Kind => OC_Lit, Lit_Type => Atype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Enum'(Id => Id, Val => Val))); if Prev /= O_Cnode_Null then if Prev + 2 /= Res then raise Syntax_Error; end if; end if; return Res; end New_Named_Literal; function Get_Lit_Ident (L : O_Cnode) return O_Ident is begin return To_Cnode_Enum (Cnodes.Table (L + 1)).Id; end Get_Lit_Ident; function Get_Lit_Value (L : O_Cnode) return Uns32 is begin return To_Cnode_Enum (Cnodes.Table (L + 1)).Val; end Get_Lit_Value; function Get_Lit_Chain (L : O_Cnode) return O_Cnode is begin return L + 2; end Get_Lit_Chain; package Els is new GNAT.Table (Table_Component_Type => O_Cnode, Table_Index_Type => Int32, Table_Low_Bound => 2, Table_Initial => 128, Table_Increment => 100); function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Aggr, Target => Cnode_Common); function To_Cnode_Aggr is new Ada.Unchecked_Conversion (Source => Cnode_Common, Target => Cnode_Aggr); procedure Start_Record_Aggr (List : out O_Record_Aggr_List; Atype : O_Tnode) is Val : Int32; Num : Uns32; begin Num := Get_Type_Record_Nbr_Fields (Atype); Val := Els.Allocate (Integer (Num)); Cnodes.Append (Cnode_Common'(Kind => OC_Record, Lit_Type => Atype)); List := (Res => Cnodes.Last, Rec_Field => Get_Type_Record_Fields (Atype), El => Val); Cnodes.Append (To_Cnode_Common (Cnode_Aggr'(Els => Val, Nbr => Int32 (Num)))); end Start_Record_Aggr; procedure New_Record_Aggr_El (List : in out O_Record_Aggr_List; Value : O_Cnode) is begin Els.Table (List.El) := Value; List.El := List.El + 1; end New_Record_Aggr_El; procedure Finish_Record_Aggr (List : in out O_Record_Aggr_List; Res : out O_Cnode) is begin Res := List.Res; end Finish_Record_Aggr; procedure Start_Array_Aggr (List : out O_Array_Aggr_List; Atype : O_Tnode) is Val : Int32; Num : Uns32; begin Num := Get_Type_Subarray_Length (Atype); Val := Els.Allocate (Integer (Num)); Cnodes.Append (Cnode_Common'(Kind => OC_Array, Lit_Type => Atype)); List := (Res => Cnodes.Last, El => Val); Cnodes.Append (To_Cnode_Common (Cnode_Aggr'(Els => Val, Nbr => Int32 (Num)))); end Start_Array_Aggr; procedure New_Array_Aggr_El (List : in out O_Array_Aggr_List; Value : O_Cnode) is begin Els.Table (List.El) := Value; List.El := List.El + 1; end New_Array_Aggr_El; procedure Finish_Array_Aggr (List : in out O_Array_Aggr_List; Res : out O_Cnode) is begin Res := List.Res; end Finish_Array_Aggr; function Get_Const_Aggr_Length (Cst : O_Cnode) return Int32 is begin return To_Cnode_Aggr (Cnodes.Table (Cst + 1)).Nbr; end Get_Const_Aggr_Length; function Get_Const_Aggr_Element (Cst : O_Cnode; N : Int32) return O_Cnode is El : Int32; begin El := To_Cnode_Aggr (Cnodes.Table (Cst + 1)).Els; return Els.Table (El + N); end Get_Const_Aggr_Element; function New_Union_Aggr (Atype : O_Tnode; Field : O_Fnode; Value : O_Cnode) return O_Cnode is function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Union, Target => Cnode_Common); Res : O_Cnode; begin if Debug.Flag_Debug_Hli then Cnodes.Append (Cnode_Common'(Kind => OC_Union, Lit_Type => Atype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Union'(El => Value, Field => Field))); return Res; else return Value; end if; end New_Union_Aggr; function To_Cnode_Union is new Ada.Unchecked_Conversion (Source => Cnode_Common, Target => Cnode_Union); function Get_Const_Union_Field (Cst : O_Cnode) return O_Fnode is begin return To_Cnode_Union (Cnodes.Table (Cst + 1)).Field; end Get_Const_Union_Field; function Get_Const_Union_Value (Cst : O_Cnode) return O_Cnode is begin return To_Cnode_Union (Cnodes.Table (Cst + 1)).El; end Get_Const_Union_Value; function New_Sizeof (Atype : O_Tnode; Rtype : O_Tnode) return O_Cnode is function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Sizeof, Target => Cnode_Common); Res : O_Cnode; begin if Debug.Flag_Debug_Hli then Cnodes.Append (Cnode_Common'(Kind => OC_Sizeof, Lit_Type => Rtype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Sizeof'(Atype => Atype, Pad => 0))); return Res; else return New_Unsigned_Literal (Rtype, Unsigned_64 (Get_Type_Size (Atype))); end if; end New_Sizeof; function Get_Sizeof_Type (Cst : O_Cnode) return O_Tnode is function To_Cnode_Sizeof is new Ada.Unchecked_Conversion (Cnode_Common, Cnode_Sizeof); begin return To_Cnode_Sizeof (Cnodes.Table (Cst + 1)).Atype; end Get_Sizeof_Type; function New_Alignof (Atype : O_Tnode; Rtype : O_Tnode) return O_Cnode is function To_Cnode_Common is new Ada.Unchecked_Conversion (Source => Cnode_Sizeof, Target => Cnode_Common); Res : O_Cnode; begin if Debug.Flag_Debug_Hli then Cnodes.Append (Cnode_Common'(Kind => OC_Alignof, Lit_Type => Rtype)); Res := Cnodes.Last; Cnodes.Append (To_Cnode_Common (Cnode_Sizeof'(Atype => Atype, Pad => 0))); return Res; else return New_Unsigned_Literal (Rtype, Unsigned_64 (Get_Type_Align_Bytes (Atype))); end if; end New_Alignof; function Get_Alignof_Type (Cst : O_Cnode) return O_Tnode is function To_Cnode_Sizeof is new Ada.Unchecked_Conversion (Cnode_Common, Cnode_Sizeof); begin return To_Cnode_Sizeof (Cnodes.Table (Cst + 1)).Atype; end Get_Alignof_Type; function New_Offsetof (Rec_Type : O_Tnode; Field : O_Fnode; Rtype : O_Tnode) return O_Cnode is begin if Get_Field_Parent (Field) /= Rec_Type then raise Syntax_Error; end if; return New_Unsigned_Literal (Rtype, Unsigned_64 (Get_Field_Offset (Field))); end New_Offsetof; procedure Get_Const_Bytes (Cst : O_Cnode; H, L : out Uns32) is begin case Get_Const_Kind (Cst) is when OC_Signed | OC_Unsigned | OC_Float => H := Get_Const_High (Cst); L := Get_Const_Low (Cst); when OC_Null => H := 0; L := 0; when OC_Lit => H := 0; L := To_Cnode_Enum (Cnodes.Table (Cst + 1)).Val; when OC_Array | OC_Record | OC_Union | OC_Sizeof | OC_Alignof | OC_Address | OC_Subprg_Address => raise Syntax_Error; end case; end Get_Const_Bytes; procedure Mark (M : out Mark_Type) is begin M.Cnode := Cnodes.Last; M.Els := Els.Last; end Mark; procedure Release (M : Mark_Type) is begin Cnodes.Set_Last (M.Cnode); Els.Set_Last (M.Els); end Release; procedure Disp_Stats is use Ada.Text_IO; begin Put_Line ("Number of Cnodes: " & O_Cnode'Image (Cnodes.Last)); Put_Line ("Number of Cnodes-Els: " & Int32'Image (Els.Last)); end Disp_Stats; procedure Finish is begin Cnodes.Free; Els.Free; end Finish; end Ortho_Code.Consts;