Skip to content

Commit e75fd0e

Browse files
dhalberttannewt
authored andcommitted
add SPI.write_readinto() - bidirectional SPI
1 parent 9ac6890 commit e75fd0e

File tree

5 files changed

+103
-15
lines changed

5 files changed

+103
-15
lines changed

atmel-samd/common-hal/busio/SPI.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,16 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self,
257257
}
258258
return status == STATUS_OK;
259259
}
260+
261+
bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_out, uint8_t *data_in, size_t len) {
262+
if (len == 0) {
263+
return true;
264+
}
265+
enum status_code status;
266+
if (len >= 16) {
267+
status = shared_dma_transfer(self->spi_master_instance.hw, data_out, data_in, len, 0 /*ignored*/);
268+
} else {
269+
status = spi_transceive_buffer_wait(&self->spi_master_instance, data_out, data_in, len);
270+
}
271+
return status == STATUS_OK;
272+
}

atmel-samd/shared_dma.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ enum status_code shared_dma_write(Sercom* sercom, const uint8_t* buffer, uint32_
103103
}
104104
dma_configure(general_dma_tx.channel_id, sercom_index(sercom) * 2 + 2, false);
105105

106-
// Set up TX second.
106+
// Set up TX. There is no RX job.
107107
struct dma_descriptor_config descriptor_config;
108108
dma_descriptor_get_config_defaults(&descriptor_config);
109109
descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
@@ -137,6 +137,12 @@ enum status_code shared_dma_write(Sercom* sercom, const uint8_t* buffer, uint32_
137137
}
138138

139139
enum status_code shared_dma_read(Sercom* sercom, uint8_t* buffer, uint32_t length, uint8_t tx) {
140+
return shared_dma_transfer(sercom, NULL, buffer, length, tx);
141+
}
142+
143+
// Do write and read simultaneously. If buffer_out is NULL, write the tx byte over and over.
144+
// If buffer_out is a real buffer, ignore tx.
145+
enum status_code shared_dma_transfer(Sercom* sercom, uint8_t* buffer_out, uint8_t* buffer_in, uint32_t length, uint8_t tx) {
140146
if (general_dma_tx.job_status != STATUS_OK) {
141147
return general_dma_tx.job_status;
142148
}
@@ -153,17 +159,19 @@ enum status_code shared_dma_read(Sercom* sercom, uint8_t* buffer, uint32_t lengt
153159
descriptor_config.block_transfer_count = length;
154160
// DATA register is consistently addressed across all SERCOM modes.
155161
descriptor_config.source_address = ((uint32_t)&sercom->SPI.DATA.reg);
156-
descriptor_config.destination_address = ((uint32_t)buffer + length);
162+
descriptor_config.destination_address = ((uint32_t)buffer_in + length);
157163

158164
dma_descriptor_create(general_dma_rx.descriptor, &descriptor_config);
159165

160-
// Set up TX to retransmit the same byte over and over.
166+
// Set up TX second.
161167
dma_descriptor_get_config_defaults(&descriptor_config);
162168
descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
163-
descriptor_config.src_increment_enable = false;
169+
// Increment write address only if we have a real buffer.
170+
descriptor_config.src_increment_enable = buffer_out != NULL;
164171
descriptor_config.dst_increment_enable = false;
165172
descriptor_config.block_transfer_count = length;
166-
descriptor_config.source_address = ((uint32_t)&tx);
173+
//
174+
descriptor_config.source_address = ((uint32_t) (buffer_out != NULL ? buffer_out + length : &tx));
167175
// DATA register is consistently addressed across all SERCOM modes.
168176
descriptor_config.destination_address = ((uint32_t)&sercom->SPI.DATA.reg);
169177

atmel-samd/shared_dma.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ void init_shared_dma(void);
3939

4040
enum status_code shared_dma_write(Sercom* sercom, const uint8_t* buffer, uint32_t length);
4141
enum status_code shared_dma_read(Sercom* sercom, uint8_t* buffer, uint32_t length, uint8_t tx);
42+
enum status_code shared_dma_transfer(Sercom* sercom, uint8_t* buffer_out, uint8_t* buffer_in, uint32_t length, uint8_t tx);
4243

4344
// Allocate a counter to track how far along we are in a DMA double buffer.
4445
bool allocate_block_counter(void);

shared-bindings/busio/SPI.c

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_unlock_obj, busio_spi_obj_unlock);
206206

207207
//| .. method:: SPI.write(buffer, \*, start=0, end=len(buffer))
208208
//|
209-
//| Write the data contained in ``buf``. Requires the SPI being locked.
209+
//| Write the data contained in ``buffer``. The SPI object must be locked.
210210
//| If the buffer is empty, nothing happens.
211211
//|
212-
//| :param bytearray buffer: buffer containing the bytes to write
213-
//| :param int start: Index to start writing from
214-
//| :param int end: Index to read up to but not include
212+
//| :param bytearray buffer: Write out the data in this buffer
213+
//| :param int start: Start of the slice of ``buffer`` to write out: ``buffer[start:end]``
214+
//| :param int end: End of the slice; this index is not included
215215
//|
216216
STATIC mp_obj_t busio_spi_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
217217
enum { ARG_buffer, ARG_start, ARG_end };
@@ -247,14 +247,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_write_obj, 2, busio_spi_write);
247247

248248
//| .. method:: SPI.readinto(buffer, \*, start=0, end=len(buffer), write_value=0)
249249
//|
250-
//| Read into the buffer specified by ``buf`` while writing zeroes.
251-
//| Requires the SPI being locked.
250+
//| Read into ``buffer`` while writing ``write_value`` for each byte read.
251+
//| The SPI object must be locked.
252252
//| If the number of bytes to read is 0, nothing happens.
253253
//|
254-
//| :param bytearray buffer: buffer to write into
255-
//| :param int start: Index to start writing at
256-
//| :param int end: Index to write up to but not include
257-
//| :param int write_value: Value to write reading. (Usually ignored.)
254+
//| :param bytearray buffer: Read data into this buffer
255+
//| :param int start: Start of the slice of ``buffer`` to read into: ``buffer[start:end]``
256+
//| :param int end: End of the slice; this index is not included
257+
//| :param int write_value: Value to write while reading. (Usually ignored.)
258258
//|
259259
STATIC mp_obj_t busio_spi_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
260260
enum { ARG_buffer, ARG_start, ARG_end, ARG_write_value };
@@ -288,6 +288,68 @@ STATIC mp_obj_t busio_spi_readinto(size_t n_args, const mp_obj_t *pos_args, mp_m
288288
}
289289
MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_readinto_obj, 2, busio_spi_readinto);
290290

291+
//| .. method:: SPI.write_readinto(buffer_out, buffer_in, \*, out_start=0, out_end=len(buffer_out), in_start=0, in_end=len(buffer_in))
292+
//|
293+
//| Write out the data in ``buffer_out`` while simultaneously reading data into ``buffer_in``.
294+
//| The SPI object must be locked.
295+
//| The lengths of the slices defined by ``buffer_out[out_start:out_end]`` and ``buffer_in[in_start:in_end]``
296+
//| must be equal.
297+
//| If buffer slice lengths are both 0, nothing happens.
298+
//|
299+
//| :param bytearray buffer_out: Write out the data in this buffer
300+
//| :param bytearray buffer_in: Read data into this buffer
301+
//| :param int out_start: Start of the slice of buffer_out to write out: ``buffer_out[out_start:out_end]``
302+
//| :param int out_end: End of the slice; this index is not included
303+
//| :param int in_start: Start of the slice of ``buffer_in`` to read into: ``buffer_in[in_start:in_end]``
304+
//| :param int in_end: End of the slice; this index is not included
305+
//|
306+
STATIC mp_obj_t busio_spi_write_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
307+
enum { ARG_buffer_out, ARG_buffer_in, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end };
308+
static const mp_arg_t allowed_args[] = {
309+
{ MP_QSTR_buffer_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
310+
{ MP_QSTR_buffer_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
311+
{ MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
312+
{ MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
313+
{ MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
314+
{ MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
315+
};
316+
busio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
317+
raise_error_if_deinited(common_hal_busio_spi_deinited(self));
318+
check_lock(self);
319+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
320+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
321+
322+
mp_buffer_info_t buf_out_info;
323+
mp_get_buffer_raise(args[ARG_buffer_out].u_obj, &buf_out_info, MP_BUFFER_READ);
324+
int32_t out_start = args[ARG_out_start].u_int;
325+
uint32_t out_length = buf_out_info.len;
326+
normalize_buffer_bounds(&out_start, args[ARG_out_end].u_int, &out_length);
327+
328+
mp_buffer_info_t buf_in_info;
329+
mp_get_buffer_raise(args[ARG_buffer_in].u_obj, &buf_in_info, MP_BUFFER_WRITE);
330+
int32_t in_start = args[ARG_in_start].u_int;
331+
uint32_t in_length = buf_in_info.len;
332+
normalize_buffer_bounds(&in_start, args[ARG_in_end].u_int, &in_length);
333+
334+
if (out_length != in_length) {
335+
mp_raise_ValueError("buffer slices must be of equal length");
336+
}
337+
338+
if (out_length == 0) {
339+
return mp_const_none;
340+
}
341+
342+
bool ok = common_hal_busio_spi_transfer(self,
343+
((uint8_t*)buf_out_info.buf) + out_start,
344+
((uint8_t*)buf_in_info.buf) + in_start,
345+
out_length);
346+
if (!ok) {
347+
mp_raise_OSError(MP_EIO);
348+
}
349+
return mp_const_none;
350+
}
351+
MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_write_readinto_obj, 2, busio_spi_write_readinto);
352+
291353
STATIC const mp_rom_map_elem_t busio_spi_locals_dict_table[] = {
292354
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_spi_deinit_obj) },
293355
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
@@ -299,6 +361,7 @@ STATIC const mp_rom_map_elem_t busio_spi_locals_dict_table[] = {
299361

300362
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&busio_spi_readinto_obj) },
301363
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&busio_spi_write_obj) },
364+
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&busio_spi_write_readinto_obj) },
302365
};
303366
STATIC MP_DEFINE_CONST_DICT(busio_spi_locals_dict, busio_spi_locals_dict_table);
304367

shared-bindings/busio/SPI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,7 @@ extern bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *dat
5555
// Reads in len bytes while outputting zeroes.
5656
extern bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value);
5757

58+
// Reads and write len bytes simultaneously.
59+
extern bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_out, uint8_t *data_in, size_t len);
60+
5861
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy