Skip to content

Commit d749597

Browse files
committed
Add dual role (concurrent) example
This reads HID devices over host and then translates that to ASCII and sends it over CDC device.
1 parent 8a6fe8a commit d749597

File tree

9 files changed

+799
-3
lines changed

9 files changed

+799
-3
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
board:mimxrt1060_evk

examples/host/hid_to_cdc/src/main.c

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
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+
/* Blink pattern
43+
* - 250 ms : device not mounted
44+
* - 1000 ms : device mounted
45+
* - 2500 ms : device is suspended
46+
*/
47+
enum {
48+
BLINK_NOT_MOUNTED = 250,
49+
BLINK_MOUNTED = 1000,
50+
BLINK_SUSPENDED = 2500,
51+
};
52+
53+
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
54+
55+
void led_blinking_task(void);
56+
void cdc_task(void);
57+
58+
/*------------- MAIN -------------*/
59+
int main(void)
60+
{
61+
board_init();
62+
tusb_init();
63+
64+
while (1)
65+
{
66+
tud_task(); // tinyusb device task
67+
tuh_task(); // tinyusb host task
68+
led_blinking_task();
69+
70+
cdc_task();
71+
}
72+
73+
return 0;
74+
}
75+
76+
//--------------------------------------------------------------------+
77+
// Device callbacks
78+
//--------------------------------------------------------------------+
79+
80+
// Invoked when device is mounted
81+
void tud_mount_cb(void)
82+
{
83+
blink_interval_ms = BLINK_MOUNTED;
84+
}
85+
86+
// Invoked when device is unmounted
87+
void tud_umount_cb(void)
88+
{
89+
blink_interval_ms = BLINK_NOT_MOUNTED;
90+
}
91+
92+
// Invoked when usb bus is suspended
93+
// remote_wakeup_en : if host allow us to perform remote wakeup
94+
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
95+
void tud_suspend_cb(bool remote_wakeup_en)
96+
{
97+
(void) remote_wakeup_en;
98+
blink_interval_ms = BLINK_SUSPENDED;
99+
}
100+
101+
// Invoked when usb bus is resumed
102+
void tud_resume_cb(void)
103+
{
104+
blink_interval_ms = BLINK_MOUNTED;
105+
}
106+
107+
//--------------------------------------------------------------------+
108+
// Host callbacks
109+
//--------------------------------------------------------------------+
110+
111+
// Invoked when device with hid interface is mounted
112+
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
113+
// can be used to parse common/simple enough descriptor.
114+
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
115+
// therefore report_desc = NULL, desc_len = 0
116+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
117+
{
118+
(void)desc_report;
119+
(void)desc_len;
120+
uint16_t vid, pid;
121+
tuh_vid_pid_get(dev_addr, &vid, &pid);
122+
123+
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
124+
printf("VID = %04x, PID = %04x\r\n", vid, pid);
125+
126+
// Receive any report and treat it like a keyboard.
127+
// tuh_hid_report_received_cb() will be invoked when report is available
128+
if ( !tuh_hid_receive_report(dev_addr, instance) )
129+
{
130+
printf("Error: cannot request to receive report\r\n");
131+
}
132+
}
133+
134+
// Invoked when device with hid interface is un-mounted
135+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
136+
{
137+
printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
138+
}
139+
140+
const char* numbers = "0123456789";
141+
142+
// Uncomment if you use colemak and need to remap keys (like @tannewt.)
143+
// const uint8_t colemak[77] = {
144+
// 0, 0, 0, 0, 0, 0, 0, 22,
145+
// 9, 23, 7, 0, 24, 17, 8, 12,
146+
// 0, 14, 28, 51, 0, 19, 21, 10,
147+
// 15, 0, 0, 0, 13, 0, 0, 0,
148+
// 0, 0, 0, 0, 0, 0, 0, 0,
149+
// 0, 0, 0, 0, 0, 0, 0, 0,
150+
// 0, 0, 0, 18, 0, 0, 0, 0,
151+
// 0, 0, 0, 0, 0, 0, 0, 0,
152+
// 0, 0, 0, 0, 0, 0, 0, 0,
153+
// 0, 0, 0, 0, 0
154+
// };
155+
156+
// This is the reverse mapping of the US key layout in Adafruit_CircuitPython_HID.
157+
const char* ascii = "\0\0\0\0abcdefghijklmnopqrstuvwxyz1234567890\n\x1b\x08\t -=[]\\\x00;\'`,./\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7f";
158+
const char* shifted = "\0\0\0\0ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()\0\0\0\0\0_+{}|\0:\"~<>?\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
159+
// Bitmask of pressed keys. We use the current modifier state.
160+
uint32_t last_state[8];
161+
162+
// Invoked when received report from device via interrupt endpoint
163+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
164+
{
165+
if (len != 8 && len < 10) {
166+
tud_cdc_write("report len: ", 12);
167+
tud_cdc_write(numbers + len, 1);
168+
tud_cdc_write("\r\n", 2);
169+
tud_cdc_write_flush();
170+
}
171+
if (len != 8) {
172+
// Don't request a new report for a wrong sized endpoint.
173+
return;
174+
}
175+
uint8_t modifiers = report[0];
176+
bool flush = false;
177+
for (int i = 2; i < 8; i++) {
178+
if (report[i] == 0) {
179+
continue;
180+
}
181+
uint8_t down = report[i];
182+
uint32_t mask = 1 << (down % 32);
183+
bool was_down = (last_state[down / 32] & mask) != 0;
184+
// Only map keycodes 0 - 76.
185+
if (!was_down && down < 77) {
186+
const char* layer = ascii;
187+
// Check shift bits
188+
if ((modifiers & 0x22) != 0) {
189+
layer = shifted;
190+
}
191+
// Map the key code for Colemak layout so @tannewt can type.
192+
// uint8_t colemak_key_code = colemak[down];
193+
// if (colemak_key_code != 0) {
194+
// down = colemak_key_code;
195+
// }
196+
char c = layer[down];
197+
if (c == '\0') {
198+
continue;
199+
}
200+
if (c == '\n') {
201+
tud_cdc_write("\r", 1);
202+
}
203+
tud_cdc_write(&c, 1);
204+
flush = true;
205+
}
206+
}
207+
if (flush) {
208+
tud_cdc_write_flush();
209+
}
210+
// Now update last_state
211+
memset(last_state, 0, 8 * sizeof(uint32_t));
212+
for (int i = 2; i < 8; i++) {
213+
if (report[i] == 0) {
214+
continue;
215+
}
216+
uint8_t down = report[i];
217+
last_state[down / 32] |= 1 << (down % 32);
218+
}
219+
// continue to request to receive report
220+
if ( !tuh_hid_receive_report(dev_addr, instance) )
221+
{
222+
printf("Error: cannot request to receive report\r\n");
223+
}
224+
}
225+
226+
227+
228+
//--------------------------------------------------------------------+
229+
// USB CDC
230+
//--------------------------------------------------------------------+
231+
void cdc_task(void)
232+
{
233+
// connected() check for DTR bit
234+
// Most but not all terminal client set this when making connection
235+
// if ( tud_cdc_connected() )
236+
{
237+
// connected and there are data available
238+
if ( tud_cdc_available() )
239+
{
240+
// read datas
241+
char buf[64];
242+
uint32_t count = tud_cdc_read(buf, sizeof(buf));
243+
(void) count;
244+
245+
// Echo back
246+
// Note: Skip echo by commenting out write() and write_flush()
247+
// for throughput test e.g
248+
// $ dd if=/dev/zero of=/dev/ttyACM0 count=10000
249+
tud_cdc_write(buf, count);
250+
tud_cdc_write_flush();
251+
}
252+
}
253+
}
254+
255+
// Invoked when cdc when line state changed e.g connected/disconnected
256+
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
257+
{
258+
(void) itf;
259+
(void) rts;
260+
261+
// TODO set some indicator
262+
if ( dtr )
263+
{
264+
// Terminal connected
265+
}else
266+
{
267+
// Terminal disconnected
268+
}
269+
}
270+
271+
// Invoked when CDC interface received data from host
272+
void tud_cdc_rx_cb(uint8_t itf)
273+
{
274+
(void) itf;
275+
}
276+
277+
//--------------------------------------------------------------------+
278+
// BLINKING TASK
279+
//--------------------------------------------------------------------+
280+
void led_blinking_task(void)
281+
{
282+
static uint32_t start_ms = 0;
283+
static bool led_state = false;
284+
285+
// Blink every interval ms
286+
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
287+
start_ms += blink_interval_ms;
288+
289+
board_led_write(led_state);
290+
led_state = 1 - led_state; // toggle
291+
}

0 commit comments

Comments
 (0)