Skip to content

Commit e8e6bbf

Browse files
committed
pbio/drv/block_device: Restructure shared SPI code.
Also separates the ramdisk logic from the actual SPI read and write operations. This helps to understand the shared SPI code better. It also becomes simpler to split them properly if we choose to do that later.
1 parent de6f11c commit e8e6bbf

File tree

4 files changed

+357
-407
lines changed

4 files changed

+357
-407
lines changed

lib/pbio/drv/adc/adc_ev3.c

Lines changed: 3 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2024 The Pybricks Authors
2+
// Copyright (c) 2024-2025 The Pybricks Authors
33

44
#include <pbdrv/config.h>
55

@@ -9,185 +9,10 @@
99
#error "EV3 block device driver must be enabled"
1010
#endif
1111

12-
#include <stdbool.h>
13-
#include <stdint.h>
14-
#include <string.h>
12+
// NB: This driver is implemented by pbdrv/block_device because it is on the
13+
// same SPI bus.
1514

16-
#include <pbdrv/adc.h>
17-
#include <pbdrv/clock.h>
18-
#include <pbdrv/gpio.h>
19-
20-
#include <pbio/error.h>
21-
#include <pbio/os.h>
22-
#include <pbio/util.h>
23-
24-
#include <tiam1808/spi.h>
25-
#include <tiam1808/psc.h>
26-
#include <tiam1808/hw/soc_AM1808.h>
27-
#include <tiam1808/hw/hw_types.h>
28-
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
29-
#include <tiam1808/armv5/am1808/interrupt.h>
30-
31-
#include "adc_ev3.h"
32-
#include "../drv/block_device/block_device_ev3.h"
33-
#include "../drv/gpio/gpio_ev3.h"
34-
35-
#include "../sys/storage.h"
36-
37-
#define PBDRV_ADC_EV3_NUM_DELAY_SAMPLES (2)
38-
39-
/**
40-
* Constants.
41-
*/
42-
enum {
43-
// The maximum ADC clock speed according to the datasheet is 20 MHz.
44-
// However, because the SPI peripheral does not have a fractional clock generator,
45-
// the closest achievable in-spec speed is a division factor of 8.
46-
//
47-
// 150 MHz / 8 = 18.75 MHz actual
48-
SPI_CLK_SPEED_ADC = 20000000,
49-
50-
ADC_SAMPLE_PERIOD = 2,
51-
};
52-
53-
// Construct both SPI peripheral settings (data format, chip select)
54-
// and ADC chip settings (manual mode, 2xVref) in one go,
55-
// so that DMA can be used efficiently.
56-
//
57-
// NOTE: CSHOLD is *not* set here, so that CS is deasserted between each 16-bit unit
58-
#define MANUAL_ADC_CHANNEL(x) \
59-
(1 << 26) | \
60-
(SPI_SPIDAT1_DFSEL_FORMAT1 << SPI_SPIDAT1_DFSEL_SHIFT) | \
61-
(0 << (SPI_SPIDAT1_CSNR_SHIFT + PBDRV_EV3_SPI0_ADC_CS)) | \
62-
(1 << (SPI_SPIDAT1_CSNR_SHIFT + PBDRV_EV3_SPI0_FLASH_CS)) | \
63-
(1 << 12) | \
64-
(1 << 11) | \
65-
(((x) & 0xf) << 7) | \
66-
(1 << 6)
67-
68-
static const uint32_t channel_cmd[PBDRV_CONFIG_ADC_EV3_ADC_NUM_CHANNELS + PBDRV_ADC_EV3_NUM_DELAY_SAMPLES] = {
69-
MANUAL_ADC_CHANNEL(0),
70-
MANUAL_ADC_CHANNEL(1),
71-
MANUAL_ADC_CHANNEL(2),
72-
MANUAL_ADC_CHANNEL(3),
73-
MANUAL_ADC_CHANNEL(4),
74-
MANUAL_ADC_CHANNEL(5),
75-
MANUAL_ADC_CHANNEL(6),
76-
MANUAL_ADC_CHANNEL(7),
77-
MANUAL_ADC_CHANNEL(8),
78-
MANUAL_ADC_CHANNEL(9),
79-
MANUAL_ADC_CHANNEL(10),
80-
MANUAL_ADC_CHANNEL(11),
81-
MANUAL_ADC_CHANNEL(12),
82-
MANUAL_ADC_CHANNEL(13),
83-
MANUAL_ADC_CHANNEL(14),
84-
MANUAL_ADC_CHANNEL(15),
85-
// We need two additional commands here because of how the ADC works.
86-
// In every given command frame, a new analog channel is selected in the ADC frontend multiplexer.
87-
// In frame n+1, that value actually gets converted to a digital value.
88-
// In frame n+2, the converted digital value is finally output, and we are able to receive it.
89-
// These requests are _pipelined_, so there is a latency of 2 frames, but we get a new sample on each frame.
90-
//
91-
// For more information, see figures 1 and 51 in the ADS7957 datasheet.
92-
MANUAL_ADC_CHANNEL(15),
93-
MANUAL_ADC_CHANNEL(15),
94-
};
95-
static volatile uint16_t channel_data[PBDRV_CONFIG_ADC_EV3_ADC_NUM_CHANNELS + PBDRV_ADC_EV3_NUM_DELAY_SAMPLES];
96-
97-
pbio_error_t pbdrv_adc_get_ch(uint8_t ch, uint16_t *value) {
98-
if (ch >= PBDRV_CONFIG_ADC_EV3_ADC_NUM_CHANNELS) {
99-
return PBIO_ERROR_INVALID_ARG;
100-
}
101-
// XXX We probably need to figure out how atomicity works between the DMA and the CPU.
102-
// For now, read the value twice and assume it's good (not torn) if the values are the same.
103-
uint16_t a, b;
104-
do {
105-
// Values for the requested channel are received several samples later.
106-
a = channel_data[ch + PBDRV_ADC_EV3_NUM_DELAY_SAMPLES];
107-
b = channel_data[ch + PBDRV_ADC_EV3_NUM_DELAY_SAMPLES];
108-
} while (a != b);
109-
110-
// Mask the data to 10 bits
111-
*value = (a >> 2) & 0x3ff;
112-
return PBIO_SUCCESS;
113-
}
114-
115-
static pbio_os_process_t pbdrv_adc_ev3_process;
116-
117-
/**
118-
* Request ADC process to exit and await until it does.
119-
*/
120-
pbio_error_t pbdrv_adc_ev3_exit(pbio_os_state_t *state) {
121-
PBIO_OS_ASYNC_BEGIN(state);
122-
123-
pbio_os_process_make_request(&pbdrv_adc_ev3_process, PBIO_OS_PROCESS_REQUEST_TYPE_CANCEL);
124-
PBIO_OS_AWAIT_UNTIL(state, pbdrv_adc_ev3_process.err == PBIO_SUCCESS);
125-
126-
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
127-
}
128-
129-
static pbio_error_t pbdrv_adc_ev3_process_thread(pbio_os_state_t *state, void *context) {
130-
static pbio_os_timer_t timer;
131-
132-
PBIO_OS_ASYNC_BEGIN(state);
133-
134-
// Poll continuously until cancellation is requested.
135-
while (!(pbdrv_adc_ev3_process.request & PBIO_OS_PROCESS_REQUEST_TYPE_CANCEL)) {
136-
137-
// Start a sample of all channels
138-
pbdrv_block_device_ev3_spi_begin_for_adc(
139-
channel_cmd,
140-
channel_data,
141-
PBDRV_CONFIG_ADC_EV3_ADC_NUM_CHANNELS + PBDRV_ADC_EV3_NUM_DELAY_SAMPLES);
142-
143-
// Allow event loop to run once so that processes that await new
144-
// samples can begin awaiting completion of the transfer.
145-
PBIO_OS_AWAIT_ONCE_AND_POLL(state);
146-
147-
// Await for actual transfer to complete.
148-
PBIO_OS_AWAIT_WHILE(state, pbdrv_block_device_ev3_is_busy());
149-
150-
pbio_os_timer_set(&timer, ADC_SAMPLE_PERIOD);
151-
PBIO_OS_AWAIT_UNTIL(state, pbdrv_adc_ev3_process.request || pbio_os_timer_is_expired(&timer));
152-
}
153-
154-
// Processes may be waiting on us to complete, so kick when done.
155-
pbio_os_request_poll();
156-
157-
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
158-
}
159-
160-
pbio_error_t pbdrv_adc_await_new_samples(pbio_os_state_t *state) {
161-
PBIO_OS_ASYNC_BEGIN(state);
162-
PBIO_OS_AWAIT_UNTIL(state, pbdrv_block_device_ev3_is_busy());
163-
PBIO_OS_AWAIT_UNTIL(state, !pbdrv_block_device_ev3_is_busy());
164-
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
165-
}
166-
167-
// Public init is not used.
16815
void pbdrv_adc_init(void) {
16916
}
17017

171-
// Init starts here instead. Called by SPI flash driver when it is done initializing.
172-
void pbdrv_adc_ev3_init(void) {
173-
174-
// Most of the SPI initialization is already done by the SPI flash driver.
175-
176-
SPIClkConfigure(SOC_SPI_0_REGS, SOC_SYSCLK_2_FREQ, SPI_CLK_SPEED_ADC, SPI_DATA_FORMAT1);
177-
// NOTE: Cannot be CPOL=1 CPHA=1 like SPI flash
178-
// The ADC seems to use the last falling edge to trigger conversions (see Figure 1 in the datasheet).
179-
SPIConfigClkFormat(SOC_SPI_0_REGS, SPI_CLK_POL_LOW | SPI_CLK_OUTOFPHASE, SPI_DATA_FORMAT1);
180-
SPIShiftMsbFirst(SOC_SPI_0_REGS, SPI_DATA_FORMAT1);
181-
SPICharLengthSet(SOC_SPI_0_REGS, 16, SPI_DATA_FORMAT1);
182-
// In order to compensate for analog impedance issues and capacitor charging time,
183-
// we set all SPI delays to the maximum for the ADC. This helps get more accurate readings.
184-
// This includes both this delay (the delay where CS is held inactive),
185-
// as well as the CS-assert-to-clock-start and clock-end-to-CS-deassert delays
186-
// (which are global and set in block_device_ev3.c).
187-
SPIWdelaySet(SOC_SPI_0_REGS, 0x3f << SPI_SPIFMT_WDELAY_SHIFT, SPI_DATA_FORMAT1);
188-
189-
// Begin polling.
190-
pbio_os_process_start(&pbdrv_adc_ev3_process, pbdrv_adc_ev3_process_thread, NULL);
191-
}
192-
19318
#endif // PBDRV_CONFIG_ADC_EV3

lib/pbio/drv/adc/adc_ev3.h

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)