|
42 | 42 | #include "py/objproperty.h"
|
43 | 43 | #include "py/runtime.h"
|
44 | 44 |
|
| 45 | +#define NO_DMA_CHANNEL (-1) |
| 46 | + |
45 | 47 | // Count how many state machines are using each pin.
|
46 | 48 | STATIC uint8_t _pin_reference_count[TOTAL_GPIO_COUNT];
|
47 | 49 | STATIC uint32_t _current_program_id[NUM_PIOS][NUM_PIO_STATE_MACHINES];
|
@@ -330,6 +332,9 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
330 | 332 | sm_config_set_fifo_join(&c, join);
|
331 | 333 | self->sm_config = c;
|
332 | 334 |
|
| 335 | + // no DMA allocated |
| 336 | + self->dma_channel[0] = self->dma_channel[1] = NO_DMA_CHANNEL; |
| 337 | + |
333 | 338 | pio_sm_init(self->pio, self->state_machine, program_offset, &c);
|
334 | 339 | common_hal_rp2pio_statemachine_run(self, init, init_len);
|
335 | 340 |
|
@@ -581,6 +586,7 @@ void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t *sel
|
581 | 586 |
|
582 | 587 | void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) {
|
583 | 588 | common_hal_rp2pio_statemachine_stop(self);
|
| 589 | + (void)common_hal_rp2pio_statemachine_end_continuous_write(self); |
584 | 590 |
|
585 | 591 | uint8_t sm = self->state_machine;
|
586 | 592 | uint8_t pio_index = pio_get_index(self->pio);
|
@@ -849,10 +855,114 @@ uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self) {
|
849 | 855 | return _current_program_offset[pio_index][sm];
|
850 | 856 | }
|
851 | 857 |
|
852 |
| -bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len, uint8_t stride_in_bytes) { |
853 |
| - return false; |
| 858 | +#define HERE(fmt, ...) (mp_printf(&mp_plat_print, "%s: %d: " fmt "\n", __FILE__, __LINE__,##__VA_ARGS__)) |
| 859 | + |
| 860 | +bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_obj_t *self, mp_obj_t buf_obj, const uint8_t *data, size_t len, uint8_t stride_in_bytes) { |
| 861 | + if (self->dma_channel[0] != NO_DMA_CHANNEL && stride_in_bytes == self->continuous_stride_in_bytes) { |
| 862 | + HERE("updating channel pending=%d\n", self->pending_set_data); |
| 863 | + while (self->pending_set_data) { |
| 864 | + RUN_BACKGROUND_TASKS; |
| 865 | + if (self->user_interruptible && mp_hal_is_interrupted()) { |
| 866 | + (void)common_hal_rp2pio_statemachine_end_continuous_write(self); |
| 867 | + return false; |
| 868 | + } |
| 869 | + } |
| 870 | + |
| 871 | + common_hal_mcu_disable_interrupts(); |
| 872 | + self->next_buffer = data; |
| 873 | + self->next_size = len / stride_in_bytes; |
| 874 | + self->pending_set_data = true; |
| 875 | + common_hal_mcu_enable_interrupts(); |
| 876 | + |
| 877 | + // need to keep a reference alive to the buffer, lest the GC collect it while its lone remaining pointer is in the DMA peripheral register |
| 878 | + self->buf_objs[++self->buf_obj_idx % 2] = buf_obj; |
| 879 | + return true; |
| 880 | + } |
| 881 | + |
| 882 | + common_hal_rp2pio_statemachine_end_continuous_write(self); |
| 883 | + |
| 884 | + for (int i = 0; i < 2; i++) { |
| 885 | + HERE("allocating channel %d", i); |
| 886 | + if (self->dma_channel[i] == -1) { |
| 887 | + self->dma_channel[i] = dma_claim_unused_channel(false); |
| 888 | + HERE("got channel %d", self->dma_channel[i]); |
| 889 | + MP_STATE_PORT(continuous_pio)[self->dma_channel[i]] = self; |
| 890 | + } |
| 891 | + if (self->dma_channel[i] == -1) { |
| 892 | + HERE("allocating channel %d failed", i); |
| 893 | + (void)common_hal_rp2pio_statemachine_end_continuous_write(self); |
| 894 | + return false; |
| 895 | + } |
| 896 | + } |
| 897 | + |
| 898 | + volatile uint8_t *tx_destination = (volatile uint8_t *)&self->pio->txf[self->state_machine]; |
| 899 | + |
| 900 | + self->tx_dreq = pio_get_dreq(self->pio, self->state_machine, true); |
| 901 | + |
| 902 | + dma_channel_config c; |
| 903 | + |
| 904 | + self->pending_set_data = false; |
| 905 | + self->continuous_stride_in_bytes = stride_in_bytes; |
| 906 | + self->buf_objs[0] = buf_obj; |
| 907 | + self->buf_objs[1] = NULL; |
| 908 | + |
| 909 | + self->next_buffer = data; |
| 910 | + self->next_size = len / stride_in_bytes; |
| 911 | + |
| 912 | + c = dma_channel_get_default_config(self->dma_channel[0]); |
| 913 | + channel_config_set_transfer_data_size(&c, _stride_to_dma_size(stride_in_bytes)); |
| 914 | + channel_config_set_dreq(&c, self->tx_dreq); |
| 915 | + channel_config_set_read_increment(&c, true); |
| 916 | + channel_config_set_write_increment(&c, false); |
| 917 | + // channel_config_set_chain_to(&c, self->dma_channel[1]); |
| 918 | + dma_channel_configure(self->dma_channel[0], &c, |
| 919 | + tx_destination, |
| 920 | + data, |
| 921 | + len / stride_in_bytes, |
| 922 | + false); |
| 923 | + |
| 924 | + #if 0 |
| 925 | + channel_config_set_chain_to(&c, self->dma_channel[0]); |
| 926 | + dma_channel_configure(self->dma_channel[1], &c, |
| 927 | + tx_destination, |
| 928 | + data, |
| 929 | + len / stride_in_bytes, |
| 930 | + false); |
| 931 | + #endif |
| 932 | + |
| 933 | + dma_hw->inte0 |= (1 << self->dma_channel[0]) | (1 << self->dma_channel[1]); |
| 934 | + irq_set_mask_enabled(1 << DMA_IRQ_0, true); |
| 935 | + |
| 936 | + HERE("OK let's go"); |
| 937 | + dma_start_channel_mask(1u << self->dma_channel[0]); |
| 938 | + return true; |
| 939 | +} |
| 940 | + |
| 941 | +void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel) { |
| 942 | + HERE("dma complete[%d] pending set data=%d busy=%d,%d %d@%p", channel, self->pending_set_data, dma_channel_is_busy(self->dma_channel[0]), dma_channel_is_busy(self->dma_channel[1]), |
| 943 | + self->next_size, self->next_buffer); |
| 944 | + |
| 945 | + dma_channel_set_read_addr(channel, self->next_buffer, false); |
| 946 | + dma_channel_set_trans_count(channel, self->next_size, true); |
| 947 | + |
| 948 | + self->pending_set_data = false; |
854 | 949 | }
|
855 | 950 |
|
856 | 951 | bool common_hal_rp2pio_statemachine_end_continuous_write(rp2pio_statemachine_obj_t *self) {
|
857 |
| - return false; |
| 952 | + for (int i = 0; i < 2; i++) { |
| 953 | + if (self->dma_channel[i] == NO_DMA_CHANNEL) { |
| 954 | + continue; |
| 955 | + } |
| 956 | + int channel = self->dma_channel[i]; |
| 957 | + uint32_t channel_mask = 1u << channel; |
| 958 | + dma_hw->inte0 &= ~channel_mask; |
| 959 | + if (!dma_hw->inte0) { |
| 960 | + irq_set_mask_enabled(1 << DMA_IRQ_0, false); |
| 961 | + } |
| 962 | + MP_STATE_PORT(continuous_pio)[channel] = NULL; |
| 963 | + dma_channel_abort(channel); |
| 964 | + dma_channel_unclaim(channel); |
| 965 | + self->dma_channel[i] = NO_DMA_CHANNEL; |
| 966 | + } |
| 967 | + return true; |
858 | 968 | }
|
0 commit comments