Skip to content

Commit fbc4dec

Browse files
zelenskiCS107E BOT
authored andcommitted
Code/slides from output lecture (workflow stalled)
commit 59ba5c7055d0f6b5db2c69e2a763273bb5097110 Author: Julie Zelenski <[email protected]> Date: Mon Nov 25 23:50:12 2024 -0800 Code/slides from output lecture (workflow stalled)
1 parent 67faaa5 commit fbc4dec

File tree

26 files changed

+1137
-59
lines changed

26 files changed

+1137
-59
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Modules in this directory demonstrate how to interface with some of the hardware peripherals available on D1.
2+
3+
This code was last updated for demos in Output lecture (Friday Nov 22, 2024)
4+
You are welcome to incoporate this code into your projects, but keep in mind
5+
further testing/development of your own may be needed.
6+
7+
TWI (Two-Wire,i.e. i2c)
8+
- driver successfully used in past, minor recent updates to code
9+
SPI (serial peripheral)
10+
- driver successfully used in past, minor recent updates to code
11+
PWM (pulse width modulation)
12+
- code is brand-new, needs further testing
13+
Audio (I2S/PCM, DMIC, OWA)
14+
- draft version, newish code, needs further testing

lectures/Output/code/extras/i2c.c

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
Module to support i2c communication via TWI hardware peripheral
3+
4+
Author: Julie Zelenski
5+
Thu May 16 17:04:14 PDT 2024
6+
*/
7+
#include "assert.h"
8+
#include "ccu.h"
9+
#include "gpio.h"
10+
#include "i2c.h"
11+
#include "malloc.h"
12+
#include <stddef.h>
13+
#include "strings.h"
14+
#include "timer.h"
15+
16+
/*
17+
* Use D1-H TWI engine peripheral as hardware controller for I2C communication
18+
*/
19+
20+
// p. 859 D1 user manual
21+
// structs defined to match layout of hardware registers
22+
typedef union {
23+
struct {
24+
uint32_t addr;
25+
uint32_t xaddr;
26+
uint32_t data;
27+
struct {
28+
uint32_t clk_mode : 1;
29+
uint32_t : 1;
30+
uint32_t ack : 1;
31+
uint32_t int_flag : 1;
32+
uint32_t m_stp : 1;
33+
uint32_t m_sta : 1;
34+
uint32_t bus_en : 1;
35+
uint32_t int_en : 1;
36+
uint32_t : 24;
37+
} cntr;
38+
uint32_t stat;
39+
struct {
40+
uint32_t clk_N : 3;
41+
uint32_t clk_M : 4;
42+
uint32_t clk_duty : 1;
43+
uint32_t : 24;
44+
} ccr;
45+
uint32_t srst;
46+
uint32_t efr;
47+
uint32_t lcr;
48+
} regs;
49+
uint32_t padding[0x100];
50+
} twi_t;
51+
52+
#define TWI_BASE ((twi_t *)0x02502000)
53+
_Static_assert(&(TWI_BASE[0].regs.lcr) == (uint32_t *)0x02502020, "TWI0 lcr reg must be at address 0x02502020");
54+
_Static_assert(&(TWI_BASE[1].regs.efr) == (uint32_t *)0x0250241c, "TWI1 efr reg must be at address 0x0250241c");
55+
56+
static struct {
57+
volatile twi_t * const twi_base, *twi;
58+
const gpio_id_t sda, scl;
59+
} module = {
60+
.twi_base = &TWI_BASE[0], // twi0
61+
.sda = GPIO_PG13,
62+
.scl = GPIO_PG12,
63+
.twi = NULL,
64+
};
65+
66+
#define SENTINEL 0x7e
67+
68+
struct i2c_device {
69+
uint8_t addr;
70+
};
71+
72+
i2c_device_t * i2c_new(uint8_t addr) {
73+
i2c_device_t *dev = malloc(sizeof(*dev));
74+
dev->addr = addr;
75+
if (!i2c_block_write(dev, 0, 0)) {
76+
free(dev);
77+
return NULL;
78+
}
79+
return dev;
80+
}
81+
82+
bool i2c_write_reg(i2c_device_t *dev, uint8_t reg, uint8_t val) {
83+
assert(dev);
84+
uint8_t buf[2] = {reg, val};
85+
return i2c_block_write(dev, buf, sizeof(buf));
86+
}
87+
88+
bool i2c_write_reg_n(i2c_device_t *dev, uint8_t reg, uint8_t *bytes, int n) {
89+
assert(dev);
90+
uint8_t buf[n+1];
91+
buf[0] = reg;
92+
memcpy(buf+1, bytes, n);
93+
return i2c_block_write(dev, buf, sizeof(buf));
94+
}
95+
96+
uint8_t i2c_read_reg(i2c_device_t *dev, uint8_t reg) {
97+
assert(dev);
98+
uint8_t buf[1];
99+
if (!i2c_block_write(dev, &reg, 1) || !i2c_block_read(dev, buf, 1)) {
100+
return SENTINEL; // return distinctive pattern to help debug read failure
101+
}
102+
return buf[0];
103+
}
104+
105+
bool i2c_read_reg_n(i2c_device_t *dev, uint8_t reg, uint8_t *bytes, int n) {
106+
assert(dev);
107+
memset(bytes, SENTINEL, n); // init distinctive pattern to help debug read failure
108+
if (!i2c_block_write(dev, &reg, 1)) return false;
109+
return i2c_block_read(dev, bytes, n);
110+
}
111+
112+
enum { WRITE_BIT = 0, READ_BIT = 1};
113+
114+
typedef enum {
115+
BUS_ERROR = 0,
116+
START_TRANSMIT = 0x08,
117+
REPEATED_START_TRANSMIT= 0x10,
118+
ADDR_W_ACK = 0x18,
119+
ADDR_W_NAK = 0x20,
120+
DATA_TRANSMIT_ACK = 0x28,
121+
DATA_TRANSMIT_NAK = 0x30,
122+
LOST_ARBITRATION = 0x38,
123+
ADDR_R_ACK = 0x40,
124+
ADDR_R_NAK = 0x48,
125+
DATA_RECEIVE_ACK = 0x50,
126+
DATA_RECEIVE_NAK = 0x58,
127+
IDLE = 0xf8,
128+
} i2c_stat_t;
129+
130+
void i2c_init(void) {
131+
// this driver supports only TWI0
132+
module.twi = &module.twi_base[0];
133+
// (gating bit 16)
134+
ccu_ungate_bus_clock(CCU_TWI_BGR_REG);
135+
gpio_set_function(module.sda, GPIO_FN_ALT3); // TWI0
136+
gpio_set_function(module.scl, GPIO_FN_ALT3);
137+
// clock divisor values from p.876 of user manual
138+
// set for 100Khz
139+
module.twi->regs.ccr.clk_duty = 1;
140+
module.twi->regs.ccr.clk_M = 11;
141+
module.twi->regs.ccr.clk_N = 1;
142+
module.twi->regs.cntr.bus_en = 1;
143+
module.twi->regs.efr = 0;
144+
// efr disables special-case handling for unusual devices
145+
// see https://lore.kernel.org/linux-kernel/CAF8uH3u9L1cVyAZiY=981bDewYgVYM=27kcV0GwqHFURg21FgA@mail.gmail.com/T/
146+
}
147+
148+
static i2c_stat_t wait_completion(void) {
149+
// Note: int_flag is R/W1C Read/Write 1 to Clear. Write 0 has no effect!
150+
module.twi->regs.cntr.int_flag = 1;
151+
unsigned int wait_count = 1000*1000;
152+
while (module.twi->regs.cntr.int_flag == 0 && --wait_count);
153+
if (wait_count == 0) error("TIMEOUT wait_completion in i2c driver\n");
154+
return module.twi->regs.stat;
155+
}
156+
157+
static bool do_start(i2c_stat_t expected_status) {
158+
if (module.twi == NULL) error("i2c_init() has not been called!\n");
159+
module.twi->regs.cntr.m_sta = 1;
160+
i2c_stat_t status = wait_completion();
161+
return status == expected_status;
162+
}
163+
164+
static void do_stop(void) {
165+
module.twi->regs.cntr.m_stp = 1;
166+
while (module.twi->regs.cntr.m_stp == 1) ; // no interrupt after stop, wait for stop bit to reset
167+
timer_delay_us(30); // add delay enough for min bus free time (required by adafruit seesaw for one)
168+
}
169+
170+
static bool do_transmit(uint8_t byte, i2c_stat_t expected_status) {
171+
module.twi->regs.data = byte;
172+
i2c_stat_t status = wait_completion();
173+
return status == expected_status;
174+
}
175+
176+
static bool do_receive(uint8_t *p, bool is_last) {
177+
i2c_stat_t expected_status = is_last? DATA_RECEIVE_NAK : DATA_RECEIVE_ACK;
178+
int response = is_last? 0: 1; // respond NAK for last, ACK otherwise
179+
module.twi->regs.cntr.ack = response;
180+
i2c_stat_t status = wait_completion();
181+
*p = module.twi->regs.data;
182+
return status == expected_status;
183+
}
184+
185+
bool i2c_block_read(i2c_device_t *dev, uint8_t *bytes, int n) {
186+
assert(dev);
187+
memset(bytes, SENTINEL, n); // init distinctive pattern to help debug read failure
188+
bool success = do_start(START_TRANSMIT);
189+
success = success && do_transmit((dev->addr << 1) | READ_BIT, ADDR_R_ACK);
190+
for (int i = 0; success && i < n; i++) {
191+
bool is_last = (i == n - 1);
192+
success = do_receive(&bytes[i], is_last);
193+
}
194+
do_stop();
195+
return success;
196+
}
197+
198+
bool i2c_block_write(i2c_device_t *dev, uint8_t *bytes, int n) {
199+
assert(dev);
200+
bool success = do_start(START_TRANSMIT);
201+
success = success && do_transmit((dev->addr << 1) | WRITE_BIT, ADDR_W_ACK);
202+
for (int i = 0; success && i < n; i++) {
203+
success = do_transmit(bytes[i], DATA_TRANSMIT_ACK);
204+
}
205+
do_stop();
206+
return success;
207+
}

lectures/Output/code/extras/i2c.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef I2C_H__
2+
#define I2C_H__
3+
4+
/*
5+
Module to support i2c communication via TWI hardware peripheral
6+
7+
Author: Julie Zelenski
8+
*/
9+
10+
#include <stdbool.h>
11+
#include <stdint.h>
12+
13+
typedef struct i2c_device i2c_device_t;
14+
15+
void i2c_init(void); // call once to init i2c module
16+
i2c_device_t * i2c_new(uint8_t addr); // per each i2c device
17+
18+
bool i2c_write_reg(i2c_device_t *dev, uint8_t reg, uint8_t val);
19+
bool i2c_write_reg_n(i2c_device_t *dev, uint8_t reg, uint8_t *bytes, int n);
20+
21+
uint8_t i2c_read_reg(i2c_device_t *dev, uint8_t reg);
22+
bool i2c_read_reg_n(i2c_device_t *dev, uint8_t reg, uint8_t *bytes, int n);
23+
24+
// if register specifier not single byte, block read/write to manually construct packet
25+
bool i2c_block_read(i2c_device_t *dev, uint8_t *bytes, int n);
26+
bool i2c_block_write(i2c_device_t *dev, uint8_t *bytes, int n);
27+
28+
#endif

0 commit comments

Comments
 (0)