Skip to content

Commit b797d1a

Browse files
authored
Merge pull request hathach#1340 from tannewt/hid_to_cdc
Add concurrent host and device example
2 parents fa895ed + a8d6e82 commit b797d1a

File tree

11 files changed

+833
-6
lines changed

11 files changed

+833
-6
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
3+
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
4+
5+
# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
6+
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
7+
8+
project(${PROJECT})
9+
10+
# Checks this example is valid for the family and initializes the project
11+
family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
12+
13+
add_executable(${PROJECT})
14+
15+
# Example source
16+
target_sources(${PROJECT} PUBLIC
17+
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
18+
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
19+
)
20+
21+
# Example include
22+
target_include_directories(${PROJECT} PUBLIC
23+
${CMAKE_CURRENT_SOURCE_DIR}/src
24+
)
25+
26+
# Configure compilation flags and libraries for the example... see the corresponding function
27+
# in hw/bsp/FAMILY/family.cmake for details.
28+
family_configure_device_example(${PROJECT})

examples/host/hid_to_cdc/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
include ../../../tools/top.mk
2+
include ../../make.mk
3+
4+
INC += \
5+
src \
6+
$(TOP)/hw \
7+
8+
# Example source
9+
EXAMPLE_SOURCE += $(wildcard src/*.c)
10+
SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
11+
12+
CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference
13+
14+
SRC_C += \
15+
src/class/hid/hid_host.c \
16+
src/host/hub.c \
17+
src/host/usbh.c \
18+
src/host/usbh_control.c
19+
20+
include ../../rules.mk

examples/host/hid_to_cdc/only.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
board:mimxrt1060_evk
2+
board:mimxrt1064_evk

examples/host/hid_to_cdc/src/main.c

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
/*
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+
// This example runs both host and device concurrently. The USB host looks for
27+
// any HID device with reports that are 8 bytes long and then assumes they are
28+
// keyboard reports. It translates the keypresses of the reports to ASCII and
29+
// transmits it over CDC to the device's host.
30+
31+
#include <stdlib.h>
32+
#include <stdio.h>
33+
#include <string.h>
34+
35+
#include "bsp/board.h"
36+
#include "tusb.h"
37+
38+
//--------------------------------------------------------------------+
39+
// MACRO CONSTANT TYPEDEF PROTYPES
40+
//--------------------------------------------------------------------+
41+
42+
// uncomment if you are using colemak layout
43+
// #define KEYBOARD_COLEMAK
44+
45+
#ifdef KEYBOARD_COLEMAK
46+
const uint8_t colemak[128] = {
47+
0 , 0, 0, 0, 0, 0, 0, 22,
48+
9 , 23, 7, 0, 24, 17, 8, 12,
49+
0 , 14, 28, 51, 0, 19, 21, 10,
50+
15 , 0, 0, 0, 13, 0, 0, 0,
51+
0 , 0, 0, 0, 0, 0, 0, 0,
52+
0 , 0, 0, 0, 0, 0, 0, 0,
53+
0 , 0, 0, 18, 0, 0, 0, 0,
54+
0 , 0, 0, 0, 0, 0, 0, 0,
55+
0 , 0, 0, 0, 0, 0, 0, 0,
56+
0 , 0, 0, 0, 0, 0, 0, 0
57+
};
58+
#endif
59+
60+
static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
61+
62+
/* Blink pattern
63+
* - 250 ms : device not mounted
64+
* - 1000 ms : device mounted
65+
* - 2500 ms : device is suspended
66+
*/
67+
enum {
68+
BLINK_NOT_MOUNTED = 250,
69+
BLINK_MOUNTED = 1000,
70+
BLINK_SUSPENDED = 2500,
71+
};
72+
73+
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
74+
75+
void led_blinking_task(void);
76+
void cdc_task(void);
77+
78+
/*------------- MAIN -------------*/
79+
int main(void)
80+
{
81+
board_init();
82+
tusb_init();
83+
84+
while (1)
85+
{
86+
tud_task(); // tinyusb device task
87+
tuh_task(); // tinyusb host task
88+
led_blinking_task();
89+
90+
cdc_task();
91+
}
92+
93+
return 0;
94+
}
95+
96+
//--------------------------------------------------------------------+
97+
// Device callbacks
98+
//--------------------------------------------------------------------+
99+
100+
// Invoked when device is mounted
101+
void tud_mount_cb(void)
102+
{
103+
blink_interval_ms = BLINK_MOUNTED;
104+
}
105+
106+
// Invoked when device is unmounted
107+
void tud_umount_cb(void)
108+
{
109+
blink_interval_ms = BLINK_NOT_MOUNTED;
110+
}
111+
112+
// Invoked when usb bus is suspended
113+
// remote_wakeup_en : if host allow us to perform remote wakeup
114+
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
115+
void tud_suspend_cb(bool remote_wakeup_en)
116+
{
117+
(void) remote_wakeup_en;
118+
blink_interval_ms = BLINK_SUSPENDED;
119+
}
120+
121+
// Invoked when usb bus is resumed
122+
void tud_resume_cb(void)
123+
{
124+
blink_interval_ms = BLINK_MOUNTED;
125+
}
126+
127+
//--------------------------------------------------------------------+
128+
// Host callbacks
129+
//--------------------------------------------------------------------+
130+
131+
// Invoked when device with hid interface is mounted
132+
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
133+
// can be used to parse common/simple enough descriptor.
134+
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
135+
// therefore report_desc = NULL, desc_len = 0
136+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
137+
{
138+
(void)desc_report;
139+
(void)desc_len;
140+
uint16_t vid, pid;
141+
tuh_vid_pid_get(dev_addr, &vid, &pid);
142+
143+
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
144+
printf("VID = %04x, PID = %04x\r\n", vid, pid);
145+
146+
// Receive any report and treat it like a keyboard.
147+
// tuh_hid_report_received_cb() will be invoked when report is available
148+
if ( !tuh_hid_receive_report(dev_addr, instance) )
149+
{
150+
printf("Error: cannot request to receive report\r\n");
151+
}
152+
}
153+
154+
// Invoked when device with hid interface is un-mounted
155+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
156+
{
157+
printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
158+
}
159+
160+
// keycodes from last report to check if key is holding or newly pressed
161+
uint8_t last_keycodes[6] = {0};
162+
163+
// look up new key in previous keys
164+
static inline bool key_in_last_report(const uint8_t key_arr[6], uint8_t keycode)
165+
{
166+
for(uint8_t i=0; i<6; i++)
167+
{
168+
if (key_arr[i] == keycode) return true;
169+
}
170+
171+
return false;
172+
}
173+
174+
// Invoked when received report from device via interrupt endpoint
175+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
176+
{
177+
if (len != 8)
178+
{
179+
char ch_num;
180+
181+
tud_cdc_write_str("incorrect report len: ");
182+
183+
if ( len > 10 )
184+
{
185+
ch_num = '0' + (len / 10);
186+
tud_cdc_write(&ch_num, 1);
187+
len = len % 10;
188+
}
189+
190+
ch_num = '0' + len;
191+
tud_cdc_write(&ch_num, 1);
192+
193+
tud_cdc_write_str("\r\n");
194+
tud_cdc_write_flush();
195+
196+
// Don't request a new report for a wrong sized endpoint.
197+
return;
198+
}
199+
200+
uint8_t const modifiers = report[0];
201+
bool flush = false;
202+
203+
for (int i = 2; i < 8; i++)
204+
{
205+
uint8_t keycode = report[i];
206+
207+
if (keycode)
208+
{
209+
if ( key_in_last_report(last_keycodes, keycode) )
210+
{
211+
// exist in previous report means the current key is holding
212+
// do nothing
213+
}else
214+
{
215+
// not existed in previous report means the current key is pressed
216+
// Only print keycodes 0 - 128.
217+
if (keycode < 128)
218+
{
219+
// remap the key code for Colemak layout so @tannewt can type.
220+
#ifdef KEYBOARD_COLEMAK
221+
uint8_t colemak_key_code = colemak[keycode];
222+
if (colemak_key_code != 0) keycode = colemak_key_code;
223+
#endif
224+
225+
bool const is_shift = modifiers & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
226+
char c = keycode2ascii[keycode][is_shift ? 1 : 0];
227+
if (c)
228+
{
229+
if (c == '\n') tud_cdc_write("\r", 1);
230+
tud_cdc_write(&c, 1);
231+
flush = true;
232+
}
233+
}
234+
}
235+
}
236+
}
237+
238+
if (flush) tud_cdc_write_flush();
239+
240+
// save current report
241+
memcpy(last_keycodes, report+2, 6);
242+
243+
// continue to request to receive report
244+
if ( !tuh_hid_receive_report(dev_addr, instance) )
245+
{
246+
printf("Error: cannot request to receive report\r\n");
247+
}
248+
}
249+
250+
251+
252+
//--------------------------------------------------------------------+
253+
// USB CDC
254+
//--------------------------------------------------------------------+
255+
void cdc_task(void)
256+
{
257+
// connected() check for DTR bit
258+
// Most but not all terminal client set this when making connection
259+
// if ( tud_cdc_connected() )
260+
{
261+
// connected and there are data available
262+
if ( tud_cdc_available() )
263+
{
264+
// read datas
265+
char buf[64];
266+
uint32_t count = tud_cdc_read(buf, sizeof(buf));
267+
(void) count;
268+
269+
// Echo back
270+
// Note: Skip echo by commenting out write() and write_flush()
271+
// for throughput test e.g
272+
// $ dd if=/dev/zero of=/dev/ttyACM0 count=10000
273+
tud_cdc_write(buf, count);
274+
tud_cdc_write_flush();
275+
}
276+
}
277+
}
278+
279+
// Invoked when cdc when line state changed e.g connected/disconnected
280+
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
281+
{
282+
(void) itf;
283+
(void) rts;
284+
285+
// TODO set some indicator
286+
if ( dtr )
287+
{
288+
// Terminal connected
289+
}else
290+
{
291+
// Terminal disconnected
292+
}
293+
}
294+
295+
// Invoked when CDC interface received data from host
296+
void tud_cdc_rx_cb(uint8_t itf)
297+
{
298+
(void) itf;
299+
}
300+
301+
//--------------------------------------------------------------------+
302+
// BLINKING TASK
303+
//--------------------------------------------------------------------+
304+
void led_blinking_task(void)
305+
{
306+
static uint32_t start_ms = 0;
307+
static bool led_state = false;
308+
309+
// Blink every interval ms
310+
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
311+
start_ms += blink_interval_ms;
312+
313+
board_led_write(led_state);
314+
led_state = 1 - led_state; // toggle
315+
}

0 commit comments

Comments
 (0)