-
Notifications
You must be signed in to change notification settings - Fork 0
/
vospi.c
187 lines (160 loc) · 4.9 KB
/
vospi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "include/vospi.h"
static const char* TAG = "VOSPIDriver";
static spi_device_handle_t spi;
#define CAMERA_SPI_HOST SPI3_HOST
#define CAMERA_SPI_SPEED_HZ 20000000
#define CAMERA_VIDEO_WIDTH 160
#define CAMERA_VIDEO_HEIGHT 120
/**
* Perform a SPI read.
*/
int spi_read(void* buf, uint32_t len)
{
esp_err_t ret;
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.rxlength = len * 8;
t.tx_buffer = NULL;
t.rx_buffer = buf;
ret = spi_device_transmit(spi, &t);
assert(ret == ESP_OK);
return ret;
}
/**
* Initialise the VoSPI interface.
*/
int vospi_init(uint8_t GpioMosiPin, uint8_t GpioMisoPin, uint8_t GpioClkPin, uint8_t GpioCsPin)
{
ESP_LOGI(TAG, "starting VoSPI initialisation...");
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num=GpioMisoPin,
.mosi_io_num=GpioMosiPin,
.sclk_io_num=GpioClkPin,
.max_transfer_sz=50000,
.quadwp_io_num=-1,
.quadhd_io_num=-1
};
spi_device_interface_config_t devcfg = {
.command_bits = 0,
.address_bits = 0,
.clock_speed_hz = CAMERA_SPI_SPEED_HZ,
.mode = 3,
.spics_io_num = GpioCsPin,
.queue_size = 1,
.flags = SPI_DEVICE_HALFDUPLEX,
.cs_ena_pretrans = 10
};
ret=spi_bus_initialize(CAMERA_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
assert(ret==ESP_OK);
ret=spi_bus_add_device(CAMERA_SPI_HOST, &devcfg, &spi);
assert(ret==ESP_OK);
return ret;
}
/**
* Transfer a single VoSPI segment.
* Returns the number of successfully-transferred segments (0 or 1).
*/
int transfer_segment(vospi_segment_t* segment)
{
// Perform the spidev transfer
if (spi_read(&segment->packets[0], VOSPI_PACKET_BYTES) != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer packet");
return 0;
}
// Flip the byte order of the ID & CRC
segment->packets[0].id = FLIP_WORD_BYTES(segment->packets[0].id);
segment->packets[0].crc = FLIP_WORD_BYTES(segment->packets[0].crc);
while ((segment->packets[0].id & 0x0f00) == 0x0f00) {
spi_read(&segment->packets[0], VOSPI_PACKET_BYTES);
}
// Read the remaining packets
if (spi_read(&segment->packets[1], VOSPI_PACKET_BYTES * (segment->packet_count - 1)) != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer the rest of the segment");
return 0;
}
// Flip the byte order for the rest of the packet IDs
for (int i = 1; i < segment->packet_count; i ++) {
segment->packets[i].id = FLIP_WORD_BYTES(segment->packets[i].id);
segment->packets[i].crc = FLIP_WORD_BYTES(segment->packets[i].crc);
}
return 1;
}
/**
* Synchroise the VoSPI stream and transfer a single frame.
* Returns the number of successfully-transferred frames (0 or 1).
*/
int sync_and_transfer_frame(vospi_frame_t* frame)
{
// Keep streaming segments until we receive a valid, first segment to sync
ESP_LOGD(TAG, "synchronising with first segment");
uint16_t packet_20_num;
uint8_t ttt_bits, resets = 0;
while (1) {
// Stream a first segment
if (!transfer_segment(&frame->segments[0])) {
ESP_LOGE(TAG, "failed to receive the first segment");
return 0;
}
// If the packet number isn't even correct, we'll reset the bus to sync
packet_20_num = frame->segments[0].packets[20].id & 0xff;
if (packet_20_num != 20) {
// Deselect the chip, wait 200ms with CS deasserted
ESP_LOGW(
TAG,
"packet 20 ID was %d (%02x)- deasserting CS & waiting to reset...",
packet_20_num,
frame->segments[0].packets[20].id
);
vTaskDelay(200 / portTICK_PERIOD_MS);
if (++resets >= VOSPI_MAX_SYNC_RESETS) {
ESP_LOGE(TAG, "too many resets while synchronising (%d)", resets);
return 0;
}
continue;
}
// Check we're looking at the first segment, if not, just keep reading until we get there
ttt_bits = frame->segments[0].packets[20].id >> 12;
ESP_LOGD(TAG, "TTT bits were: %d P20 Num: %d", ttt_bits, packet_20_num);
if (ttt_bits == 1) {
break;
}
}
// Receive the remaining segments
for (int seg = 1; seg < VOSPI_SEGMENTS_PER_FRAME; seg ++) {
transfer_segment(&frame->segments[seg]);
}
return 1;
}
/**
* Transfer a frame.
* Assumes that we're already synchronised with the VoSPI stream.
*/
int transfer_frame(vospi_frame_t* frame)
{
uint8_t ttt_bits, restarts = 0;
// Receive all segments
for (int seg = 0; seg < VOSPI_SEGMENTS_PER_FRAME; seg ++) {
transfer_segment(&frame->segments[seg]);
ttt_bits = frame->segments[seg].packets[20].id >> 12;
if (ttt_bits != seg + 1) {
seg --;
if (restarts ++ > VOSPI_MAX_INVALID_FRAMES * 4) {
ESP_LOGE(TAG, "too many invalid frames - need to resync");
return 0;
}
continue;
}
}
return 1;
}