@@ -481,6 +481,146 @@ MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_get_frequency_obj, busio_spi_obj_get_frequen
481481
482482MP_PROPERTY_GETTER (busio_spi_frequency_obj ,
483483 (mp_obj_t )& busio_spi_get_frequency_obj );
484+
485+ #if CIRCUITPY_SAMD
486+
487+ //| import sys
488+ //| def async_transfer_start(
489+ //| self,
490+ //| out_buffer: ReadableBuffer,
491+ //| in_buffer: WriteableBuffer,
492+ //| *,
493+ //| out_start: int = 0,
494+ //| out_end: int = sys.maxsize,
495+ //| in_start: int = 0,
496+ //| in_end: int = sys.maxsize
497+ //| ) -> None:
498+ //| """Write out the data in ``out_buffer`` while simultaneously reading data into ``in_buffer``.
499+ //| The SPI object must be locked. Note: this method returns immediately, and the data will not
500+ //| actually be transferred until some time has passed. Use `async_transfer_finished` and
501+ //| `async_transfer_end` to check on the status of the transfer and close out its resources.
502+ //|
503+ //| If ``out_start`` or ``out_end`` is provided, then the buffer will be sliced
504+ //| as if ``out_buffer[out_start:out_end]`` were passed, but without copying the data.
505+ //| The number of bytes written will be the length of ``out_buffer[out_start:out_end]``.
506+ //|
507+ //| If ``in_start`` or ``in_end`` is provided, then the input buffer will be sliced
508+ //| as if ``in_buffer[in_start:in_end]`` were passed,
509+ //| The number of bytes read will be the length of ``out_buffer[in_start:in_end]``.
510+ //|
511+ //| The lengths of the slices defined by ``out_buffer[out_start:out_end]``
512+ //| and ``in_buffer[in_start:in_end]`` must be equal.
513+ //| If buffer slice lengths are both 0, nothing happens.
514+ //|
515+ //| Note: This method is currently only available on atmel-samd` ports of CircuitPython.
516+ //|
517+ //| :param ReadableBuffer out_buffer: write out bytes from this buffer
518+ //| :param WriteableBuffer in_buffer: read bytes into this buffer
519+ //| :param int out_start: beginning of ``out_buffer`` slice
520+ //| :param int out_end: end of ``out_buffer`` slice; if not specified, use ``len(out_buffer)``
521+ //| :param int in_start: beginning of ``in_buffer`` slice
522+ //| :param int in_end: end of ``in_buffer slice``; if not specified, use ``len(in_buffer)``
523+ //| """
524+ //| ...
525+
526+ STATIC mp_obj_t busio_spi_start_async_transfer (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
527+ enum { ARG_out_buffer , ARG_in_buffer , ARG_out_start , ARG_out_end , ARG_in_start , ARG_in_end };
528+ static const mp_arg_t allowed_args [] = {
529+ { MP_QSTR_out_buffer , MP_ARG_REQUIRED | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
530+ { MP_QSTR_in_buffer , MP_ARG_REQUIRED | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
531+ { MP_QSTR_out_start , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
532+ { MP_QSTR_out_end , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = INT_MAX } },
533+ { MP_QSTR_in_start , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
534+ { MP_QSTR_in_end , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = INT_MAX } },
535+ };
536+ busio_spi_obj_t * self = MP_OBJ_TO_PTR (pos_args [0 ]);
537+ check_for_deinit (self );
538+ check_lock (self );
539+ mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
540+ mp_arg_parse_all (n_args - 1 , pos_args + 1 , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
541+
542+ mp_buffer_info_t buf_out_info ;
543+ mp_get_buffer_raise (args [ARG_out_buffer ].u_obj , & buf_out_info , MP_BUFFER_READ );
544+ int out_stride_in_bytes = mp_binary_get_size ('@' , buf_out_info .typecode , NULL );
545+ int32_t out_start = args [ARG_out_start ].u_int ;
546+ size_t out_length = buf_out_info .len / out_stride_in_bytes ;
547+ normalize_buffer_bounds (& out_start , args [ARG_out_end ].u_int , & out_length );
548+
549+ mp_buffer_info_t buf_in_info ;
550+ mp_get_buffer_raise (args [ARG_in_buffer ].u_obj , & buf_in_info , MP_BUFFER_WRITE );
551+ int in_stride_in_bytes = mp_binary_get_size ('@' , buf_in_info .typecode , NULL );
552+ int32_t in_start = args [ARG_in_start ].u_int ;
553+ size_t in_length = buf_in_info .len / in_stride_in_bytes ;
554+ normalize_buffer_bounds (& in_start , args [ARG_in_end ].u_int , & in_length );
555+
556+ // Treat start and length in terms of bytes from now on.
557+ out_start *= out_stride_in_bytes ;
558+ out_length *= out_stride_in_bytes ;
559+ in_start *= in_stride_in_bytes ;
560+ in_length *= in_stride_in_bytes ;
561+
562+ if (out_length != in_length ) {
563+ mp_raise_ValueError (MP_ERROR_TEXT ("buffer slices must be of equal length" ));
564+ }
565+
566+ common_hal_busio_spi_transfer_async_start (self ,
567+ ((uint8_t * )buf_out_info .buf ) + out_start ,
568+ ((uint8_t * )buf_in_info .buf ) + in_start ,
569+ out_length );
570+ return mp_const_none ;
571+ }
572+ MP_DEFINE_CONST_FUN_OBJ_KW (busio_spi_start_transfer_obj , 1 , busio_spi_start_async_transfer );
573+
574+ //| import sys
575+ //| def async_transfer_finished(
576+ //| self
577+ //| ) -> None:
578+ //| """Check whether or not the last async transfer started on this SPI object has finished. If
579+ //| no transfer was started, this method behaves as though the most recent transfer has finished
580+ //| and returns `True`. Otherwise, it returns `False`.
581+ //|
582+ //| Note: This method is currently only available on atmel-samd` ports of CircuitPython.
583+ //| """
584+ //| ...
585+ STATIC mp_obj_t busio_spi_obj_check_async_transfer (mp_obj_t self_in ) {
586+ busio_spi_obj_t * self = MP_OBJ_TO_PTR (self_in );
587+ check_for_deinit (self );
588+ return common_hal_busio_spi_transfer_async_check (self ) ? mp_const_true : mp_const_false ;
589+ }
590+ MP_DEFINE_CONST_FUN_OBJ_1 (busio_spi_check_transfer_obj , busio_spi_obj_check_async_transfer );
591+
592+ //| import sys
593+ //| def async_transfer_end(
594+ //| self
595+ //| ) -> None:
596+ //| """Return the status code with which the last async transfer on this SPI object completed. This
597+ //| method MUST be called for all transfers, regardless of user interest in status code. The resources
598+ //| for the transfer will be left open until this method is called. Once this method is called, the
599+ //| peripheral resets and is ready for another transfer. The return code of this method also resets to
600+ //| its pre-transfer state: repeated calls to this method may produce different codes.
601+ //|
602+ //| Return code 0: No transfer has occured, either because `start_async_transfer` was never called, or because
603+ //| it was called with zero-length buffers.
604+ //| Return code -1: The transfer failed because no DMA channels are available.
605+ //| Return code -2: The transfer executed, but the DMA controller indicates that either some data is
606+ //| untransferred, that a software issue prevented the data transfer from completing, or that some other error
607+ //| has occured within the DMA controller.
608+ //| Return code -3: An unaligned buffer was passed to the QSPI peripheral, which prevents the DMA controller from
609+ //| appropriately chunking the transfer.
610+ //| Return code n>0: A transfer of `n` bytes in each direction has succeeded.
611+ //|
612+ //| Note: This method is currently only available on atmel-samd` ports of CircuitPython.
613+ //| """
614+ //| ...
615+ STATIC mp_obj_t busio_spi_obj_end_async_transfer (mp_obj_t self_in ) {
616+ busio_spi_obj_t * self = MP_OBJ_TO_PTR (self_in );
617+ check_for_deinit (self );
618+ return MP_OBJ_NEW_SMALL_INT (common_hal_busio_spi_transfer_async_end (self ));
619+ }
620+ MP_DEFINE_CONST_FUN_OBJ_1 (busio_spi_end_transfer_obj , busio_spi_obj_end_async_transfer );
621+
622+ #endif // CIRCUITPY_SAMD
623+
484624#endif // CIRCUITPY_BUSIO_SPI
485625
486626
@@ -498,6 +638,14 @@ STATIC const mp_rom_map_elem_t busio_spi_locals_dict_table[] = {
498638 { MP_ROM_QSTR (MP_QSTR_write ), MP_ROM_PTR (& busio_spi_write_obj ) },
499639 { MP_ROM_QSTR (MP_QSTR_write_readinto ), MP_ROM_PTR (& busio_spi_write_readinto_obj ) },
500640 { MP_ROM_QSTR (MP_QSTR_frequency ), MP_ROM_PTR (& busio_spi_frequency_obj ) }
641+
642+ #if CIRCUITPY_SAMD
643+ ,
644+ { MP_ROM_QSTR (MP_QSTR_async_transfer_start ), MP_ROM_PTR (& busio_spi_start_transfer_obj ) },
645+ { MP_ROM_QSTR (MP_QSTR_async_transfer_finished ), MP_ROM_PTR (& busio_spi_check_transfer_obj ) },
646+ { MP_ROM_QSTR (MP_QSTR_async_transfer_end ), MP_ROM_PTR (& busio_spi_end_transfer_obj ) }
647+ #endif // CIRCUITPY_SAMD
648+
501649 #endif // CIRCUITPY_BUSIO_SPI
502650};
503651STATIC MP_DEFINE_CONST_DICT (busio_spi_locals_dict , busio_spi_locals_dict_table );
0 commit comments