blob: f1d48385f4f6ab1f2a4a0ad227b3aad08764bcaf [file] [log] [blame]
Austin Schuh41baf202022-01-01 14:33:40 -08001/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 */
25
26#include <stdlib.h>
27#include <stdio.h>
28#include <string.h>
29
30#include "bsp/board.h"
31#include "tusb.h"
32#include "usb_descriptors.h"
33
34//--------------------------------------------------------------------+
35// MACRO CONSTANT TYPEDEF PROTYPES
36//--------------------------------------------------------------------+
37
38/* Blink pattern
39 * - 250 ms : device not mounted
40 * - 1000 ms : device mounted
41 * - 2500 ms : device is suspended
42 */
43enum {
44 BLINK_NOT_MOUNTED = 250,
45 BLINK_MOUNTED = 1000,
46 BLINK_SUSPENDED = 2500,
47};
48
49static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
50
51void led_blinking_task(void);
52void video_task(void);
53
54/*------------- MAIN -------------*/
55int main(void)
56{
57 board_init();
58 tusb_init();
59
60 while (1)
61 {
62 tud_task(); // tinyusb device task
63 led_blinking_task();
64
65 video_task();
66 }
67
68 return 0;
69}
70
71//--------------------------------------------------------------------+
72// Device callbacks
73//--------------------------------------------------------------------+
74
75// Invoked when device is mounted
76void tud_mount_cb(void)
77{
78 blink_interval_ms = BLINK_MOUNTED;
79}
80
81// Invoked when device is unmounted
82void tud_umount_cb(void)
83{
84 blink_interval_ms = BLINK_NOT_MOUNTED;
85}
86
87// Invoked when usb bus is suspended
88// remote_wakeup_en : if host allow us to perform remote wakeup
89// Within 7ms, device must draw an average of current less than 2.5 mA from bus
90void tud_suspend_cb(bool remote_wakeup_en)
91{
92 (void) remote_wakeup_en;
93 blink_interval_ms = BLINK_SUSPENDED;
94}
95
96// Invoked when usb bus is resumed
97void tud_resume_cb(void)
98{
99 blink_interval_ms = BLINK_MOUNTED;
100}
101
102
103//--------------------------------------------------------------------+
104// USB Video
105//--------------------------------------------------------------------+
106static unsigned frame_num = 0;
107static unsigned tx_busy = 0;
108static unsigned interval_ms = 1000 / FRAME_RATE;
109
110/* YUY2 frame buffer */
111#ifdef CFG_EXAMPLE_VIDEO_READONLY
112#include "images.h"
113#else
114static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8];
115static void fill_color_bar(uint8_t *buffer, unsigned start_position)
116{
117 /* EBU color bars
118 * See also https://stackoverflow.com/questions/6939422 */
119 static uint8_t const bar_color[8][4] = {
120 /* Y, U, Y, V */
121 { 235, 128, 235, 128}, /* 100% White */
122 { 219, 16, 219, 138}, /* Yellow */
123 { 188, 154, 188, 16}, /* Cyan */
124 { 173, 42, 173, 26}, /* Green */
125 { 78, 214, 78, 230}, /* Magenta */
126 { 63, 102, 63, 240}, /* Red */
127 { 32, 240, 32, 118}, /* Blue */
128 { 16, 128, 16, 128}, /* Black */
129 };
130 uint8_t *p;
131
132 /* Generate the 1st line */
133 uint8_t *end = &buffer[FRAME_WIDTH * 2];
134 unsigned idx = (FRAME_WIDTH / 2 - 1) - (start_position % (FRAME_WIDTH / 2));
135 p = &buffer[idx * 4];
136 for (unsigned i = 0; i < 8; ++i) {
137 for (int j = 0; j < FRAME_WIDTH / (2 * 8); ++j) {
138 memcpy(p, &bar_color[i], 4);
139 p += 4;
140 if (end <= p) {
141 p = buffer;
142 }
143 }
144 }
145 /* Duplicate the 1st line to the others */
146 p = &buffer[FRAME_WIDTH * 2];
147 for (unsigned i = 1; i < FRAME_HEIGHT; ++i) {
148 memcpy(p, buffer, FRAME_WIDTH * 2);
149 p += FRAME_WIDTH * 2;
150 }
151}
152#endif
153
154void video_task(void)
155{
156 static unsigned start_ms = 0;
157 static unsigned already_sent = 0;
158
159 if (!tud_video_n_streaming(0, 0)) {
160 already_sent = 0;
161 frame_num = 0;
162 return;
163 }
164
165 if (!already_sent) {
166 already_sent = 1;
167 start_ms = board_millis();
168#ifdef CFG_EXAMPLE_VIDEO_READONLY
169 tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t) &frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
170 FRAME_WIDTH * FRAME_HEIGHT * 16/8);
171#else
172 fill_color_bar(frame_buffer, frame_num);
173 tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
174#endif
175 }
176
177 unsigned cur = board_millis();
178 if (cur - start_ms < interval_ms) return; // not enough time
179 if (tx_busy) return;
180 start_ms += interval_ms;
181
182#ifdef CFG_EXAMPLE_VIDEO_READONLY
183 tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t) &frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
184 FRAME_WIDTH * FRAME_HEIGHT * 16/8);
185#else
186 fill_color_bar(frame_buffer, frame_num);
187 tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
188#endif
189}
190
191void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
192{
193 (void)ctl_idx; (void)stm_idx;
194 tx_busy = 0;
195 /* flip buffer */
196 ++frame_num;
197}
198
199int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
200 video_probe_and_commit_control_t const *parameters)
201{
202 (void)ctl_idx; (void)stm_idx;
203 /* convert unit to ms from 100 ns */
204 interval_ms = parameters->dwFrameInterval / 10000;
205 return VIDEO_ERROR_NONE;
206}
207
208//--------------------------------------------------------------------+
209// BLINKING TASK
210//--------------------------------------------------------------------+
211void led_blinking_task(void)
212{
213 static uint32_t start_ms = 0;
214 static bool led_state = false;
215
216 // Blink every interval ms
217 if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
218 start_ms += blink_interval_ms;
219
220 board_led_write(led_state);
221 led_state = 1 - led_state; // toggle
222}