1 /* SDIO example, slave (uses sdio slave driver)
2 
3    This example code is in the Public Domain (or CC0 licensed, at your option.)
4 
5    Unless required by applicable law or agreed to in writing, this
6    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7    CONDITIONS OF ANY KIND, either express or implied.
8    */
9 #include "driver/sdio_slave.h"
10 #include "esp_log.h"
11 #include "sys/queue.h"
12 #include "soc/soc.h"
13 #include "freertos/task.h"
14 #include "freertos/ringbuf.h"
15 #include "sdkconfig.h"
16 
17 /*
18    sdio slave example.
19 
20    This example is supposed to work together with the sdio host example. It uses the pins as follows:
21 
22      *   Host      Slave
23      *   IO14      CLK
24      *   IO15      CMD
25      *   IO2       D0
26      *   IO4       D1
27      *   IO12      D2
28      *   IO13      D3
29 
30     This is the only pins that can be used in standard ESP modules. The other set of pins (6, 11, 7, 8, 9, 10)
31     are occupied by the spi bus communicating with the flash.
32 
33     Protocol Above the ESP slave service:
34         - Interrupts:
35             0 is used to notify the slave to read the register 0.
36 
37         - Registers:
38             - 0 is the register to hold tasks. Bits:
39                 - 0: the slave should reset.
40                 - 1: the slave should send interrupts.
41                 - 2: the slave should write the shared registers acoording to the value in register 1.
42             - 1 is the register to hold test value.
43             - other registers will be written by the slave for testing.
44 
45         - FIFO:
46             The receving FIFO is size of 256 bytes.
47             When the host writes something to slave recv FIFO, the slave should return it as is to the sending FIFO.
48 
49     The host works as following process:
50 
51         1. reset the slave.
52         2. tell the slave to write registers and read them back.
53         3. tell the slave to send interrupts to the host.
54         4. send data to slave FIFO and read them back.
55         5. loop step 4.
56    */
57 
58 #define SDIO_SLAVE_QUEUE_SIZE 11
59 
60 #define BUFFER_SIZE     128
61 #define BUFFER_NUM      16
62 
63 #define EV_STR(s) "================ "s" ================"
64 
65 typedef enum {
66     JOB_IDLE = 0,
67     JOB_RESET = 1,
68     JOB_SEND_INT = 2,
69     JOB_WRITE_REG = 4,
70 } example_job_t;
71 
72 static const char TAG[] = "example_slave";
73 static int s_job = JOB_IDLE;
74 
75 DMA_ATTR uint8_t data_to_send[BUFFER_SIZE] = {0x97, 0x84, 0x43, 0x67, 0xc1, 0xdd, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x56, 0x55, 0x44, 0x33 ,0x22, 0x11, 0x00 };
76 DMA_ATTR uint8_t data_to_recv[BUFFER_SIZE] = {0x97, 0x84, 0x43, 0x67, 0xc1, 0xdd, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x56, 0x55, 0x44, 0x33 ,0x22, 0x11, 0x00 };
77 
78 static const char job_desc[][32] = {
79     "JOB_IDLE",
80     "JOB_RESET",
81     "JOB_SEND_INT",
82     "JOB_WRITE_REG",
83 };
84 
85 
86 //reset counters of the slave hardware, and clean the receive buffer (normally they should be sent back to the host)
slave_reset(void)87 static esp_err_t slave_reset(void)
88 {
89     esp_err_t ret;
90     sdio_slave_stop();
91     ret = sdio_slave_reset();
92     if (ret != ESP_OK) return ret;
93     ret = sdio_slave_start();
94     if (ret != ESP_OK) return ret;
95 
96     //Since the buffer will not be sent any more, we return them back to receving driver
97     while(1) {
98         sdio_slave_buf_handle_t handle;
99         ret = sdio_slave_send_get_finished(&handle, 0);
100         if (ret != ESP_OK) break;
101         ret = sdio_slave_recv_load_buf(handle);
102         ESP_ERROR_CHECK(ret);
103     }
104     return ESP_OK;
105 }
106 
107 //sent interrupts to the host in turns
task_hostint(void)108 static esp_err_t task_hostint(void)
109 {
110     for(int i = 0; i < 8; i++) {
111         ESP_LOGV(TAG, "send intr: %d", i);
112         sdio_slave_send_host_int(i);
113         //check reset for quick response to RESET signal
114         if (s_job & JOB_RESET) break;
115         vTaskDelay(500/portTICK_RATE_MS);
116     }
117     return ESP_OK;
118 }
119 
120 //read the value in a specified register set by the host, and set other register according to this.
121 //the host will read these registers later
task_write_reg(void)122 static esp_err_t task_write_reg(void)
123 {
124     //the host write REG1, the slave should write its registers according to value of REG1
125     uint8_t read = sdio_slave_read_reg(1);
126     for (int i = 0; i < 64; i++) {
127         //skip interrupt regs.
128         if (i >= 28 && i <= 31) continue;
129         sdio_slave_write_reg(i, read+3*i);
130     }
131     uint8_t reg[64] = {0};
132     for (int i = 0; i < 64; i++) {
133         //skip interrupt regs.
134         if (i >= 28 && i <= 31) continue;
135         reg[i] = sdio_slave_read_reg(i);
136     }
137     ESP_LOGI(TAG, "write regs:");
138     ESP_LOG_BUFFER_HEXDUMP(TAG, reg, 64, ESP_LOG_INFO);
139     return ESP_OK;
140 }
141 
142 //we use the event callback (in ISR) in this example to get higer responding speed
143 //note you can't do delay in the ISR
144 //``sdio_slave_wait_int`` is another way to handle interrupts
event_cb(uint8_t pos)145 static void event_cb(uint8_t pos)
146 {
147     ESP_EARLY_LOGD(TAG, "event: %d", pos);
148     switch(pos) {
149         case 0:
150             s_job = sdio_slave_read_reg(0);
151             sdio_slave_write_reg(0, JOB_IDLE);
152             break;
153     }
154 }
155 
156 DMA_ATTR uint8_t buffer[BUFFER_NUM][BUFFER_SIZE] = {};
157 
158 //Main application
app_main(void)159 void app_main(void)
160 {
161     esp_err_t ret;
162 
163     sdio_slave_config_t config = {
164         .sending_mode       = SDIO_SLAVE_SEND_PACKET,
165         .send_queue_size    = SDIO_SLAVE_QUEUE_SIZE,
166         .recv_buffer_size   = BUFFER_SIZE,
167         .event_cb           = event_cb,
168         /* Note: For small devkits there may be no pullups on the board.
169            This enables the internal pullups to help evaluate the driver
170            quickly. However the internal pullups are not sufficient and not
171            reliable, please make sure external pullups are connected to the
172            bus in your real design.
173         */
174         //.flags              = SDIO_SLAVE_FLAG_INTERNAL_PULLUP,
175     };
176 #ifdef CONFIG_SDIO_DAT2_DISABLED
177     /* For slave chips with 3.3V flash, DAT2 pullup conflicts with the pulldown
178        required by strapping pin (MTDI). We can either burn the EFUSE for the
179        strapping or just disable the DAT2 and work in 1-bit mode.
180      */
181     config.flags |= SDIO_SLAVE_FLAG_DAT2_DISABLED;
182 #endif
183 
184     ret = sdio_slave_initialize(&config);
185     ESP_ERROR_CHECK(ret);
186 
187     sdio_slave_write_reg(0, JOB_IDLE);
188 
189     sdio_slave_buf_handle_t handle;
190     for(int i = 0; i < BUFFER_NUM; i++) {
191         handle = sdio_slave_recv_register_buf(buffer[i]);
192         assert(handle != NULL);
193 
194         ret = sdio_slave_recv_load_buf(handle);
195         ESP_ERROR_CHECK(ret);
196     }
197 
198     sdio_slave_set_host_intena(SDIO_SLAVE_HOSTINT_SEND_NEW_PACKET |
199             SDIO_SLAVE_HOSTINT_BIT0 |
200             SDIO_SLAVE_HOSTINT_BIT1 |
201             SDIO_SLAVE_HOSTINT_BIT2 |
202             SDIO_SLAVE_HOSTINT_BIT3 |
203             SDIO_SLAVE_HOSTINT_BIT4 |
204             SDIO_SLAVE_HOSTINT_BIT5 |
205             SDIO_SLAVE_HOSTINT_BIT6 |
206             SDIO_SLAVE_HOSTINT_BIT7
207       );
208 
209     sdio_slave_start();
210 
211     ESP_LOGI(TAG, EV_STR("slave ready"));
212 
213     for(;;) {
214         //receive data and send back to host.
215         size_t length;
216         uint8_t *ptr;
217 
218         const TickType_t non_blocking = 0;
219         ret = sdio_slave_recv(&handle, &ptr, &length, non_blocking);
220         if (ret == ESP_OK) {
221             ESP_LOGI(TAG, "handle: %p, recv len: %d, data:", handle, length);
222             ESP_LOG_BUFFER_HEXDUMP(TAG, ptr, length, ESP_LOG_INFO);
223             /* If buffer is no longer used, call sdio_slave_recv_load_buf to return it here.  Since we wants to show how
224              * to share large buffers between drivers here (we share between sending and receiving), keep the buffer
225              * until the buffer is sent by sending driver.
226              */
227 
228             //send the received buffer to host, with the handle as the argument
229             ret = sdio_slave_send_queue(ptr, length, handle, non_blocking);
230             if (ret == ESP_ERR_TIMEOUT) {
231                 // send failed, direct return the buffer to rx
232                 ESP_LOGE(TAG, "send_queue full, discard received.");
233                 ret = sdio_slave_recv_load_buf(handle);
234             }
235             ESP_ERROR_CHECK(ret);
236         }
237 
238         // if there's finished sending desc, return the buffer to receiving driver
239         for(;;){
240             sdio_slave_buf_handle_t handle;
241             ret = sdio_slave_send_get_finished(&handle, 0);
242             if (ret == ESP_ERR_TIMEOUT) break;
243             ESP_ERROR_CHECK(ret);
244             ret = sdio_slave_recv_load_buf(handle);
245             ESP_ERROR_CHECK(ret);
246         }
247 
248         if (s_job != 0) {
249             for(int i = 0; i < 8; i++) {
250                 if (s_job & BIT(i)) {
251                     ESP_LOGI(TAG, EV_STR("%s"), job_desc[i+1]);
252                     s_job &= ~BIT(i);
253 
254                     switch(BIT(i)) {
255                     case JOB_SEND_INT:
256                         ret = task_hostint();
257                         ESP_ERROR_CHECK(ret);
258                         break;
259                     case JOB_RESET:
260                         ret = slave_reset();
261                         ESP_ERROR_CHECK(ret);
262                         break;
263                     case JOB_WRITE_REG:
264                         ret = task_write_reg();
265                         ESP_ERROR_CHECK(ret);
266                         break;
267                     }
268                 }
269             }
270         }
271         vTaskDelay(1);
272     }
273 }
274