aboutsummaryrefslogtreecommitdiffstats
path: root/examples/baremetal/msp430f5529
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2019-10-30 16:52:03 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2019-11-02 09:05:44 +0000
commit77719b58932541e63d926ee9f454163c57273670 (patch)
treee22028f4a835d43c0eb89e31d16d924e6dafb0bd /examples/baremetal/msp430f5529
parent4ce68dd8dd4a012545a5d7f19039840e416ddfc3 (diff)
baremetal: Add USB HID gamepads example for MSP430f5529 chip
This example demonstrates how to create the USB HID device on MSP430f5529 chip (based on MSP430 architecture) using the different toolchains (IAR EW, GCC). Change-Id: Ic4d6a17d3e75e7ccb9b1f3eeaaef9abdf2da896b Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'examples/baremetal/msp430f5529')
-rw-r--r--examples/baremetal/msp430f5529/msp430f5529.qbs3
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/README.md36
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/gamepads.ld57
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/gpio.c167
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/gpio.h110
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/hid.h116
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/hiddesc.c303
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/hidep0.c554
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/hidep1.c214
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/hwdefs.h81
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/main.c92
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs148
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/pmm.c237
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/pmm.h73
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/ucs.c264
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/ucs.h106
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/usb.c317
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/usb.h148
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/wdt_a.c60
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/wdt_a.h64
20 files changed, 3149 insertions, 1 deletions
diff --git a/examples/baremetal/msp430f5529/msp430f5529.qbs b/examples/baremetal/msp430f5529/msp430f5529.qbs
index 7f9799208..f4a6f95b9 100644
--- a/examples/baremetal/msp430f5529/msp430f5529.qbs
+++ b/examples/baremetal/msp430f5529/msp430f5529.qbs
@@ -53,6 +53,7 @@ import qbs
Project {
name: "Examples for msp430f5529 board"
references: [
- "redblink/redblink.qbs"
+ "redblink/redblink.qbs",
+ "nes-gamepads/nes-gamepads.qbs"
]
}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/README.md b/examples/baremetal/msp430f5529/nes-gamepads/README.md
new file mode 100644
index 000000000..442bb587a
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/README.md
@@ -0,0 +1,36 @@
+This example demonstrates how to build a bare-metal application using
+different MSP430 toolchains. It is designed for the MSP-EXP430F5529LP
+target board (based on msp430f5529 chip):
+
+* http://www.ti.com/tool/MSP-EXP430F5529LP
+
+It implements a USB HID device that connects two 8-buttons NES
+(Dendy) gamepads to a PC. The gamepads are connected to the
+msp430f5529 chip as follows:
+
+1. CLK - it is an output clock signal which generates by chip from
+ the port 6, pin 0 (P6.0). This pin should be connected to the CLK
+ inputs for both gamepads.
+
+2. DATA1 - it is an input data signal which comes to chip on the
+ the port 6, pin 1 (P6.1). This pin should be connected to the DATA
+ output from the gamepad #1.
+
+3. DATA2 - it is an input data signal which comes to chip on the
+ the port 6, pin 2 (P6.2). This pin should be connected to the DATA
+ output from the gamepad #2.
+
+4. LATCH - it is an output clock signal which generates by chip from
+ the port 6, pin 3 (P6.3). This pin should be connected to the LATCH
+ inputs for both gamepads.
+
+Actual schematic and pinouts depends on an used gamepads (with 7, 9
+or other pins connectors) and a development boards.
+
+Also, do not forget to connect the +3.3V and GND wires to the gamepads.
+Then it is possible to play 8-bit NES games using various PC simulators.
+
+The following toolchains are supported:
+
+ * IAR Embedded Workbench
+ * GCC
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/gamepads.ld b/examples/baremetal/msp430f5529/nes-gamepads/gamepads.ld
new file mode 100644
index 000000000..a3906a474
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/gamepads.ld
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+g_setupdat = 0x2380; /* Setup packet block address. */
+
+g_ep0 = 0x0920; /* Input ep0 configuration address. */
+g_ep0_in_buf = 0x2378; /* Input ep0 buffer address. */
+
+g_ep1_in = 0x23C8; /* Input ep1 configuration address. */
+g_ep1_in_xbuf = 0x1C80; /* Ep1 X-buffer address. */
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/gpio.c b/examples/baremetal/msp430f5529/nes-gamepads/gpio.c
new file mode 100644
index 000000000..b5e58bc7c
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/gpio.c
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hwdefs.h"
+#include "gpio.h"
+
+enum { INVALID_ADDRESS = 0xFFFF };
+
+static uint16_t gpio_port_address_get(enum gpio_port port)
+{
+ switch (port) {
+ case GPIO_PORT_P1:
+ return P1_BASE;
+ case GPIO_PORT_P2:
+ return P2_BASE;
+ case GPIO_PORT_P3:
+ return P3_BASE;
+ case GPIO_PORT_P4:
+ return P4_BASE;
+ case GPIO_PORT_P5:
+ return P5_BASE;
+ case GPIO_PORT_P6:
+ return P6_BASE;
+ case GPIO_PORT_P7:
+ return P7_BASE;
+ case GPIO_PORT_P8:
+ return P8_BASE;
+ case GPIO_PORT_PJ:
+ return PJ_BASE;
+ default:
+ break;
+ }
+ return INVALID_ADDRESS;
+}
+
+static uint16_t gpio_pins_adjust(enum gpio_port port, uint16_t pins)
+{
+ if ((port & 1) ^ 1)
+ pins <<= 8;
+ return pins;
+}
+
+void gpio_pins_set_as_out(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PASEL) &= ~pins;
+ HWREG16(base_address + OFS_PADIR) |= pins;
+}
+
+void gpio_pins_set_as_in(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PASEL) &= ~pins;
+ HWREG16(base_address + OFS_PADIR) &= ~pins;
+ HWREG16(base_address + OFS_PAREN) &= ~pins;
+}
+
+void gpio_pins_set_as_pf_out(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PADIR) |= pins;
+ HWREG16(base_address + OFS_PASEL) |= pins;
+}
+
+void gpio_pins_set_as_pf_in(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PADIR) &= ~pins;
+ HWREG16(base_address + OFS_PASEL) |= pins;
+}
+
+void gpio_pins_set_high(enum gpio_port port,
+ uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PAOUT) |= pins;
+}
+
+void gpio_pins_set_low(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PAOUT) &= ~pins;
+}
+
+void gpio_pins_toggle(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return;
+ pins = gpio_pins_adjust(port, pins);
+ HWREG16(base_address + OFS_PAOUT) ^= pins;
+}
+
+enum gpio_pin_status gpio_pin_get(enum gpio_port port, uint16_t pins)
+{
+ const uint16_t base_address = gpio_port_address_get(port);
+ if (base_address == INVALID_ADDRESS)
+ return GPIO_INPUT_PIN_LOW;
+ pins = gpio_pins_adjust(port, pins);
+ const uint16_t value = HWREG16(base_address + OFS_PAIN) & pins;
+ return (value > 0) ? GPIO_INPUT_PIN_HIGH : GPIO_INPUT_PIN_LOW;
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/gpio.h b/examples/baremetal/msp430f5529/nes-gamepads/gpio.h
new file mode 100644
index 000000000..d69a543b5
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/gpio.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_GPIO_H
+#define MSP430_GPIO_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum gpio_port {
+ GPIO_PORT_P1 = 1,
+ GPIO_PORT_P2 = 2,
+ GPIO_PORT_P3 = 3,
+ GPIO_PORT_P4 = 4,
+ GPIO_PORT_P5 = 5,
+ GPIO_PORT_P6 = 6,
+ GPIO_PORT_P7 = 7,
+ GPIO_PORT_P8 = 8,
+ GPIO_PORT_PJ = 13,
+};
+
+enum gpio_pin {
+ GPIO_PIN0 = 0x0001,
+ GPIO_PIN1 = 0x0002,
+ GPIO_PIN2 = 0x0004,
+ GPIO_PIN3 = 0x0008,
+ GPIO_PIN4 = 0x0010,
+ GPIO_PIN5 = 0x0020,
+ GPIO_PIN6 = 0x0040,
+ GPIO_PIN7 = 0x0080,
+ GPIO_PIN8 = 0x0100,
+ GPIO_PIN9 = 0x0200,
+ GPIO_PIN10 = 0x0400,
+ GPIO_PIN11 = 0x0800,
+ GPIO_PIN12 = 0x1000,
+ GPIO_PIN13 = 0x2000,
+ GPIO_PIN14 = 0x4000,
+ GPIO_PIN15 = 0x8000,
+};
+
+enum gpio_pin_status {
+ GPIO_INPUT_PIN_HIGH = 0x01,
+ GPIO_INPUT_PIN_LOW = 0x00
+};
+
+void gpio_pins_set_as_out(enum gpio_port port, uint16_t pins);
+void gpio_pins_set_as_in(enum gpio_port port, uint16_t pins);
+void gpio_pins_set_as_pf_out(enum gpio_port port, uint16_t pins);
+void gpio_pins_set_as_pf_in(enum gpio_port port, uint16_t pins);
+void gpio_pins_set_high(enum gpio_port port, uint16_t pins);
+void gpio_pins_set_low(enum gpio_port port, uint16_t pins);
+void gpio_pins_toggle(enum gpio_port port, uint16_t pins);
+
+enum gpio_pin_status gpio_pin_get(enum gpio_port port, uint16_t pins);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MSP430_GPIO_H
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/hid.h b/examples/baremetal/msp430f5529/nes-gamepads/hid.h
new file mode 100644
index 000000000..1307301dc
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/hid.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_HID_H
+#define MSP430_HID_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum hid_constants {
+ HID_CONFIG_NUMBER = 1, // Number of valid configuration.
+ HID_IFACE_NUMBER = 0, // Number of valid interface.
+ HID_ALT_IFACE_NUMBER = 0, // Number of valid alternate interface.
+ HID_EP_IN = 0x81 // Active end point address.
+};
+
+enum hid_gamepad_id {
+ HID_REPORT_ID_GAMEPAD1 = 1,
+ HID_REPORT_ID_GAMEPAD2 = 2
+};
+
+enum {
+ HID_REPORTS_COUNT = 2,
+ HID_REPORT_BITS_COUNT = 8
+};
+
+void hid_ep0_init(void);
+
+// Called only in interrupt context.
+void hid_ep0_setup_handler(void);
+void hid_ep0_in_handler(void);
+
+void hid_ep0_in_nak(void);
+void hid_ep0_in_stall(void);
+void hid_ep0_in_clear(void);
+bool hid_ep0_in_is_stalled(void);
+
+void hid_ep0_out_nak(void);
+void hid_ep0_out_stall(void);
+void hid_ep0_out_clear(void);
+
+const uint8_t *hid_ep0_desc_get(uint8_t type, uint16_t *length);
+
+uint8_t hid_ep0_setup_bm_request_type_get(void);
+uint8_t hid_ep0_setup_request_get(void);
+uint8_t hid_ep0_setup_lvalue_get(void);
+uint8_t hid_ep0_setup_hvalue_get(void);
+uint8_t hid_ep0_setup_lindex_get(void);
+uint8_t hid_ep0_setup_hindex_get(void);
+
+void hid_ep0_enumerated_set(bool enumerated);
+bool hid_ep0_is_enumerated(void);
+
+void hid_ep1_init(void);
+void hid_ep1_task(void);
+
+void hid_ep1_in_stall(void);
+void hid_ep1_in_unstall(void);
+bool hid_ep1_in_is_stalled(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MSP430_HID_H
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/hiddesc.c b/examples/baremetal/msp430f5529/nes-gamepads/hiddesc.c
new file mode 100644
index 000000000..eb391d6c0
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/hiddesc.c
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hid.h"
+#include "usb.h"
+
+#include <stdint.h>
+
+#define usb_word_msb_get(word) \
+ (uint8_t)(((uint16_t)(word) >> 8) & 0xFF)
+#define usb_word_lsb_get(word) \
+ (uint8_t)((uint16_t)(word) & 0xFF)
+
+enum usb_bcd_version {
+ SPEC_BCD_VERSION = 0x0200,
+ DEVICE_BCD_VERSION = 0x1234,
+ HID_BCD_VERSION = 0x0111
+};
+
+enum usb_ids {
+ DEVICE_VID = 0xFFFF,
+ DEVICE_PID = 0xFFFF
+};
+
+enum usb_lang_id {
+ LANG_ID = 0x0409
+};
+
+enum usb_descriptor_string_index {
+ DESC_LANGID_STRING_INDEX = 0,
+ DESC_MFG_STRING_INDEX,
+ DESC_PRODUCT_STRING_INDEX,
+ DESC_SERIAL_STRING_INDEX
+};
+
+enum usb_descr_length {
+ // Standard length.
+ DESC_DEVICE_LEN = 18,
+ DESC_DEVICE_QUAL_LEN = 10,
+ DESC_CONF_LEN = 9,
+ DESC_OTHER_SPEED_CONF_LEN = 9,
+ DESC_INTERFACE_LEN = 9,
+ DESC_HID_LEN = 9,
+ DESCR_HID_REP_LEN = 56,
+ DESC_ENDPOINT_LEN = 7,
+
+ // Total length.
+ DESC_DEVICE_TOTAL_LEN = DESC_DEVICE_LEN,
+ DESC_DEVICE_QUAL_TOTAL_LEN = DESC_DEVICE_QUAL_LEN,
+ DESC_CONF_TOTAL_LEN = DESC_CONF_LEN + DESC_INTERFACE_LEN + DESC_HID_LEN + DESC_ENDPOINT_LEN,
+ DESC_OTHER_SPEED_CONF_TOTAL_LEN = DESC_OTHER_SPEED_CONF_LEN + DESC_INTERFACE_LEN,
+ DESCR_HID_REP_TOTAL_LEN = DESCR_HID_REP_LEN
+};
+
+enum usb_descr_attributes {
+ // Attributes (b7 - buspwr, b6 - selfpwr, b5 - rwu).
+ USB_DESC_ATTRIBUTES = 0xA0,
+ // 100 mA (div 2).
+ USB_DESC_POWER_CONSUMPTION = 50
+};
+
+enum usb_descr_numbers {
+ USB_DESC_CONFIG_COUNT = 1
+};
+
+static const uint8_t g_hid_report_desc[DESCR_HID_REP_TOTAL_LEN] = {
+ // Pad #1 configuration.
+ 0x05, 0x01, // Usage page (Generic Desktop).
+ 0x09, 0x05, // Usage (Game Pad).
+ 0xa1, 0x01, // Collection (Application).
+ 0xa1, 0x00, // Collection (Physical).
+ 0x85, HID_REPORT_ID_GAMEPAD1, // Report id (1).
+ 0x05, 0x09, // Usage page (Button).
+ 0x19, 0x01, // Usage minimum (Button 1).
+ 0x29, 0x08, // Usage maximum (Button 8).
+ 0x15, 0x00, // Logical minimum (0).
+ 0x25, 0x01, // Logical maximum (1).
+ 0x95, HID_REPORT_BITS_COUNT, // Report count (8).
+ 0x75, 0x01, // Report size (1).
+ 0x81, 0x02, // Input (Data,Var,Abs).
+ 0xc0, // End collection.
+ 0xc0, // End collection.
+ // Pad #2 configuration.
+ 0x05, 0x01, // Usage page (Generic Desktop).
+ 0x09, 0x05, // Usage (Game Pad).
+ 0xa1, 0x01, // Collection (Application).
+ 0xa1, 0x00, // Collection (Physical).
+ 0x85, HID_REPORT_ID_GAMEPAD2, // Report id (2).
+ 0x05, 0x09, // Usage page (Button).
+ 0x19, 0x01, // Usage minimum (Button 1).
+ 0x29, 0x08, // Usage maximum (Button 8).
+ 0x15, 0x00, // Logical minimum (0).
+ 0x25, 0x01, // Logical maximum (1).
+ 0x95, HID_REPORT_BITS_COUNT, // Report count (8).
+ 0x75, 0x01, // Report size (1).
+ 0x81, 0x02, // Input (Data,Var,Abs).
+ 0xc0, // End collection
+ 0xc0 // End collection.
+};
+
+static const uint8_t g_device_desc[DESC_DEVICE_TOTAL_LEN] = {
+ DESC_DEVICE_LEN, // Descriptor length.
+ DESC_DEVICE, // Descriptor type.
+ usb_word_lsb_get(SPEC_BCD_VERSION), // USB BCD version, lo.
+ usb_word_msb_get(SPEC_BCD_VERSION), // USB BCD version, hi.
+ 0x00, // Device class.
+ 0x00, // Device sub-class.
+ 0x00, // Device protocol.
+ EP0_MAX_PACKET_SIZE, // Maximum packet size (ep0 size).
+ usb_word_lsb_get(DEVICE_VID), // Vendor ID, lo.
+ usb_word_msb_get(DEVICE_VID), // Vendor ID, hi.
+ usb_word_lsb_get(DEVICE_PID), // Product ID, lo.
+ usb_word_msb_get(DEVICE_PID), // Product ID, hi.
+ usb_word_lsb_get(DEVICE_BCD_VERSION), // Device BCD version, lo.
+ usb_word_msb_get(DEVICE_BCD_VERSION), // Device BCD version, hi.
+ DESC_MFG_STRING_INDEX,
+ DESC_PRODUCT_STRING_INDEX,
+ DESC_SERIAL_STRING_INDEX,
+ USB_DESC_CONFIG_COUNT // Configurations count.
+};
+
+static const uint8_t g_device_qual_desc[DESC_DEVICE_QUAL_TOTAL_LEN] = {
+ DESC_DEVICE_QUAL_LEN, // Descriptor length.
+ DESC_DEVICE_QUAL, // Descriptor type.
+ usb_word_lsb_get(SPEC_BCD_VERSION), // USB BCD version, lo.
+ usb_word_msb_get(SPEC_BCD_VERSION), // USB BCD version, hi.
+ 0x00, // Device class.
+ 0x00, // Device sub-class.
+ 0x00, // Device protocol.
+ EP0_MAX_PACKET_SIZE, // Maximum packet size (ep0 size).
+ USB_DESC_CONFIG_COUNT, // Configurations count.
+ 0x00 // Reserved.
+};
+
+static const uint8_t g_config_desc[DESC_CONF_TOTAL_LEN] = {
+ DESC_CONF_LEN, // Descriptor length.
+ DESC_CONF, // Descriptor type.
+ usb_word_lsb_get(DESC_CONF_TOTAL_LEN), // Total length, lo.
+ usb_word_msb_get(DESC_CONF_TOTAL_LEN), // Total length, hi.
+ 0x01, // Interfaces count.
+ HID_CONFIG_NUMBER, // Configuration number.
+ 0x00, // Configuration string index.
+ USB_DESC_ATTRIBUTES, // Attributes.
+ USB_DESC_POWER_CONSUMPTION, // Power consumption.
+
+ // Interface descriptor.
+ DESC_INTERFACE_LEN, // Descriptor length.
+ DESC_INTERFACE, // Descriptor type.
+ HID_IFACE_NUMBER, // Zero-based index of this interfacce.
+ HID_ALT_IFACE_NUMBER, // Alternate setting.
+ 0x01, // End points count (ep1 in).
+ 0x03, // Class code (HID).
+ 0x00, // Subclass code (boot).
+ 0x00, // Protocol code (none).
+ 0x00, // Interface string descriptor index.
+
+ // HID descriptor.
+ DESC_HID_LEN, // Descriptor length.
+ DESC_HID, // Descriptor type.
+ usb_word_lsb_get(HID_BCD_VERSION), // HID class BCD version, lo.
+ usb_word_msb_get(HID_BCD_VERSION), // HID class BCD version, hi.
+ 0x00, // Country code (HW country code).
+ 0x01, // Number of HID class descriptors to follow.
+ DESC_REPORT, // Repord descriptor type (HID).
+ usb_word_lsb_get(DESCR_HID_REP_LEN), // Report descriptor total length, lo.
+ usb_word_msb_get(DESCR_HID_REP_LEN), // Report descriptor total length, hi.
+
+ // End point descriptor.
+ DESC_ENDPOINT_LEN, // Descriptor length.
+ DESC_ENDPOINT, // Descriptor type.
+ HID_EP_IN, // End point address (ep1 in).
+ 0x03, // End point type (interrupt).
+ usb_word_lsb_get(EP_MAX_PACKET_SIZE), // Maximum packet size, lo (ep1 size).
+ usb_word_msb_get(EP_MAX_PACKET_SIZE), // Maximum packet size, hi (ep1 size).
+ 0x01 // Polling interval (1 ms).
+};
+
+static const uint8_t g_lang_id_desc[] = {
+ 0x04, // Descriptor length.
+ DESC_STRING, // Descriptor type.
+ usb_word_lsb_get(LANG_ID), // Language id, lo.
+ usb_word_msb_get(LANG_ID) // Language id, hi.
+};
+
+static const uint8_t g_manuf_str_desc[] = {
+ 0x18, // Descriptor length.
+ DESC_STRING, // Descriptor type.
+ // Unicode string:
+ 'Q', 0, 'B', 0, 'S', 0, ' ', 0,
+ 'e', 0, 'x', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0
+};
+
+static const uint8_t g_product_str_desc[] = {
+ 0x1A, // Descriptor length.
+ DESC_STRING, // Descriptor type.
+ // Unicode string:
+ 'N', 0, 'E', 0, 'S', 0, ' ', 0,
+ 'G', 0, 'a', 0, 'm', 0, 'e', 0,
+ 'P', 0, 'a', 0, 'd', 0, 's', 0
+};
+
+static const uint8_t g_serialno_str_desc[] = {
+ 0x1A, // Descriptor length.
+ DESC_STRING, // Descriptor type.
+ // Unicode string:
+ '0', 0 ,'1', 0, '0', 0, '2', 0, '0', 0, '3', 0,
+ '0', 0, '4', 0, '0', 0, '5', 0, '0', 0, '6', 0
+};
+
+static const uint8_t *ep0_string_desc_get(uint16_t *length)
+{
+ const uint8_t index = hid_ep0_setup_lvalue_get();
+ switch (index) {
+ case DESC_LANGID_STRING_INDEX:
+ *length = sizeof(g_lang_id_desc);
+ return g_lang_id_desc;
+ case DESC_MFG_STRING_INDEX:
+ *length = sizeof(g_manuf_str_desc);
+ return g_manuf_str_desc;
+ case DESC_PRODUCT_STRING_INDEX:
+ *length = sizeof(g_product_str_desc);
+ return g_product_str_desc;
+ case DESC_SERIAL_STRING_INDEX:
+ *length = sizeof(g_serialno_str_desc);
+ return g_serialno_str_desc;
+ default:
+ break;
+ }
+ return 0;
+}
+
+const uint8_t *hid_ep0_desc_get(uint8_t type, uint16_t *length)
+{
+ switch (type) {
+ case DESC_DEVICE:
+ *length = DESC_DEVICE_TOTAL_LEN;
+ return g_device_desc;
+ case DESC_CONF:
+ *length = DESC_CONF_TOTAL_LEN;
+ return g_config_desc;
+ case DESC_STRING:
+ return ep0_string_desc_get(length);
+ case DESC_DEVICE_QUAL:
+ *length = DESC_DEVICE_QUAL_TOTAL_LEN;
+ return g_device_qual_desc;
+ case DESC_HID:
+ *length = DESC_HID_LEN;
+ return &g_config_desc[DESC_CONF_LEN + DESC_INTERFACE_LEN];
+ case DESC_REPORT:
+ *length = DESCR_HID_REP_TOTAL_LEN;
+ return g_hid_report_desc;
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/hidep0.c b/examples/baremetal/msp430f5529/nes-gamepads/hidep0.c
new file mode 100644
index 000000000..c4a267a59
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/hidep0.c
@@ -0,0 +1,554 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hid.h"
+#include "hwdefs.h"
+#include "usb.h"
+
+enum usb_ep0_action {
+ STATUS_ACTION_NOTHING,
+ STATUS_ACTION_DATA_IN
+};
+
+struct usb_ep0 {
+ uint8_t IEPCNFG; // Input ep0 configuration register.
+ uint8_t IEPBCNT; // Input ep0 buffer byte count.
+ uint8_t OEPCNFG; // Output ep0 configuration register.
+ uint8_t OEPBCNT; // Output ep0 buffer byte count.
+};
+
+struct usb_ep0_data {
+ uint8_t data[EP_MAX_FIFO_SIZE];
+ uint16_t length;
+ uint16_t offset;
+ enum usb_ep0_action action;
+ bool host_ask_more_data_than_available;
+};
+
+#if defined(__ICC430__)
+#pragma location = 0x0920 // Input ep0 configuration address.
+__no_init struct usb_ep0 __data16 g_ep0;
+#pragma location = 0x2378 // Input ep0 buffer address.
+__no_init uint8_t __data16 g_ep0_in_buf[EP0_MAX_PACKET_SIZE];
+#pragma location = 0x2380 // Setup packet block address.
+__no_init uint8_t __data16 g_setupdat[EP0_MAX_PACKET_SIZE];
+#elif defined(__GNUC__)
+extern struct usb_ep0 g_ep0;
+extern uint8_t g_ep0_in_buf[EP0_MAX_PACKET_SIZE];
+extern uint8_t g_setupdat[EP0_MAX_PACKET_SIZE];
+#endif
+
+static volatile bool g_enumerated = false;
+static bool g_rwuen = false;
+static bool g_selfpwr = false;
+static struct usb_ep0_data g_ep0_response;
+
+static bool ep0_in_response_create(const uint8_t *data, uint16_t length)
+{
+ if (sizeof(g_ep0_response.data) < length)
+ return false;
+
+ for (uint16_t i = 0; i < length; ++i)
+ g_ep0_response.data[i] = data[i];
+
+ g_ep0_response.length = length;
+ g_ep0_response.offset = 0;
+
+ const uint16_t setup_length = (g_setupdat[7] << 8) | g_setupdat[6];
+ if (g_ep0_response.length >= setup_length) {
+ g_ep0_response.length = setup_length;
+ g_ep0_response.host_ask_more_data_than_available = false;
+ } else {
+ g_ep0_response.host_ask_more_data_than_available = true;
+ }
+
+ return true;
+}
+
+void ep0_in_frame_send(void)
+{
+ uint8_t frame_size = 0;
+
+ if (g_ep0_response.length != EP_NO_MORE_DATA) {
+ if (g_ep0_response.length > EP0_MAX_PACKET_SIZE) {
+ frame_size = EP0_MAX_PACKET_SIZE;
+ g_ep0_response.length -= EP0_MAX_PACKET_SIZE;
+ g_ep0_response.action = STATUS_ACTION_DATA_IN;
+ } else if (g_ep0_response.length < EP0_MAX_PACKET_SIZE) {
+ frame_size = g_ep0_response.length;
+ g_ep0_response.length = EP_NO_MORE_DATA;
+ g_ep0_response.action = STATUS_ACTION_NOTHING;
+ } else {
+ frame_size = EP0_MAX_PACKET_SIZE;
+ if (g_ep0_response.host_ask_more_data_than_available) {
+ g_ep0_response.length = 0;
+ g_ep0_response.action = STATUS_ACTION_DATA_IN;
+ } else {
+ g_ep0_response.length = EP_NO_MORE_DATA;
+ g_ep0_response.action = STATUS_ACTION_NOTHING;
+ }
+ }
+
+ for (uint8_t i = 0; i < frame_size; ++i) {
+ g_ep0_in_buf[i] = g_ep0_response.data[g_ep0_response.offset];
+ ++g_ep0_response.offset;
+ }
+
+ g_ep0.IEPBCNT = frame_size;
+ } else {
+ g_ep0_response.action = STATUS_ACTION_NOTHING;
+ }
+}
+
+static bool ep0_dev_status_get(void)
+{
+ uint16_t status = 0;
+ if (g_selfpwr)
+ status |= STATUS_SELF_POWERED;
+ if (g_rwuen)
+ status |= STATUS_REMOTE_WAKEUP;
+ ep0_in_response_create((uint8_t *)&status, sizeof(status));
+ return true;
+}
+
+static bool ep0_iface_status_get(void)
+{
+ uint16_t status = 0;
+ ep0_in_response_create((uint8_t *)&status, sizeof(status));
+ return true;
+}
+
+static bool ep0_ep_status_get(void)
+{
+ uint16_t status = 0;
+ const uint8_t ep = hid_ep0_setup_lindex_get();
+ const uint8_t ep_num = ep & ~SETUP_DIR;
+ if (ep_num == 0) {
+ status = hid_ep0_in_is_stalled() ? 1 : 0;
+ } else if ((ep_num == 1) && (ep & SETUP_DIR) == SETUP_INPUT) {
+ // We support only one input ep1 in.
+ status = hid_ep1_in_is_stalled() ? 1 : 0;
+ } else {
+ return false;
+ }
+
+ ep0_in_response_create((uint8_t *)&status, sizeof(status));
+ return true;
+}
+
+static bool ep0_get_status_proc(void)
+{
+ const uint8_t bm_req_type = hid_ep0_setup_bm_request_type_get();
+ if ((bm_req_type & SETUP_DIR) != SETUP_INPUT)
+ return false;
+ const uint8_t recipient = bm_req_type & SETUP_RECIPIENT;
+ switch (recipient) {
+ case SETUP_DEVICE:
+ return ep0_dev_status_get();
+ case SETUP_IFACE:
+ return ep0_iface_status_get();
+ case SETUP_EP:
+ return ep0_ep_status_get();
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool ep0_dev_feature_clear(void)
+{
+ const uint8_t feature = hid_ep0_setup_lvalue_get();
+ if (feature != FEATURE_REMOTE_WAKEUP)
+ return false;
+ g_rwuen = false;
+ return true;
+}
+
+static bool ep0_ep_feature_clear(void)
+{
+ const uint8_t feature = hid_ep0_setup_lvalue_get();
+ if (feature != FEATURE_STALL)
+ return false;
+ const uint8_t ep = hid_ep0_setup_lindex_get();
+ const uint8_t ep_num = ep & ~SETUP_DIR;
+ if (ep_num == 0) {
+ // Do nothing.
+ } else if ((ep_num == 1) && (ep & SETUP_DIR) == SETUP_INPUT) {
+ // We support only one input ep1 in.
+ hid_ep1_in_unstall();
+ } else {
+ return false;
+ }
+
+ hid_ep0_in_clear();
+ return true;
+}
+
+static bool ep0_clear_feature_proc(void)
+{
+ const uint8_t bm_req_type = hid_ep0_setup_bm_request_type_get();
+ if ((bm_req_type & SETUP_DIR) != SETUP_INPUT)
+ return false;
+ const uint8_t recipient = bm_req_type & SETUP_RECIPIENT;
+ switch (recipient) {
+ case SETUP_DEVICE:
+ return ep0_dev_feature_clear();
+ case SETUP_EP:
+ return ep0_ep_feature_clear();
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool ep0_dev_feature_set(void)
+{
+ const uint8_t feature = hid_ep0_setup_lvalue_get();
+ switch (feature) {
+ case FEATURE_REMOTE_WAKEUP:
+ g_rwuen = true;
+ return true;
+ case FEATURE_TEST_MODE:
+ // This is "test mode", just return the handshake.
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool ep0_ep_feature_set(void)
+{
+ const uint8_t feature = hid_ep0_setup_lvalue_get();
+ if (feature != FEATURE_STALL)
+ return false;
+ const uint8_t ep = hid_ep0_setup_lindex_get();
+ const uint8_t ep_num = ep & ~SETUP_DIR;
+ if (ep_num == 0) {
+ // Do nothing.
+ } else if ((ep_num == 1) && (ep & SETUP_DIR) == SETUP_INPUT) {
+ // We support only one input ep1 in.
+ hid_ep1_in_stall();
+ } else {
+ return false;
+ }
+ hid_ep0_in_clear();
+ return true;
+}
+
+static bool ep0_set_feature_proc(void)
+{
+ const uint8_t bm_req_type = hid_ep0_setup_bm_request_type_get();
+ switch (bm_req_type & SETUP_RECIPIENT) {
+ case SETUP_DEVICE:
+ return ep0_dev_feature_set();
+ case SETUP_EP:
+ return ep0_ep_feature_set();
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool ep0_set_address_proc(void)
+{
+ hid_ep0_out_stall();
+ const uint8_t address = hid_ep0_setup_lvalue_get();
+ if (address >= 128)
+ return false;
+ USBFUNADR = address;
+ hid_ep0_in_clear();
+ return true;
+}
+
+static bool ep0_descriptor_proc(uint8_t type)
+{
+ uint16_t length = 0;
+ const uint8_t *pdesc = hid_ep0_desc_get(type, &length);
+ if (!pdesc)
+ return false;
+ ep0_in_response_create(pdesc, length);
+ return true;
+}
+
+static bool ep0_get_descriptor_proc(void)
+{
+ const uint8_t descr_type = hid_ep0_setup_hvalue_get();
+ switch (descr_type) {
+ case DESC_DEVICE:
+ case DESC_CONF:
+ case DESC_STRING:
+ case DESC_DEVICE_QUAL:
+ case DESC_OTHER_SPEED_CONF:
+ case DESC_HID:
+ case DESC_REPORT:
+ return ep0_descriptor_proc(descr_type);
+ }
+ return false;
+}
+
+static bool ep0_get_config_proc(void)
+{
+ // We only support configuration 1.
+ const uint8_t cfg_num = HID_CONFIG_NUMBER;
+ ep0_in_response_create(&cfg_num, sizeof(cfg_num));
+ return true;
+}
+
+static bool ep0_set_config_proc(void)
+{
+ const uint8_t cfg_num = hid_ep0_setup_lvalue_get();
+ // We only support configuration 1.
+ const bool is_valid = (cfg_num & HID_CONFIG_NUMBER);
+ hid_ep0_enumerated_set(is_valid);
+ return is_valid;
+}
+
+static bool ep0_get_iface_proc(void)
+{
+ const uint8_t iface_num = hid_ep0_setup_lindex_get();
+ if (iface_num != HID_IFACE_NUMBER)
+ return false;
+ ep0_in_response_create(&iface_num, sizeof(iface_num));
+ return true;
+}
+
+static bool ep0_set_iface_proc(void)
+{
+ const uint8_t iface_num = hid_ep0_setup_lindex_get();
+ if (iface_num != HID_IFACE_NUMBER)
+ return false;
+
+ const uint8_t alt_iface_num = hid_ep0_setup_lvalue_get();
+ if (alt_iface_num != HID_ALT_IFACE_NUMBER)
+ return false;
+
+ hid_ep0_out_stall();
+ hid_ep0_in_clear();
+ return true;
+}
+
+static bool ep0_std_proc(void)
+{
+ const uint8_t request_code = hid_ep0_setup_request_get();
+ switch (request_code) {
+ case SETUP_GET_STATUS:
+ return ep0_get_status_proc();
+ case SETUP_CLEAR_FEATURE:
+ return ep0_clear_feature_proc();
+ case SETUP_SET_FEATURE:
+ return ep0_set_feature_proc();
+ case SETUP_SET_ADDRESS:
+ return ep0_set_address_proc();
+ case SETUP_GET_DESCRIPTOR:
+ return ep0_get_descriptor_proc();
+ case SETUP_GET_CONFIGURATION:
+ return ep0_get_config_proc();
+ case SETUP_SET_CONFIGURATION:
+ return ep0_set_config_proc();
+ case SETUP_GET_INTERFACE:
+ return ep0_get_iface_proc();
+ case SETUP_SET_INTERFACE:
+ return ep0_set_iface_proc();
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool ep0_setup(void)
+{
+ const uint8_t bm_req_type = hid_ep0_setup_bm_request_type_get();
+ const uint8_t setup_type = bm_req_type & SETUP_TYPE;
+ switch (setup_type) {
+ case SETUP_STANDARD:
+ return ep0_std_proc();
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool ep0_has_another_setup(void)
+{
+ if ((USBIFG & STPOWIFG) != 0) {
+ USBIFG &= ~(STPOWIFG | SETUPIFG);
+ return true;
+ }
+ return false;
+}
+
+void hid_ep0_init(void)
+{
+ hid_ep0_in_nak();
+ hid_ep0_out_nak();
+
+ g_ep0.IEPCNFG = UBME | STALL | USBIIE;
+ g_ep0.OEPCNFG = UBME | STALL | USBIIE;
+
+ // Enable only ep0in interrupts.
+ USBIEPIE |= BIT0;
+}
+
+void hid_ep0_setup_handler(void)
+{
+ USBCTL |= FRSTE;
+
+ g_ep0_response.length = 0;
+ g_ep0_response.offset = 0;
+ g_ep0_response.action = STATUS_ACTION_NOTHING;
+ g_ep0_response.host_ask_more_data_than_available = false;
+
+ for (;;) {
+ const uint8_t bm_req_type = hid_ep0_setup_bm_request_type_get();
+ if ((bm_req_type & SETUP_INPUT) == SETUP_INPUT)
+ USBCTL |= DIR;
+ else
+ USBCTL &= ~DIR;
+
+ const bool success = ep0_setup();
+ if (success) {
+ hid_ep0_out_clear();
+ ep0_in_frame_send();
+ } else {
+ hid_ep0_in_stall();
+ }
+
+ if (!ep0_has_another_setup())
+ return;
+ }
+}
+
+void hid_ep0_in_handler(void)
+{
+ USBCTL |= FRSTE;
+ hid_ep0_out_clear();
+ if (g_ep0_response.action == STATUS_ACTION_DATA_IN)
+ ep0_in_frame_send();
+ else
+ hid_ep0_in_stall();
+}
+
+void hid_ep0_in_nak(void)
+{
+ g_ep0.IEPBCNT = NAK;
+}
+
+void hid_ep0_in_stall(void)
+{
+ g_ep0.IEPCNFG |= STALL;
+}
+
+void hid_ep0_in_clear(void)
+{
+ g_ep0_response.length = EP_NO_MORE_DATA;
+ g_ep0_response.action = STATUS_ACTION_NOTHING;
+ g_ep0.IEPBCNT = 0;
+}
+
+bool hid_ep0_in_is_stalled(void)
+{
+ return (g_ep0.IEPCNFG & STALL);
+}
+
+void hid_ep0_out_nak(void)
+{
+ g_ep0.OEPBCNT = NAK;
+}
+
+void hid_ep0_out_stall(void)
+{
+ g_ep0.OEPCNFG |= STALL;
+}
+
+void hid_ep0_out_clear(void)
+{
+ g_ep0.OEPBCNT = 0;
+}
+
+uint8_t hid_ep0_setup_bm_request_type_get(void)
+{
+ return g_setupdat[0];
+}
+
+uint8_t hid_ep0_setup_request_get(void)
+{
+ return g_setupdat[1];
+}
+
+uint8_t hid_ep0_setup_lvalue_get(void)
+{
+ return g_setupdat[2];
+}
+
+uint8_t hid_ep0_setup_hvalue_get(void)
+{
+ return g_setupdat[3];
+}
+
+uint8_t hid_ep0_setup_lindex_get(void)
+{
+ return g_setupdat[4];
+}
+
+uint8_t hid_ep0_setup_hindex_get(void)
+{
+ return g_setupdat[5];
+}
+
+void hid_ep0_enumerated_set(bool enumerated)
+{
+ g_enumerated = enumerated;
+}
+
+bool hid_ep0_is_enumerated(void)
+{
+ return g_enumerated;
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/hidep1.c b/examples/baremetal/msp430f5529/nes-gamepads/hidep1.c
new file mode 100644
index 000000000..2d29490ec
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/hidep1.c
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gpio.h"
+#include "hid.h"
+#include "hwdefs.h"
+#include "usb.h"
+
+// We use this empirical value to make a GPIO polling
+// every ~10 msec (rough). It is a simplest way, because
+// instead we need to use a timers with interrupts callbacks.
+enum { POLLING_COUNTER = 888 };
+
+// Pins on the port 6.
+enum gpio_pins {
+ GPIO_CLK_PIN = BIT0,
+ GPIO_DATA1_PIN = BIT1,
+ GPIO_DATA2_PIN = BIT2,
+ GPIO_LATCH_PIN = BIT3,
+};
+
+struct usb_ep {
+ uint8_t EPCNF; // Endpoint configuration.
+ uint8_t EPBBAX; // Endpoint X buffer base address.
+ uint8_t EPBCTX; // Endpoint X Buffer byte count.
+ uint8_t SPARE0; // No used.
+ uint8_t SPARE1; // No used.
+ uint8_t EPBBAY; // Endpoint Y buffer Base address.
+ uint8_t EPBCTY; // Endpoint Y buffer byte count.
+ uint8_t EPSIZXY; // Endpoint XY buffer size.
+};
+
+#if defined(__ICC430__)
+#pragma location = 0x23C8 // Input ep1 configuration address.
+__no_init struct usb_ep __data16 g_ep1_in;
+
+#pragma location = 0x1C80 // Input ep1 X-buffer address.
+__no_init uint8_t __data16 g_ep1_in_xbuf[EP_MAX_PACKET_SIZE];
+#elif defined(__GNUC__)
+extern struct usb_ep g_ep1_in;
+extern uint8_t g_ep1_in_xbuf[EP_MAX_PACKET_SIZE];
+#endif
+
+static struct {
+ const uint8_t data_pin;
+ const uint8_t id;
+ uint8_t buttons;
+ bool ready;
+} g_reports[HID_REPORTS_COUNT] = {
+ {GPIO_DATA1_PIN, HID_REPORT_ID_GAMEPAD1, 0, false},
+ {GPIO_DATA2_PIN, HID_REPORT_ID_GAMEPAD2, 0, false}
+};
+
+static uint16_t g_poll_counter = 0;
+
+// Pulse width around ~10 usec.
+static void ep1_latch_pulse(void)
+{
+ gpio_pins_set_high(GPIO_PORT_P6, GPIO_LATCH_PIN);
+ gpio_pins_set_low(GPIO_PORT_P6, GPIO_LATCH_PIN);
+}
+
+// Pulse width around ~10 usec.
+static void ep1_clk_pulse(void)
+{
+ gpio_pins_set_high(GPIO_PORT_P6, GPIO_CLK_PIN);
+ gpio_pins_set_low(GPIO_PORT_P6, GPIO_CLK_PIN);
+}
+
+static void ep1_reports_clean(void)
+{
+ for (uint8_t index = 0; index < HID_REPORTS_COUNT; ++index) {
+ g_reports[index].buttons = 0;
+ g_reports[index].ready = false;
+ }
+}
+
+static void ep1_reports_update(void)
+{
+ for (uint8_t index = 0; index < HID_REPORTS_COUNT; ++index)
+ g_reports[index].ready = true;
+}
+
+static void ep1_gamepads_poll(void)
+{
+ ep1_reports_clean();
+
+ ep1_latch_pulse();
+
+ for (uint8_t pos = 0; pos < HID_REPORT_BITS_COUNT; ++pos) {
+ // TODO: Add some nops here?
+
+ for (uint8_t index = 0; index < HID_REPORTS_COUNT; ++index) {
+ const uint8_t pin = g_reports[index].data_pin;
+ const enum gpio_pin_status st = gpio_pin_get(GPIO_PORT_P6, pin);
+ // Low state means that a button is pressed.
+ const bool v = (st == GPIO_INPUT_PIN_LOW);
+ g_reports[index].buttons |= (v << pos);
+ }
+
+ ep1_clk_pulse();
+ }
+
+ ep1_reports_update();
+}
+
+static void ep1_report_send(uint8_t report_index)
+{
+ if (!g_reports[report_index].ready)
+ return;
+
+ if ((g_ep1_in.EPBCTX & NAK) == 0)
+ return;
+
+ g_ep1_in_xbuf[0] = g_reports[report_index].id;
+ g_ep1_in_xbuf[1] = g_reports[report_index].buttons;
+ g_ep1_in.EPBCTX = 2;
+
+ g_reports[report_index].ready = false;
+}
+
+void hid_ep1_init(void)
+{
+ enum {
+ USBSTABUFF_ADDRESS = 0x1C00, // Start of buffer space address.
+ USBIEPBBAX_1_ADDRESS = 0x1C80 // Input ep1 X-buffer address.
+ };
+
+ g_ep1_in.EPCNF = UBME | USBIIE;
+ g_ep1_in.EPBBAX = ((USBIEPBBAX_1_ADDRESS - USBSTABUFF_ADDRESS) >> 3) & 0x00FF;
+ g_ep1_in.EPBCTX = NAK;
+ g_ep1_in.EPSIZXY = EP_MAX_PACKET_SIZE;
+
+ gpio_pins_set_as_out(GPIO_PORT_P6, GPIO_CLK_PIN | GPIO_LATCH_PIN);
+ gpio_pins_set_as_in(GPIO_PORT_P6, GPIO_DATA1_PIN | GPIO_DATA2_PIN);
+}
+
+void hid_ep1_task(void)
+{
+ if (!hid_ep0_is_enumerated())
+ return;
+
+ for (uint8_t index = 0; index < HID_REPORTS_COUNT; ++index)
+ ep1_report_send(index);
+
+ if (g_poll_counter <= POLLING_COUNTER) {
+ ++g_poll_counter;
+ } else {
+ g_poll_counter = 0;
+ ep1_gamepads_poll();
+ }
+}
+
+void hid_ep1_in_stall(void)
+{
+ g_ep1_in.EPCNF |= STALL;
+}
+
+void hid_ep1_in_unstall(void)
+{
+ g_ep1_in.EPCNF &= ~(STALL | TOGGLE);
+}
+
+bool hid_ep1_in_is_stalled(void)
+{
+ return (g_ep1_in.EPCNF & STALL);
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/hwdefs.h b/examples/baremetal/msp430f5529/nes-gamepads/hwdefs.h
new file mode 100644
index 000000000..89dbcf5af
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/hwdefs.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_HW_DEFS_H
+#define MSP430_HW_DEFS_H
+
+#include <msp430.h>
+#include <msp430f5xx_6xxgeneric.h>
+
+#if defined(__ICC430__)
+# define _PPTOSTR_(x) #x
+# define _PPARAM_(address) _PPTOSTR_(vector=address)
+# define INTERRUPT(isr_name, vector) \
+ _Pragma(_PPARAM_(vector)) __interrupt void isr_name(void)
+#elif defined(__GNUC__)
+# define INTERRUPT(isr_name, vector) \
+ void __attribute__ ((interrupt(vector))) isr_name(void)
+#else
+# error "Unsupported toolchain"
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define HWREG8(x) \
+ (*((volatile uint8_t *)((uint16_t)x)))
+
+#define HWREG16(x) \
+ (*((volatile uint16_t *)((uint16_t)x)))
+
+#define HWREG32(x) \
+ (*((volatile uint32_t *)((uint16_t)x)))
+
+#endif // MSP430_HW_DEFS_H
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/main.c b/examples/baremetal/msp430f5529/nes-gamepads/main.c
new file mode 100644
index 000000000..97c499eef
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/main.c
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hwdefs.h"
+#include "pmm.h"
+#include "ucs.h"
+#include "usb.h"
+#include "wdt_a.h"
+
+static void hw_clocks_init(uint32_t mclk_freq)
+{
+ ucs_init(UCS_FLLREF, UCS_REFOCLK_SELECT, UCS_CLOCK_DIVIDER_1);
+ ucs_init(UCS_ACLK, UCS_REFOCLK_SELECT, UCS_CLOCK_DIVIDER_1);
+ ucs_fll_settle_init(mclk_freq / 1000, mclk_freq / 32768);
+
+ // Use REFO for FLL and ACLK.
+ UCSCTL3 = (UCSCTL3 & ~SELREF_7) | SELREF__REFOCLK;
+ UCSCTL4 = (UCSCTL4 & ~SELA_7) | SELA__REFOCLK;
+}
+
+static void hw_init(void)
+{
+ __disable_interrupt();
+
+ wdt_a_stop();
+ pmm_voltage_init(PMM_VOLTAGE_1_85V);
+ hw_clocks_init(8000000);
+ usb_init();
+
+ __enable_interrupt();
+}
+
+static void hw_loop_exec(void)
+{
+ for (;;) {
+ usb_task();
+ }
+}
+
+int main(void)
+{
+ hw_init();
+ hw_loop_exec();
+ return 0;
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs b/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs
new file mode 100644
index 000000000..37dfe8ca6
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import qbs
+
+CppApplication {
+ condition: {
+ if (!qbs.architecture.contains("msp430"))
+ return false;
+ return qbs.toolchain.contains("iar")
+ || qbs.toolchain.contains("gcc")
+ }
+ name: "msp430f5529-nes-gamepads"
+ cpp.cLanguageVersion: "c99"
+ cpp.positionIndependentCode: false
+
+ //
+ // IAR-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("iar")
+
+ property path dlibIncludePath: cpp.toolchainInstallPath + "/../lib/dlib/dl430xssfn.h"
+ property path dlibRuntimePath: cpp.toolchainInstallPath + "/../lib/dlib/dl430xssfn.r43"
+
+ cpp.entryPoint: "__program_start"
+
+ cpp.defines: ["__MSP430F5529__"]
+
+ cpp.driverFlags: [
+ "-e",
+ "--core=430X",
+ "--data_model=small",
+ "--code_model=small",
+ "--dlib_config", dlibIncludePath
+ ]
+
+ cpp.driverLinkerFlags: [
+ "-D_STACK_SIZE=A0",
+ "-D_DATA16_HEAP_SIZE=A0",
+ "-D_DATA20_HEAP_SIZE=50"
+ ]
+
+ cpp.staticLibraries: [
+ // Explicitly link with the runtime dlib library (which contains
+ // all required startup code and other stuff).
+ dlibRuntimePath
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("iar")
+ name: "IAR Linker Script"
+ prefix: cpp.toolchainInstallPath + "/../config/linker/"
+ fileTags: ["linkerscript"]
+ // Explicitly use the default linker scripts for current target.
+ files: ["lnk430f5529.xcl"]
+ }
+
+ //
+ // GCC-specific properties and soucres.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ property path supportFilesPath
+ // A path to the MSP430 support files, which are
+ // provided by the Texas Instruments separately:
+ // e.g. 'c:/msp430-gcc-support-files/include/'
+ cpp.includePaths: supportFilesPath
+ cpp.libraryPaths: supportFilesPath
+ cpp.driverFlags: ["-mmcu=msp430f5529"]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("gcc")
+ name: "GCC Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["gamepads.ld"]
+ }
+
+ files: [
+ "gpio.c",
+ "gpio.h",
+ "hid.h",
+ "hiddesc.c",
+ "hidep0.c",
+ "hidep1.c",
+ "hwdefs.h",
+ "main.c",
+ "pmm.c",
+ "pmm.h",
+ "ucs.c",
+ "ucs.h",
+ "usb.c",
+ "usb.h",
+ "wdt_a.c",
+ "wdt_a.h",
+ ]
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/pmm.c b/examples/baremetal/msp430f5529/nes-gamepads/pmm.c
new file mode 100644
index 000000000..b588e7ad3
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/pmm.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hwdefs.h"
+#include "pmm.h"
+
+#define PMMCTL0_H_ADDRESS (PMM_BASE + OFS_PMMCTL0_H)
+#define PMMCTL0_L_ADDRESS (PMM_BASE + OFS_PMMCTL0_L)
+#define PMMRIE_ADDRESS (PMM_BASE + OFS_PMMRIE)
+#define PMMIFG_ADDRESS (PMM_BASE + OFS_PMMIFG)
+#define SVSMHCTL_ADDRESS (PMM_BASE + OFS_SVSMHCTL)
+#define SVSMLCTL_ADDRESS (PMM_BASE + OFS_SVSMLCTL)
+
+#define PMM_OTHER_BITSL \
+ (SVSLRVL0 | SVSLRVL1 | SVSMLRRL0 | SVSMLRRL1 | SVSMLRRL2)
+
+#define PMM_OTHER_BITSH \
+ (SVSHRVL0 | SVSHRVL1 | SVSMHRRL0 | SVSMHRRL1 | SVSMHRRL2)
+
+#define PMM_CLEANUP_FLAGS \
+ (SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG | SVMLVLRIFG | SVMLIFG | SVSMLDLYIFG)
+
+static void pmm_write_access_allow(bool allow)
+{
+ HWREG8(PMMCTL0_H_ADDRESS) = allow ? PMMPW_H : 0;
+}
+
+static void pmm_interrupt_flags_clear(uint16_t flags)
+{
+ HWREG16(PMMIFG_ADDRESS) &= ~flags;
+}
+
+static void pmm_interrupt_flags_wait(uint16_t flags)
+{
+ while ((HWREG16(PMMIFG_ADDRESS) & flags) == 0) {
+ ;
+ }
+}
+
+static uint8_t pmm_level_mask(enum ppm_voltage voltage)
+{
+ switch (voltage) {
+ case PMM_VOLTAGE_1_35V:
+ return PMMCOREV_0;
+ case PMM_VOLTAGE_1_55V:
+ return PMMCOREV_1;
+ case PMM_VOLTAGE_1_75V:
+ return PMMCOREV_2;
+ case PMM_VOLTAGE_1_85V:
+ return PMMCOREV_3;
+ default:
+ break;
+ }
+ return PMMCOREV_0;
+}
+
+static bool pmm_voltage_set_up(uint8_t level)
+{
+ pmm_write_access_allow(true);
+
+ // Disable interrupts and backup all registers.
+ uint16_t pmmrie_backup = HWREG16(PMMRIE_ADDRESS);
+ HWREG16(PMMRIE_ADDRESS) &= ~(SVMHVLRPE | SVSHPE | SVMLVLRPE | SVSLPE
+ | SVMHVLRIE | SVMHIE | SVSMHDLYIE | SVMLVLRIE
+ | SVMLIE | SVSMLDLYIE);
+ uint16_t svsmhctl_backup = HWREG16(SVSMHCTL_ADDRESS);
+ uint16_t svsmlctl_backup = HWREG16(SVSMLCTL_ADDRESS);
+
+ // Clear all interrupt flags.
+ pmm_interrupt_flags_clear(0xFFFF);
+
+ // Set SVM high side to new level and check if voltage increase is possible.
+ HWREG16(SVSMHCTL_ADDRESS) = SVMHE | SVSHE | (SVSMHRRL0 * level);
+ pmm_interrupt_flags_wait(SVSMHDLYIFG);
+ pmm_interrupt_flags_clear(SVSMHDLYIFG);
+
+ // Check if a voltage increase is possible.
+ if ((HWREG16(PMMIFG_ADDRESS) & SVMHIFG) == SVMHIFG) {
+ // Vcc is too low for a voltage increase, recover the previous settings.
+ HWREG16(PMMIFG_ADDRESS) &= ~SVSMHDLYIFG;
+ HWREG16(SVSMHCTL_ADDRESS) = svsmhctl_backup;
+ pmm_interrupt_flags_wait(SVSMHDLYIFG);
+ pmm_interrupt_flags_clear(PMM_CLEANUP_FLAGS);
+ // Restore interrupt enable register.
+ HWREG16(PMMRIE_ADDRESS) = pmmrie_backup;
+ pmm_write_access_allow(false);
+ return false;
+ }
+
+ // Set SVS high side to new level.
+ HWREG16(SVSMHCTL_ADDRESS) |= (SVSHRVL0 * level);
+ pmm_interrupt_flags_wait(SVSMHDLYIFG);
+ pmm_interrupt_flags_clear(SVSMHDLYIFG);
+
+ // Set new voltage level.
+ HWREG8(PMMCTL0_L_ADDRESS) = PMMCOREV0 * level;
+
+ // Set SVM, SVS low side to new level.
+ HWREG16(SVSMLCTL_ADDRESS) = SVMLE | (SVSMLRRL0 * level)
+ | SVSLE | (SVSLRVL0 * level);
+ pmm_interrupt_flags_wait(SVSMLDLYIFG);
+ pmm_interrupt_flags_clear(SVSMLDLYIFG);
+
+ // Restore low side settings and clear all other bits.
+ HWREG16(SVSMLCTL_ADDRESS) &= PMM_OTHER_BITSL;
+ // Clear low side level settings in backup register and keep all other bits.
+ svsmlctl_backup &= ~PMM_OTHER_BITSL;
+ // Restore low side SVS monitor settings.
+ HWREG16(SVSMLCTL_ADDRESS) |= svsmlctl_backup;
+
+ // Restore high side settings and clear all other bits.
+ HWREG16(SVSMHCTL_ADDRESS) &= PMM_OTHER_BITSH;
+ // Clear high side level settings in backup register and keep all other bits.
+ svsmhctl_backup &= ~PMM_OTHER_BITSH;
+ // Restore high side SVS monitor settings.
+ HWREG16(SVSMHCTL_ADDRESS) |= svsmhctl_backup;
+
+ pmm_interrupt_flags_wait(SVSMLDLYIFG | SVSMHDLYIFG);
+ pmm_interrupt_flags_clear(PMM_CLEANUP_FLAGS);
+
+ // Restore interrupt enable register.
+ HWREG16(PMMRIE_ADDRESS) = pmmrie_backup;
+ pmm_write_access_allow(false);
+
+ return true;
+}
+
+static uint16_t pmm_voltage_set_down(uint8_t level)
+{
+ pmm_write_access_allow(true);
+
+ // Disable interrupts and backup all registers.
+ uint16_t pmmrie_backup = HWREG16(PMMRIE_ADDRESS);
+ HWREG16(PMMRIE_ADDRESS) &= ~(SVMHVLRPE | SVSHPE | SVMLVLRPE |
+ SVSLPE | SVMHVLRIE | SVMHIE |
+ SVSMHDLYIE | SVMLVLRIE | SVMLIE |
+ SVSMLDLYIE
+ );
+ uint16_t svsmhctl_backup = HWREG16(SVSMHCTL_ADDRESS);
+ uint16_t svsmlctl_backup = HWREG16(SVSMLCTL_ADDRESS);
+
+ pmm_interrupt_flags_clear(SVMHIFG | SVSMHDLYIFG | SVMLIFG | SVSMLDLYIFG);
+
+ // Set SVM, SVS high & low side to new settings in normal mode.
+ HWREG16(SVSMHCTL_ADDRESS) = SVMHE | (SVSMHRRL0 * level) | SVSHE | (SVSHRVL0 * level);
+ HWREG16(SVSMLCTL_ADDRESS) = SVMLE | (SVSMLRRL0 * level) | SVSLE | (SVSLRVL0 * level);
+
+ pmm_interrupt_flags_wait(SVSMHDLYIFG | SVSMLDLYIFG);
+ pmm_interrupt_flags_clear(SVSMHDLYIFG | SVSMLDLYIFG);
+
+ // Set new voltage level.
+ HWREG8(PMMCTL0_L_ADDRESS) = PMMCOREV0 * level;
+
+ // Restore low side settings and clear all other bits.
+ HWREG16(SVSMLCTL_ADDRESS) &= PMM_OTHER_BITSL;
+ // Clear low side level settings in backup register and keep all other bits.
+ svsmlctl_backup &= ~PMM_OTHER_BITSL;
+ //Restore low side SVS monitor settings.
+ HWREG16(SVSMLCTL_ADDRESS) |= svsmlctl_backup;
+
+ // Restore high side settings and clear all other bits.
+ HWREG16(SVSMHCTL_ADDRESS) &= PMM_OTHER_BITSH;
+ // Clear high side level settings in backup register and keep all other bits.
+ svsmhctl_backup &= ~PMM_OTHER_BITSH;
+ // Restore high side SVS monitor settings.
+ HWREG16(SVSMHCTL_ADDRESS) |= svsmhctl_backup;
+
+ pmm_interrupt_flags_wait(SVSMLDLYIFG | SVSMHDLYIFG);
+ pmm_interrupt_flags_clear(PMM_CLEANUP_FLAGS);
+
+ // Restore interrupt enable register.
+ HWREG16(PMMRIE_ADDRESS) = pmmrie_backup;
+ pmm_write_access_allow(false);
+
+ return true;
+}
+
+bool pmm_voltage_init(enum ppm_voltage voltage)
+{
+ const uint8_t exp_level = pmm_level_mask(voltage) & PMMCOREV_3;
+ uint8_t act_level = (HWREG16(PMM_BASE + OFS_PMMCTL0) & PMMCOREV_3);
+ bool result = true;
+ while ((exp_level != act_level) && result) {
+ if (exp_level > act_level)
+ result = pmm_voltage_set_up(++act_level);
+ else
+ result = pmm_voltage_set_down(--act_level);
+ }
+ return result;
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/pmm.h b/examples/baremetal/msp430f5529/nes-gamepads/pmm.h
new file mode 100644
index 000000000..049fddc69
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/pmm.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_PMM_H
+#define MSP430_PMM_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum ppm_voltage {
+ PMM_VOLTAGE_1_35V,
+ PMM_VOLTAGE_1_55V,
+ PMM_VOLTAGE_1_75V,
+ PMM_VOLTAGE_1_85V
+};
+
+bool pmm_voltage_init(enum ppm_voltage voltage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MSP430_PMM_H
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/ucs.c b/examples/baremetal/msp430f5529/nes-gamepads/ucs.c
new file mode 100644
index 000000000..a4e17b8ac
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/ucs.c
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hwdefs.h"
+#include "ucs.h"
+
+#if defined(__GNUC__)
+#define __delay_cycles(x) \
+ ({ \
+ for (uint16_t j = 0; j < x; j++) { \
+ __no_operation(); \
+ } \
+ })
+#endif
+
+static uint16_t ucs_source_mask(enum ucs_source source)
+{
+ switch (source) {
+ case UCS_XT1CLK_SELECT:
+ return SELM__XT1CLK;
+ case UCS_VLOCLK_SELECT:
+ return SELM__VLOCLK;
+ case UCS_REFOCLK_SELECT:
+ return SELM__REFOCLK;
+ case UCS_DCOCLK_SELECT:
+ return SELM__DCOCLK;
+ case UCS_DCOCLKDIV_SELECT:
+ return SELM__DCOCLKDIV;
+ case UCS_XT2CLK_SELECT:
+ return SELM__XT2CLK;
+ default:
+ break;
+ }
+ return SELM__XT1CLK;
+}
+
+static uint16_t ucs_divider_mask(enum ucs_divider divider)
+{
+ switch (divider) {
+ case UCS_CLOCK_DIVIDER_1:
+ return DIVM__1;
+ case UCS_CLOCK_DIVIDER_2:
+ return DIVM__2;
+ case UCS_CLOCK_DIVIDER_4:
+ return DIVM__4;
+ case UCS_CLOCK_DIVIDER_8:
+ return DIVM__8;
+ case UCS_CLOCK_DIVIDER_12:
+ return DIVM__32;
+ case UCS_CLOCK_DIVIDER_16:
+ return DIVM__16;
+ case UCS_CLOCK_DIVIDER_32:
+ return DIVM__32;
+ default:
+ break;
+ }
+ return DIVM__1;
+}
+
+static void ucs_fll_init(uint16_t fsystem, uint16_t ratio)
+{
+ const uint16_t sr_reg_backup = __get_SR_register() & SCG0;
+
+ uint16_t mode = 0;
+ uint16_t d = ratio;
+ if (fsystem > 16000) {
+ d >>= 1;
+ mode = 1;
+ } else {
+ fsystem <<= 1;
+ }
+
+ uint16_t dco_div_bits = FLLD__2;
+ while (d > 512) {
+ dco_div_bits = dco_div_bits + FLLD0;
+ d >>= 1;
+ }
+
+ // Disable FLL.
+ __bis_SR_register(SCG0);
+ // Set DCO to lowest tap.
+ HWREG8(UCS_BASE + OFS_UCSCTL0_H) = 0;
+ // Reset FN bits.
+ HWREG16(UCS_BASE + OFS_UCSCTL2) &= ~(0x03FF);
+ HWREG16(UCS_BASE + OFS_UCSCTL2) = dco_div_bits | (d - 1);
+
+ if (fsystem <= 630) { // fsystem < 0.63MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_0;
+ } else if (fsystem < 1250) { // 0.63MHz < fsystem < 1.25MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_1;
+ } else if (fsystem < 2500) { // 1.25MHz < fsystem < 2.5MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_2;
+ } else if (fsystem < 5000) { // 2.5MHz < fsystem < 5MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_3;
+ } else if (fsystem < 10000) { // 5MHz < fsystem < 10MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_4;
+ } else if (fsystem < 20000) { // 10MHz < fsystem < 20MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_5;
+ } else if (fsystem < 40000) { // 20MHz < fsystem < 40MHz
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_6;
+ } else {
+ HWREG8(UCS_BASE + OFS_UCSCTL1) = DCORSEL_7;
+ }
+
+ // Re-enable FLL.
+ __bic_SR_register(SCG0);
+
+ while (HWREG8(UCS_BASE + OFS_UCSCTL7_L) & DCOFFG) {
+ // Clear OSC fault flags.
+ HWREG8(UCS_BASE + OFS_UCSCTL7_L) &= ~(DCOFFG);
+ // Clear OFIFG fault flag.
+ HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
+ }
+
+ // Restore previous SCG0.
+ __bis_SR_register(sr_reg_backup);
+
+ if (mode == 1) {
+ // Select DCOCLK because fsystem > 16000.
+ HWREG16(UCS_BASE + OFS_UCSCTL4) &= ~(SELM_7 + SELS_7);
+ HWREG16(UCS_BASE + OFS_UCSCTL4) |= SELM__DCOCLK + SELS__DCOCLK;
+ } else {
+ // Select DCODIVCLK.
+ HWREG16(UCS_BASE + OFS_UCSCTL4) &= ~(SELM_7 + SELS_7);
+ HWREG16(UCS_BASE + OFS_UCSCTL4) |= SELM__DCOCLKDIV + SELS__DCOCLKDIV;
+ }
+}
+
+void ucs_init(enum ucs_signal signal,
+ enum ucs_source source,
+ enum ucs_divider divider)
+{
+ uint16_t source_msk = ucs_source_mask(source);
+ uint16_t divider_msk = ucs_divider_mask(divider);
+
+ switch (signal) {
+ case UCS_ACLK:
+ HWREG16(UCS_BASE + OFS_UCSCTL4) &= ~SELA_7;
+ source_msk |= source_msk << 8;
+ HWREG16(UCS_BASE + OFS_UCSCTL4) |= source_msk;
+ HWREG16(UCS_BASE + OFS_UCSCTL5) &= ~DIVA_7;
+ divider_msk = divider_msk << 8;
+ HWREG16(UCS_BASE + OFS_UCSCTL5) |= divider_msk;
+ break;
+ case UCS_SMCLK:
+ HWREG16(UCS_BASE + OFS_UCSCTL4) &= ~SELS_7;
+ source_msk = source_msk << 4;
+ HWREG16(UCS_BASE + OFS_UCSCTL4) |= source_msk;
+ HWREG16(UCS_BASE + OFS_UCSCTL5) &= ~DIVS_7;
+ divider_msk = divider_msk << 4;
+ HWREG16(UCS_BASE + OFS_UCSCTL5) |= divider_msk;
+ break;
+ case UCS_MCLK:
+ HWREG16(UCS_BASE + OFS_UCSCTL4) &= ~SELM_7;
+ HWREG16(UCS_BASE + OFS_UCSCTL4) |= source_msk;
+ HWREG16(UCS_BASE + OFS_UCSCTL5) &= ~DIVM_7;
+ HWREG16(UCS_BASE + OFS_UCSCTL5) |= divider_msk;
+ break;
+ case UCS_FLLREF:
+ HWREG8(UCS_BASE + OFS_UCSCTL3) &= ~SELREF_7;
+ source_msk = source_msk << 4;
+ HWREG8(UCS_BASE + OFS_UCSCTL3) |= source_msk;
+ HWREG8(UCS_BASE + OFS_UCSCTL3) &= ~FLLREFDIV_7;
+
+ switch (divider) {
+ case UCS_CLOCK_DIVIDER_12:
+ HWREG8(UCS_BASE + OFS_UCSCTL3) |= FLLREFDIV__12;
+ break;
+ case UCS_CLOCK_DIVIDER_16:
+ HWREG8(UCS_BASE + OFS_UCSCTL3) |= FLLREFDIV__16;
+ break;
+ default:
+ HWREG8(UCS_BASE + OFS_UCSCTL3) |= divider_msk;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool ucs_xt2_blocking_turn_on(uint16_t drive, uint16_t timeout)
+{
+ // Check if drive value is expected one.
+ if ((HWREG16(UCS_BASE + OFS_UCSCTL6) & XT2DRIVE_3) != drive) {
+ // Clear XT2 drive field.
+ HWREG16(UCS_BASE + OFS_UCSCTL6) &= ~XT2DRIVE_3;
+ HWREG16(UCS_BASE + OFS_UCSCTL6) |= drive;
+ }
+
+ HWREG16(UCS_BASE + OFS_UCSCTL6) &= ~XT2BYPASS;
+ // Switch on XT2 oscillator.
+ HWREG16(UCS_BASE + OFS_UCSCTL6) &= ~XT2OFF;
+
+ do {
+ // Clear OSC fault Flags.
+ HWREG8(UCS_BASE + OFS_UCSCTL7) &= ~XT2OFFG;
+ // Clear OFIFG fault flag.
+ HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
+ } while ((HWREG8(UCS_BASE + OFS_UCSCTL7) & XT2OFFG) && --timeout);
+
+ return timeout > 0;
+}
+
+void ucs_xt2_turn_off(void)
+{
+ HWREG16(UCS_BASE + OFS_UCSCTL6) |= XT2OFF;
+}
+
+void ucs_fll_settle_init(uint16_t fsystem, uint16_t ratio)
+{
+ volatile uint16_t x = ratio * 32;
+ ucs_fll_init(fsystem, ratio);
+ while (x--) {
+ __delay_cycles(30);
+ }
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/ucs.h b/examples/baremetal/msp430f5529/nes-gamepads/ucs.h
new file mode 100644
index 000000000..1741c37ff
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/ucs.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_UCS_H
+#define MSP430_UCS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum ucs_signal {
+ UCS_ACLK = 0x01,
+ UCS_MCLK = 0x02,
+ UCS_SMCLK = 0x04,
+ UCS_FLLREF = 0x08
+};
+
+enum ucs_source {
+ UCS_XT1CLK_SELECT,
+ UCS_VLOCLK_SELECT,
+ UCS_REFOCLK_SELECT,
+ UCS_DCOCLK_SELECT,
+ UCS_DCOCLKDIV_SELECT,
+ UCS_XT2CLK_SELECT
+};
+
+enum ucs_divider {
+ UCS_CLOCK_DIVIDER_1,
+ UCS_CLOCK_DIVIDER_2,
+ UCS_CLOCK_DIVIDER_4,
+ UCS_CLOCK_DIVIDER_8,
+ UCS_CLOCK_DIVIDER_12,
+ UCS_CLOCK_DIVIDER_16,
+ UCS_CLOCK_DIVIDER_32
+};
+
+enum ucs_fault_flag {
+ UCS_XT2OFFG,
+ UCS_XT1HFOFFG,
+ UCS_XT1LFOFFG,
+ UCS_DCOFFG
+};
+
+void ucs_clocks_init(void);
+
+void ucs_init(enum ucs_signal signal,
+ enum ucs_source source,
+ enum ucs_divider divider);
+
+bool ucs_xt2_blocking_turn_on(uint16_t drive, uint16_t timeout);
+
+void ucs_xt2_turn_off(void);
+void ucs_fll_settle_init(uint16_t fsystem, uint16_t ratio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MSP430_UCS_H
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/usb.c b/examples/baremetal/msp430f5529/nes-gamepads/usb.c
new file mode 100644
index 000000000..2a6485803
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/usb.c
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gpio.h"
+#include "hid.h"
+#include "hwdefs.h"
+#include "ucs.h"
+#include "usb.h"
+
+static uint16_t usb_khz_freq_get(void)
+{
+ uint16_t freq = 0;
+ const uint8_t selm_curr = (UCSCTL4_L & SELM_7);
+ if (selm_curr <= 4) {
+ uint16_t fll_ref_freq = 33; // It is 32.768 kHz.
+ if ((UCSCTL3_L & SELREF_7) >= 0x50)
+ fll_ref_freq = (4.0) * 1000;
+
+ uint16_t flln_curr = (UCSCTL2 & 0x03FF) + 1;
+ if (selm_curr == SELM_3) {
+ uint16_t flld_curr = (UCSCTL2 & FLLD_7);
+ flld_curr >>= 12;
+ flln_curr <<= flld_curr;
+ }
+
+ const uint8_t fll_ref_div = (UCSCTL3_L & FLLREFDIV_7);
+ if (fll_ref_div == 0)
+ freq = flln_curr * (fll_ref_freq / 1);
+ else if (fll_ref_div == 1)
+ freq = flln_curr * (fll_ref_freq / 2);
+ else if (fll_ref_div == 2)
+ freq = flln_curr * (fll_ref_freq / 4);
+ else if (fll_ref_div == 3)
+ freq = flln_curr * (fll_ref_freq / 8);
+ else if (fll_ref_div == 4)
+ freq = flln_curr * (fll_ref_freq / 12);
+ else if (fll_ref_div == 5)
+ freq = flln_curr * (fll_ref_freq / 16);
+ } else {
+ freq = (4.0) * 1000;
+ }
+ return freq >> (UCSCTL5_L & DIVM_7);
+}
+
+static uint16_t usb_delay_250us_get(void)
+{
+ const uint16_t mclk_freq = usb_khz_freq_get();
+ const uint16_t delay_250us = ((mclk_freq >> 6) | (mclk_freq >> 7)
+ | (mclk_freq >> 9));
+ return delay_250us;
+}
+
+static void usb_cfg_access_allow(bool allow)
+{
+ enum { ALLOW_KEY = 0x9628, DENIED_KEY = 0x9600 };
+ USBKEYPID = allow ? ALLOW_KEY : DENIED_KEY;
+}
+
+static bool usb_pll_enable(void)
+{
+ if (!(USBPWRCTL & USBBGVBV))
+ return false;
+
+ if ((USBCNF & USB_EN) && (USBPLLCTL & UPLLEN))
+ return true;
+
+ gpio_pins_set_as_pf_out(GPIO_PORT_P5, GPIO_PIN2);
+ gpio_pins_set_as_pf_out(GPIO_PORT_P5, GPIO_PIN3);
+
+ usb_cfg_access_allow(true);
+
+ if (!ucs_xt2_blocking_turn_on(XT2DRIVE_0, 50000))
+ return false;
+
+ USBPLLDIVB = USBPLL_SETCLK_4_0;
+ USBPLLCTL = UPFDEN | UPLLEN;
+
+ const uint16_t delay_250us = usb_delay_250us_get();
+ uint8_t j = 0;
+ do {
+ USBPLLIR = 0;
+ for (uint8_t k = 0; k < 2; ++k) {
+ for (uint16_t i = 0; i < delay_250us; ++i) {
+ __no_operation();
+ }
+ }
+
+ if (j++ > 10) {
+ usb_cfg_access_allow(false);
+ return false;
+ }
+ } while (USBPLLIR != 0);
+
+ USBCNF |= USB_EN;
+ usb_cfg_access_allow(false);
+ return true;
+}
+
+static void usb_reset(void)
+{
+ usb_cfg_access_allow(true);
+
+ hid_ep0_enumerated_set(false);
+
+ USBCTL = 0;
+ USBFUNADR = 0;
+ USBOEPIE = 0;
+ USBIEPIE = 0;
+
+ hid_ep0_init();
+ hid_ep1_init();
+
+ USBCTL = FEN;
+ USBIFG = 0;
+ USBIE = SETUPIE | RSTRIE | SUSRIE;
+ usb_cfg_access_allow(false);
+}
+
+static void usb_connect(void)
+{
+ usb_cfg_access_allow(true);
+ USBCNF |= PUR_EN;
+ USBPWRCTL |= VBOFFIE;
+ usb_cfg_access_allow(false);
+}
+
+static void usb_pwr_vbus_wait(void)
+{
+ const uint16_t delay_250us = usb_delay_250us_get();
+ for (uint8_t j = 0; j < 4; ++j) {
+ for (uint16_t i = 0; i < delay_250us; ++i) {
+ __no_operation();
+ }
+ }
+}
+
+static void usb_pwr_vbus_on_handler(void)
+{
+ usb_pwr_vbus_wait();
+
+ if (USBPWRCTL & USBBGVBV) {
+ usb_cfg_access_allow(true);
+ USBPWRCTL |= VBOFFIE;
+ USBPWRCTL &= ~ (VBONIFG + VBOFFIFG);
+ usb_cfg_access_allow(false);
+ }
+}
+
+static void usb_pwr_vbus_off_handler(void)
+{
+ usb_pwr_vbus_wait();
+
+ if (!(USBPWRCTL & USBBGVBV)) {
+ usb_cfg_access_allow(true);
+ USBCNF = 0;
+ USBPLLCTL &= ~UPLLEN;
+ USBPWRCTL &= ~(VBOFFIE + VBOFFIFG + SLDOEN);
+ usb_cfg_access_allow(false);
+ }
+}
+
+void usb_suspend(void)
+{
+ usb_cfg_access_allow(true);
+ USBCTL |= FRSTE;
+ USBIFG &= ~SUSRIFG;
+ USBPLLCTL &= ~UPLLEN;
+ ucs_xt2_turn_off();
+ USBIE = RESRIE;
+ usb_cfg_access_allow(false);
+}
+
+void usb_resume(void)
+{
+ usb_pll_enable();
+ USBIFG &= ~(RESRIFG | SUSRIFG);
+ USBIE = SETUPIE | RSTRIE | SUSRIE;
+}
+
+void usb_init(void)
+{
+ const uint16_t gie_backup = (__get_SR_register() & GIE);
+
+ usb_cfg_access_allow(true);
+ USBPHYCTL = PUSEL;
+ USBPWRCTL = VUSBEN | SLDOAON;
+
+ const uint16_t delay_250us = usb_delay_250us_get();
+ for (uint8_t j = 0; j < 20; ++j) {
+ for (uint16_t i = 0; i < delay_250us; ++i) {
+ __no_operation();
+ }
+ }
+
+ USBPWRCTL |= VBONIE;
+ usb_cfg_access_allow(false);
+
+ __bis_SR_register(gie_backup);
+
+ if (USBPWRCTL & USBBGVBV) {
+ if (usb_pll_enable()) {
+ usb_reset();
+ usb_connect();
+ }
+ }
+}
+
+void usb_task(void)
+{
+ hid_ep1_task();
+}
+
+INTERRUPT(usb_ubm_isr, USB_UBM_VECTOR)
+{
+ bool wake_up = false;
+
+ if (USBIFG & SETUPIFG) {
+ hid_ep0_setup_handler();
+ USBIFG &= ~SETUPIFG;
+ }
+
+ switch (__even_in_range(USBVECINT & 0x3F, USBVECINT_OUTPUT_ENDPOINT7)) {
+ case USBVECINT_PWR_DROP:
+ __no_operation();
+ break;
+ case USBVECINT_PLL_RANGE:
+ wake_up = true;
+ break;
+ case USBVECINT_PWR_VBUSOn:
+ usb_pwr_vbus_on_handler();
+ if (usb_pll_enable()) {
+ usb_reset();
+ usb_connect();
+ }
+ wake_up = true;
+ break;
+ case USBVECINT_PWR_VBUSOff:
+ usb_pwr_vbus_off_handler();
+ ucs_xt2_turn_off();
+ wake_up = true;
+ break;
+ case USBVECINT_INPUT_ENDPOINT0:
+ hid_ep0_in_handler();
+ break;
+ case USBVECINT_RSTR:
+ usb_reset();
+ wake_up = true;
+ break;
+ case USBVECINT_SUSR:
+ usb_suspend();
+ wake_up = true;
+ break;
+ case USBVECINT_RESR:
+ usb_resume();
+ wake_up = true;
+ break;
+ case USBVECINT_SETUP_PACKET_RECEIVED:
+ hid_ep0_in_nak();
+ hid_ep0_out_nak();
+ hid_ep0_setup_handler();
+ break;
+ default:
+ break;
+ }
+
+ if (wake_up) {
+ __bic_SR_register_on_exit(LPM3_bits);
+ __no_operation();
+ }
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/usb.h b/examples/baremetal/msp430f5529/nes-gamepads/usb.h
new file mode 100644
index 000000000..503f3cf07
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/usb.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_USB_H
+#define MSP430_USB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum usb_setup_bmreq_bits {
+ SETUP_DIR = 0x80,
+ SETUP_TYPE = 0x60,
+ SETUP_RECIPIENT = 0x1F
+};
+
+// Setup request direction.
+enum usb_setup_req_direction_bits {
+ SETUP_OUTPUT = 0, // From host to device direction.
+ SETUP_INPUT = 0x80 // From device to host direction.
+};
+
+// Setup request type.
+enum usb_setup_req_type_bits {
+ SETUP_STANDARD = 0, // Standard request.
+ SETUP_CLASS = 0x20, // Class request.
+ SETUP_VENDOR = 0x40 // Vendor request.
+};
+
+// Setup request recipient.
+enum usb_setup_req_recipient_bits {
+ SETUP_DEVICE = 0, // Device recipient.
+ SETUP_IFACE = 0x01, // Interface recipient.
+ SETUP_EP = 0x02, // End point recipient.
+ SETUP_OTHER = 0x03 // Other recipient.
+};
+
+// Setup request code.
+enum usb_setup_req_code {
+ SETUP_GET_STATUS = 0x00, // Get status code.
+ SETUP_CLEAR_FEATURE = 0x01, // Clear feature code.
+ SETUP_RESERVED1 = 0x02, // Reserved code.
+ SETUP_SET_FEATURE = 0x03, // Set feature code.
+ SETUP_RESERVED2 = 0x04, // Reserved code.
+ SETUP_SET_ADDRESS = 0x05, // Set address code.
+ SETUP_GET_DESCRIPTOR = 0x06, // Get descriptor code.
+ SETUP_SET_DESCRIPTOR = 0x07, // Set descriptor code.
+ SETUP_GET_CONFIGURATION = 0x08, // Get configuration code.
+ SETUP_SET_CONFIGURATION = 0x09, // Set configuration code.
+ SETUP_GET_INTERFACE = 0x0A, // Get interface code.
+ SETUP_SET_INTERFACE = 0x0B, // Set interface code.
+ SETUP_SYNC_FRAME = 0x0C, // Sync frame code.
+ SETUP_ANCHOR_LOAD = 0xA0 // Anchor load code.
+};
+
+// Standard status responses.
+enum usb_setup_status_code {
+ STATUS_SELF_POWERED = 0x01,
+ STATUS_REMOTE_WAKEUP = 0x02
+};
+
+// Standard feature selectors.
+enum usb_setup_feature_selector {
+ FEATURE_STALL = 0x00,
+ FEATURE_REMOTE_WAKEUP = 0x01,
+ FEATURE_TEST_MODE = 0x02
+};
+
+// Get descriptor codes.
+enum usb_setup_get_descriptor_code {
+ DESC_DEVICE = 0x01, // Device descriptor.
+ DESC_CONF = 0x02, // Configuration descriptor.
+ DESC_STRING = 0x03, // String descriptor.
+ DESC_INTERFACE = 0x04, // Interface descriptor.
+ DESC_ENDPOINT = 0x05, // End point descriptor.
+ DESC_DEVICE_QUAL = 0x06, // Device qualifier descriptor.
+ DESC_OTHER_SPEED_CONF = 0x07, // Other configuration descriptor.
+ DESC_INTERFACE_POWER = 0x08, // Interface power descriptor.
+ DESC_OTG = 0x09, // OTG descriptor.
+ DESC_DEBUG = 0x0A, // Debug descriptor.
+ DESC_INTERFACE_ASSOC = 0x0B, // Interface association descriptor.
+ DESC_HID = 0x21, // Get HID descriptor.
+ DESC_REPORT = 0x22 // Get report descriptor.
+};
+
+
+enum usb_ep_size {
+ EP0_MAX_PACKET_SIZE = 8,
+ EP_MAX_PACKET_SIZE = 64,
+ EP_MAX_FIFO_SIZE = 256,
+ EP_NO_MORE_DATA = 0xFFFF
+};
+
+void usb_init(void);
+void usb_task(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MSP430_USB_H
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/wdt_a.c b/examples/baremetal/msp430f5529/nes-gamepads/wdt_a.c
new file mode 100644
index 000000000..5adf3c361
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/wdt_a.c
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "hwdefs.h"
+#include "wdt_a.h"
+
+#define WDT_CONTROL_ADDRESS (WDT_A_BASE + OFS_WDTCTL)
+
+void wdt_a_stop(void)
+{
+ const uint8_t st = (HWREG16(WDT_CONTROL_ADDRESS) & 0x00FF) | WDTHOLD;
+ HWREG16(WDT_CONTROL_ADDRESS) = WDTPW + st;
+}
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/wdt_a.h b/examples/baremetal/msp430f5529/nes-gamepads/wdt_a.h
new file mode 100644
index 000000000..3a5e5b407
--- /dev/null
+++ b/examples/baremetal/msp430f5529/nes-gamepads/wdt_a.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of Qbs.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MSP430_WDT_A_H
+#define MSP430_WDT_A_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void wdt_a_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MSP430_WDT_A_H