diff --git a/CMakeLists.txt b/CMakeLists.txt index 07ce1f9..82524d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,11 +19,13 @@ libhal_test_and_make_library( LIBRARY_NAME libhal-input SOURCES + src/ch9329.cpp src/input.cpp src/gamepad/nunchuck.cpp TEST_SOURCES tests/gamepad/nunchuck.test.cpp + tests/ch9329.test.cpp tests/input.test.cpp tests/main.test.cpp ) diff --git a/datasheets/ch9329/CH9329 Chip Serial Port Communication Protocol.PDF b/datasheets/ch9329/CH9329 Chip Serial Port Communication Protocol.PDF new file mode 100644 index 0000000..af18ad9 Binary files /dev/null and b/datasheets/ch9329/CH9329 Chip Serial Port Communication Protocol.PDF differ diff --git a/datasheets/ch9329/CH9329DS1.PDF b/datasheets/ch9329/CH9329DS1.PDF new file mode 100644 index 0000000..26f797b Binary files /dev/null and b/datasheets/ch9329/CH9329DS1.PDF differ diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index 59ba97a..402652b 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -20,6 +20,9 @@ libhal_build_demos( DEMOS input nunchuck + # ch9329_nunchuck_mouse + # ch9329_absolute_mouse + # ch9329_keyboard INCLUDES . diff --git a/demos/applications/ch9329_absolute_mouse.cpp b/demos/applications/ch9329_absolute_mouse.cpp new file mode 100644 index 0000000..c406a42 --- /dev/null +++ b/demos/applications/ch9329_absolute_mouse.cpp @@ -0,0 +1,75 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include + +void application(resource_list& p_map) +{ + using namespace std::chrono_literals; + using namespace hal::literals; + + auto& clock = *p_map.clock.value(); + auto& console = *p_map.console.value(); + auto& uart3 = *p_map.uart3.value(); + + hal::input::ch9329 usb_control(uart3); + uint16_t width = 3840; + uint16_t height = 2160; + + hal::input::ch9329::mouse_absolute abs_mouse_control(width, height); + + hal::print(console, "Demo Application Starting...\n\n"); + hal::print<32>(console, "Screen Width: %li ", width); + hal::print<32>(console, "Screen Height: %li \n", height); + + while (true) { + // top left corner + abs_mouse_control.position(1, 1); + usb_control.send(abs_mouse_control); + hal::delay(clock, 1s); + + // middle screen + abs_mouse_control.position(width / 2, height / 2); + usb_control.send(abs_mouse_control); + hal::delay(clock, 1s); + + // bottom right + abs_mouse_control.position(width, height); + usb_control.send(abs_mouse_control); + hal::delay(clock, 1s); + + // bottom left + abs_mouse_control.position(1, height); + usb_control.send(abs_mouse_control); + hal::delay(clock, 1s); + + // middle screen + abs_mouse_control.position(width / 2, height / 2); + usb_control.send(abs_mouse_control); + hal::delay(clock, 1s); + + // top right + abs_mouse_control.position(width, 1); + usb_control.send(abs_mouse_control); + hal::delay(clock, 1s); + } +} diff --git a/demos/applications/ch9329_keyboard.cpp b/demos/applications/ch9329_keyboard.cpp new file mode 100644 index 0000000..d48114f --- /dev/null +++ b/demos/applications/ch9329_keyboard.cpp @@ -0,0 +1,55 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include + +void application(resource_list& p_map) +{ + using namespace std::chrono_literals; + using namespace hal::literals; + + auto& clock = *p_map.clock.value(); + auto& console = *p_map.console.value(); + auto& uart3 = *p_map.uart3.value(); + + hal::input::ch9329 usb_control(uart3); + + hal::input::ch9329::keyboard_general keyboard_control; + + hal::print(console, "Demo Application Starting...\n\n"); + + while (true) { + keyboard_control.press_normal_key(normal_key::a, 1); + usb_control.send(keyboard_control); + hal::delay(clock, 1ms); + + keyboard_control.release_normal_key(normal_key::a); + usb_control.send(keyboard_control); + hal::delay(clock, 2s); + + keyboard_control.press_normal_key(normal_key::back_space, 1); + usb_control.send(keyboard_control); + hal::delay(clock, 1ms); + + keyboard_control.release_normal_key(normal_key::back_space); + usb_control.send(keyboard_control); + hal::delay(clock, 2s); + } +} diff --git a/demos/applications/ch9329_nunchuck_mouse.cpp b/demos/applications/ch9329_nunchuck_mouse.cpp new file mode 100644 index 0000000..8b64ce4 --- /dev/null +++ b/demos/applications/ch9329_nunchuck_mouse.cpp @@ -0,0 +1,50 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include + +void application(resource_list& p_map) +{ + using namespace std::chrono_literals; + using namespace hal::literals; + + constexpr auto sensitivity = 16; + auto& clock = *p_map.clock.value(); + auto& console = *p_map.console.value(); + auto& uart3 = *p_map.uart3.value(); + auto& i2c = *p_map.i2c.value(); + + hal::input::nunchuck nunchuck(i2c); + hal::input::ch9329 usb_control(uart3); + hal::input::ch9329::keyboard_general kb_control; + hal::input::ch9329::mouse_relative rel_mouse_control; + + hal::print(console, "Demo Application Starting...\n\n"); + while (true) { + auto data = nunchuck.read(); + std::int8_t x = (data.joystick_x() - 128) / sensitivity; + std::int8_t y = -(data.joystick_y() - 128) / sensitivity; + rel_mouse_control.move(x, y) + .left_button(data.c_button()) + .right_button(data.z_button()); + usb_control.send(rel_mouse_control); + hal::delay(clock, 1ms); + } +} diff --git a/demos/platforms/stm32f103c8.cpp b/demos/platforms/stm32f103c8.cpp index 8a314ac..b5de95b 100644 --- a/demos/platforms/stm32f103c8.cpp +++ b/demos/platforms/stm32f103c8.cpp @@ -41,6 +41,11 @@ resource_list initialize_platform() hal::serial::settings{ .baud_rate = 115200, }); + static hal::stm32f1::uart uart3(hal::port<3>, + hal::buffer<128>, + hal::serial::settings{ + .baud_rate = 9600, + }); static hal::stm32f1::output_pin led('C', 13); static hal::stm32f1::output_pin sda('B', 7); @@ -55,6 +60,7 @@ resource_list initialize_platform() return { .reset = +[]() { hal::cortex_m::reset(); }, .console = &uart1, + .uart3 = &uart3, .clock = &counter, .status_led = &led, .i2c = &bit_bang_i2c, diff --git a/demos/resource_list.hpp b/demos/resource_list.hpp index 906aad9..6a8868c 100644 --- a/demos/resource_list.hpp +++ b/demos/resource_list.hpp @@ -26,6 +26,7 @@ struct resource_list { hal::callback reset; std::optional console = std::nullopt; + std::optional uart3 = std::nullopt; std::optional clock = std::nullopt; std::optional status_led = std::nullopt; // Add more driver interfaces here ... diff --git a/include/libhal-input/ch9329.hpp b/include/libhal-input/ch9329.hpp new file mode 100644 index 0000000..0079678 --- /dev/null +++ b/include/libhal-input/ch9329.hpp @@ -0,0 +1,390 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include +#include +#include + +namespace hal::input { +/** + * @brief Driver for CH9329 UART to USB + * + */ +constexpr auto mouse_abs_data_size = 7; +constexpr auto mouse_rel_data_size = 5; +constexpr auto kb_media_data_size = 4; +constexpr auto kb_acpi_data_size = 2; +constexpr auto kb_general_data_size = 8; + +class ch9329 +{ +public: + /** + * @brief Holds data and functions related to using mouse absolute position + * commands + * + */ + class mouse_absolute + { + public: + /** + * @brief Construct a new mouse absolute object + * + * @param p_screen_width width of the screen, used as max X value + * @param p_screen_height height of the screen, used as max Y value + */ + mouse_absolute(std::uint16_t p_screen_width, std::uint16_t p_screen_height); + /** + * @brief Change the position of the cursor to specified x, y coordinates + * + * @param p_x X coordinate to move to + * @param p_y Y coordinate to move to + * @return mouse_absolute& + */ + mouse_absolute& position(std::uint16_t p_x, std::uint16_t p_y); + /** + * @brief Change the position of the scroll wheel. Negative values indicates + * scrolling down and positive indicates scrolling up + * + * @param p_scroll_offset number of "scroll teeth" to move + * @return mouse_absolute& + */ + mouse_absolute& scroll_position(std::int8_t p_scroll_offset); + /** + * @brief Change the state of the left button + * + * @param p_pressed State of button, true means pressed and false is + * released + * @return mouse_absolute& + */ + mouse_absolute& left_button(bool p_pressed); + /** + * @brief Change the state of the middle button + * + * @param p_pressed State of button, true means pressed and false is + * released + * @return mouse_absolute& + */ + mouse_absolute& middle_button(bool p_pressed); + /** + * @brief Change the state of the right button + * + * @param p_pressed State of button, true means pressed and false is + * released + * @return mouse_absolute& + */ + mouse_absolute& right_button(bool p_pressed); + /** + * @brief Get the screen width + * + * @return std::uint16_t Screen width that was set in contructor + */ + std::uint16_t get_screen_width() + { + return m_screen_width; + }; + /** + * @brief Get the screen height + * + * @return std::uint16_t Screen height that was set in contructor + */ + std::uint16_t get_screen_height() + { + return m_screen_height; + }; + /** + * @brief Get the data array containing the control bytes + * + * @return auto const& Byte array containing control information + */ + auto const& get_data() const + { + return m_data; + } + + private: + std::uint16_t m_screen_width; + std::uint16_t m_screen_height; + std::array m_data = {}; + }; + + /** + * @brief Holds data and functions related to using mouse relative position + * commands + * + */ + class mouse_relative + { + public: + /** + * @brief Construct a new mouse relative object + * + */ + mouse_relative(); + /** + * @brief Move the cursor relative to where it currently is. + * + * @param p_x_offset Number of pixels to move the cursor on the x axis. + * Negative values move left and positive move right. + * @param p_y_offset Number of pixels to move the cursor on the y axis. + * Negative values move down and positive move up. + * @return mouse_relative& + */ + mouse_relative& move(std::int8_t p_x_offset, std::int8_t p_y_offset); + /** + * @brief Change the position of the scroll wheel. Negative values indicates + * scrolling down and positive indicates scrolling up + * + * @param p_scroll_offset number of "scroll teeth" to move + * @return mouse_relative& + */ + mouse_relative& scroll_position(std::int8_t p_scroll_offset); + /** + * @brief Change the state of the left button + * + * @param p_pressed State of button, true means pressed and false is + * released + * @return mouse_relative& + */ + mouse_relative& left_button(bool p_pressed); + /** + * @brief Change the state of the middle button + * + * @param p_pressed State of button, true means pressed and false is + * released + * @return mouse_relative& + */ + mouse_relative& middle_button(bool p_pressed); + /** + * @brief Change the state of the right button + * + * @param p_pressed State of button, true means pressed and false is + * released + * @return mouse_relative& + */ + mouse_relative& right_button(bool p_pressed); + /** + * @brief Get the data array containing the control bytes + * + * @return auto const& Byte array containing control information + */ + auto const& get_data() const + { + return m_data; + } + + private: + std::array m_data = {}; + }; + + /** + * @brief Holds data and functions related to using keyboard media commands + */ + class keyboard_media + { + public: + /** + * @brief Construct a new keyboard media object + * + */ + keyboard_media(); + /** + * @brief Press a media key. + * + * @param p_key media_key enum representing which key to press + * @return keyboard_media& + */ + keyboard_media& press_media_key(media_key p_key); + /** + * @brief Release a media key. + * + * @param p_key media_key enum representing which key to release + * @return keyboard_media& + */ + keyboard_media& release_media_key(media_key p_key); + /** + * @brief Release all acpi keys + * + * @return keyboard_media& + */ + keyboard_media& release_all_keys(); + auto const& get_data() const + { + return m_data; + } + + private: + std::array m_data = {}; + }; + + /** + * @brief Holds data and functions related to using keyboard acpi commands + */ + class keyboard_acpi + { + public: + /** + * @brief Construct a new keyboard acpi object + * + */ + keyboard_acpi(); + /** + * @brief Press an acpi key. + * + * @param p_key acpi_key enum representing which key to release + * @return keyboard_acpi& + */ + keyboard_acpi& press_acpi_key(acpi_key p_key); + /** + * @brief Release an acpi key. + * + * @param p_key acpi_key enum representing which key to release + * @return keyboard_acpi& + */ + keyboard_acpi& release_acpi_key(acpi_key p_key); + /** + * @brief Release all acpi keys + * + * @return keyboard_acpi& + */ + keyboard_acpi& release_all_keys(); + /** + * @brief Get the data array containing the control bytes for acpi keys + * + * @return auto const& Byte array containing control information + */ + auto const& get_data() const + { + return m_data; + } + + private: + std::array m_data = {}; + }; + + /** + * @brief Holds data and functions related to using keyboard general commands + * + */ + class keyboard_general + { + public: + /** + * @brief Construct a new keyboard general object + * + */ + keyboard_general(); + /** + * @brief Press a control key. + * + * Control keys are left and right control, shift, alt, and windows keys. + * + * @param p_key control_key_bit enum value representing which key to press + * @return keyboard_general& + */ + keyboard_general& press_control_key(control_key p_key); + /** + * @brief Press a normal key. + * + * @param p_key normal_key enum value representing which key to press + * @param p_slot Which slot number to put the key into. Valid values are 1 - + * 6 + * @return keyboard_general& + */ + keyboard_general& press_normal_key(normal_key p_key, uint8_t p_slot); + /** + * @brief Release a control key. + * + * @param p_key control_key_bit enum value representing which key to release + * @return keyboard_general& + */ + keyboard_general& release_control_key(control_key p_key); + /** + * @brief Release a normal key. + * + * @param p_key normal_key enum value representing which key to press + * @return keyboard_general& + */ + keyboard_general& release_normal_key(normal_key p_key); + /** + * @brief Release all keys, this includes control and normal keys. + * + * @return keyboard_general& + */ + keyboard_general& release_all_keys(); + /** + * @brief Get the data array containing the control bytes + * + * @return auto const& Byte array containing control information + */ + auto const& get_data() const + { + return m_data; + } + + private: + std::array m_data = {}; + }; + + /** + * @brief Construct a new ch9329 object + * + * @param p_uart uart used to communicate with CH9329 + */ + ch9329(hal::serial& p_uart); + /** + * @brief Send mouse absolute position command + * + * @param p_data mouse absolute object containing command bytes + */ + void send(mouse_absolute const& p_data); + /** + * @brief Send mouse relative position command + * + * @param p_data mouse relative object containing command bytes + */ + void send(mouse_relative const& p_data); + /** + * @brief Send keyboard media command + * + * @param p_data keyboard media object containing command bytes + */ + void send(keyboard_media const& p_data); + /** + * @brief Send keyboard acpi command + * + * @param p_data + */ + void send(keyboard_acpi const& p_data); + /** + * @brief Send keyboard general command + * + * @param p_data keyboard general object containing command bytes + */ + void send(keyboard_general const& p_data); + /** + * @brief Reset the chip using the cmd_reset command + * + * @return hal::byte status byte returned from the chip + */ + hal::byte reset(); + +private: + hal::serial* m_uart; +}; +} // namespace hal::input diff --git a/include/libhal-input/ch9329_keyboard_constants.hpp b/include/libhal-input/ch9329_keyboard_constants.hpp new file mode 100644 index 0000000..3c912e7 --- /dev/null +++ b/include/libhal-input/ch9329_keyboard_constants.hpp @@ -0,0 +1,173 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +enum class control_key : std::uint8_t +{ + left_control = 0, + left_shift = 1, + left_alt = 2, + left_windows = 3, + right_control = 4, + right_shift = 5, + right_alt = 6, + right_windows = 7, +}; + +enum class acpi_key : std::uint8_t +{ + power = 0, + sleep = 1, + wake_up = 2 +}; + +enum class media_key : std::uint8_t +{ + volume_up = 0, + volume_down = 1, + mute = 2, + play_pause = 3, + next_track = 4, + prev_track = 5, + cd_stop = 6, + eject = 7, + email = 8, + internet_search = 9, + internet_favorites = 10, + internet_home = 11, + internet_back = 12, + internet_forward = 13, + internet_stop = 14, + refresh = 15, + media = 16, + explorer = 17, + calculator = 18, + screen_save = 19, + my_computer = 20, + minimize = 21, + record = 22, + rewind = 23 +}; + +enum class normal_key : hal::byte +{ + a = 0x04, + b = 0x05, + c = 0x06, + d = 0x07, + e = 0x08, + f = 0x09, + g = 0x0A, + h = 0x0B, + i = 0x0C, + j = 0x0D, + k = 0x0E, + l = 0x0F, + m = 0x10, + n = 0x11, + o = 0x12, + p = 0x13, + q = 0x14, + r = 0x15, + s = 0x16, + t = 0x17, + u = 0x18, + v = 0x19, + w = 0x1A, + x = 0x1B, + y = 0x1C, + z = 0x1D, + k1 = 0x1E, + k2 = 0x1F, + k3 = 0x20, + k4 = 0x21, + k5 = 0x22, + k6 = 0x23, + k7 = 0x24, + k8 = 0x25, + k9 = 0x26, + k0 = 0x27, + left_enter = 0x28, + esc = 0x29, + back_space = 0x2A, + tab = 0x2B, + space = 0x2C, + hyphen = 0x2D, + equal = 0x2E, + left_bracket = 0x2F, + right_bracket = 0x30, + keycode_29 = 0x31, + keycode_42 = 0x32, + semi_colon = 0x33, + quote_mark = 0x34, + grave = 0x35, + comma = 0x36, // this might be < only? datasheet is weird + period = 0x37, + slash = 0x38, + capslock = 0x39, + f1 = 0x3A, + f2 = 0x3B, + f3 = 0x3C, + f4 = 0x3D, + f5 = 0x3E, + f6 = 0x3F, + f7 = 0x40, + f8 = 0x41, + f9 = 0x42, + f10 = 0x43, + f11 = 0x44, + f12 = 0x45, + print_screen = 0x46, + scroll_lock = 0x47, + pause = 0x48, + insert = 0x49, + home = 0x4A, + pg_up = 0x4B, + delete_key = 0x4C, + end = 0x4D, + pg_dn = 0x4E, + right_arrow = 0x4F, + left_arrow = 0x50, + down_arrow = 0x51, + up_arrow = 0x52, + num_lock = 0x53, + num_pad_asterisk = 0x55, + num_pad_minus = 0x56, + num_pad_plus = 0x57, + num_pad_enter = 0x58, + num_pad_1 = 0x59, + num_pad_2 = 0x5A, + num_pad_3 = 0x5B, + num_pad_4 = 0x5C, + num_pad_5 = 0x5D, + num_pad_6 = 0x5E, + num_pad_7 = 0x5F, + num_pad_8 = 0x60, + num_pad_9 = 0x61, + num_pad_0 = 0x62, + num_pad_del = 0x63, + keycode_45 = 0x64, + keycode_107 = 0x85, + keycode_14 = 0x89, + keycode_56 = 0x87, + left_ctrl = 0xE0, + left_shift = 0xE1, + left_alt = 0xE2, + right_ctrl = 0xE4, + right_shift = 0xE5, + right_alt = 0xE6, +}; diff --git a/src/ch9329.cpp b/src/ch9329.cpp new file mode 100644 index 0000000..ee99201 --- /dev/null +++ b/src/ch9329.cpp @@ -0,0 +1,360 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hal::input { + +constexpr hal::byte header_byte_1 = 0x57; +constexpr hal::byte header_byte_2 = 0xAB; +constexpr hal::byte address_byte = 0x00; + +constexpr hal::byte cmd_reset = 0x0F; +constexpr hal::byte cmd_send_kb_general_data = 0x02; +constexpr hal::byte cmd_send_kb_media_data = 0x03; +constexpr hal::byte cmd_send_ms_abs_data = 0x04; +constexpr hal::byte cmd_send_ms_rel_data = 0x05; + +constexpr auto header_frame_size = 5; + +ch9329::ch9329(hal::serial& p_uart) + : m_uart(&p_uart) +{ +} + +ch9329::mouse_absolute::mouse_absolute(std::uint16_t p_screen_width, + std::uint16_t p_screen_height) + : m_screen_width(p_screen_width) + , m_screen_height(p_screen_height) + +{ + m_data = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +} + +ch9329::mouse_relative::mouse_relative() +{ + m_data = { 0x01, 0x00, 0x00, 0x00, 0x00 }; +} + +ch9329::keyboard_media::keyboard_media() +{ + m_data = { 0x02, 0x00, 0x00, 0x00 }; +} + +ch9329::keyboard_acpi::keyboard_acpi() +{ + m_data = { 0x01, 0x00 }; +} + +ch9329::keyboard_general::keyboard_general() +{ + m_data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +} + +hal::byte get_size_byte(hal::byte p_command) +{ + switch (p_command) { + case cmd_send_kb_general_data: + return 0x08; + case cmd_send_kb_media_data: + return 0x04; + case cmd_send_ms_abs_data: + return 0x07; + case cmd_send_ms_rel_data: + return 0x05; + } + return 0x00; +} + +void send_start_bytes(serial& p_serial, + hal::byte p_command, + std::uint8_t p_size = 0) +{ + std::array start_bytes = { + header_byte_1, header_byte_2, address_byte, p_command + }; + if (p_size == 0) { + start_bytes[4] = get_size_byte(p_command); + } else { + start_bytes[4] = p_size; + } + hal::print(p_serial, start_bytes); +} + +hal::byte calculate_sum(std::span p_bytes, + hal::byte p_command, + std::uint8_t p_custom_length = 0) +{ + std::uint8_t sum_byte = header_byte_1; + sum_byte += header_byte_2; + sum_byte += address_byte; + sum_byte += p_command; + sum_byte += get_size_byte(p_command); + for (hal::byte byte : p_bytes) { + sum_byte += byte; + } + sum_byte += p_custom_length; + return sum_byte; +} + +void send_command_with_bytes(std::span p_bytes, + hal::byte p_command, + serial& p_serial, + std::uint8_t p_size = 0) +{ + send_start_bytes(p_serial, p_command, p_size); + hal::print(p_serial, p_bytes); + auto sum_byte = calculate_sum(p_bytes, p_command, p_size); + hal::print(p_serial, std::to_array({ sum_byte })); +} + +void ch9329::send(ch9329::mouse_relative const& p_data) +{ + auto bytes = p_data.get_data(); + send_command_with_bytes(bytes, cmd_send_ms_rel_data, *m_uart); +} + +void ch9329::send(ch9329::mouse_absolute const& p_data) +{ + auto bytes = p_data.get_data(); + send_command_with_bytes(bytes, cmd_send_ms_abs_data, *m_uart); +} + +void ch9329::send(ch9329::keyboard_general const& p_data) +{ + auto bytes = p_data.get_data(); + send_command_with_bytes(bytes, cmd_send_kb_general_data, *m_uart); +} + +void ch9329::send(keyboard_media const& p_data) +{ + auto bytes = p_data.get_data(); + send_command_with_bytes(bytes, cmd_send_kb_media_data, *m_uart); +} + +void ch9329::send(keyboard_acpi const& p_data) +{ + auto bytes = p_data.get_data(); + send_command_with_bytes(bytes, cmd_send_kb_media_data, *m_uart, 2); +} + +hal::byte ch9329::reset() +{ + send_start_bytes(*m_uart, cmd_reset); + auto sum_byte = calculate_sum({}, cmd_reset); + hal::print(*m_uart, std::to_array({ sum_byte })); + std::array response; + hal::read(*m_uart, response, hal::never_timeout()); + return response[5]; +} + +// mouse absolute functions +ch9329::mouse_absolute& ch9329::mouse_absolute::position(std::uint16_t p_x, + std::uint16_t p_y) +{ + if (p_x >= m_screen_width) { + p_x = m_screen_width - 1; + } + if (p_y >= m_screen_height) { + p_y = m_screen_height - 1; + } + std::uint16_t x_value = ((p_x * 4096) / m_screen_width); + hal::byte lower_bits = x_value & 0xFF; + m_data[2] = lower_bits; + hal::byte upper_bits = (x_value >> 8) & 0xFF; + m_data[3] = upper_bits; + + std::uint16_t y_value = ((p_y * 4096) / m_screen_height); + lower_bits = y_value & 0xFF; + m_data[4] = lower_bits; + upper_bits = (y_value >> 8) & 0xFF; + m_data[5] = upper_bits; + return *this; +} + +ch9329::mouse_absolute& ch9329::mouse_absolute::scroll_position( + std::int8_t p_scroll_offset) +{ + m_data[6] = p_scroll_offset; + return *this; +} + +ch9329::mouse_absolute& ch9329::mouse_absolute::left_button(bool p_pressed) +{ + constexpr auto left_button_mask = hal::bit_mask::from<0>(); + hal::bit_modify(m_data[1]).insert(p_pressed); + return *this; +} + +ch9329::mouse_absolute& ch9329::mouse_absolute::middle_button(bool p_pressed) +{ + constexpr auto middle_button_mask = hal::bit_mask::from<2>(); + hal::bit_modify(m_data[1]).insert(p_pressed); + return *this; +} + +ch9329::mouse_absolute& ch9329::mouse_absolute::right_button(bool p_pressed) +{ + constexpr auto right_button_mask = hal::bit_mask::from<1>(); + hal::bit_modify(m_data[1]).insert(p_pressed); + return *this; +} + +// mouse relative functions +ch9329::mouse_relative& ch9329::mouse_relative::move(std::int8_t p_x_offset, + std::int8_t p_y_offset) +{ + m_data[2] = p_x_offset; + m_data[3] = p_y_offset; + return *this; +} + +ch9329::mouse_relative& ch9329::mouse_relative::scroll_position( + std::int8_t p_scroll_offset) +{ + m_data[4] = p_scroll_offset; + return *this; +} + +ch9329::mouse_relative& ch9329::mouse_relative::left_button(bool p_pressed) +{ + constexpr auto left_button_mask = hal::bit_mask::from<0>(); + hal::bit_modify(m_data[1]).insert(p_pressed); + return *this; +} + +ch9329::mouse_relative& ch9329::mouse_relative::middle_button(bool p_pressed) +{ + constexpr auto middle_button_mask = hal::bit_mask::from<2>(); + hal::bit_modify(m_data[1]).insert(p_pressed); + return *this; +} + +ch9329::mouse_relative& ch9329::mouse_relative::right_button(bool p_pressed) +{ + constexpr auto right_button_mask = hal::bit_mask::from<1>(); + hal::bit_modify(m_data[1]).insert(p_pressed); + return *this; +} + +// keyboard media functions +ch9329::keyboard_media& ch9329::keyboard_media::press_media_key(media_key p_key) +{ + // calculate which byte the bit to change is in + auto byte_num = (static_cast(p_key) >> 3) + 1; + // get which bit to change by reading last 3 bits + auto bit_num = static_cast(p_key) & 0b111; + // change the byte/bit combo to 1 + m_data[byte_num] |= 1 << bit_num; + return *this; +} + +ch9329::keyboard_media& ch9329::keyboard_media::release_media_key( + media_key p_key) +{ + // calculate which byte the bit to change is in + auto byte_num = (static_cast(p_key) >> 3) + 1; + // get which bit to change by reading last 3 bits + auto bit_num = static_cast(p_key) & 0b111; + // change the byte/bit combo to 0 + m_data[byte_num] &= ~(1 << bit_num); + return *this; +} + +ch9329::keyboard_media& ch9329::keyboard_media::release_all_keys() +{ + m_data = { 0x01, 0x00, 0x00, 0x00 }; + return *this; +} + +// keyboard acpi functions +ch9329::keyboard_acpi& ch9329::keyboard_acpi::press_acpi_key(acpi_key p_key) +{ + auto bit_num = static_cast(p_key); + m_data[1] |= 1 << bit_num; + return *this; +} + +ch9329::keyboard_acpi& ch9329::keyboard_acpi::release_acpi_key(acpi_key p_key) +{ + auto bit_num = static_cast(p_key); + m_data[1] &= ~(1 << bit_num); + return *this; +} + +ch9329::keyboard_acpi& ch9329::keyboard_acpi::release_all_keys() +{ + m_data = { 0x02, 0x00 }; + return *this; +} + +// keyboard general functions +ch9329::keyboard_general& ch9329::keyboard_general::press_control_key( + control_key p_key) +{ + + hal::byte mask = (1 << hal::value(p_key)); + m_data[0] = m_data[0] | mask; + return *this; +} + +ch9329::keyboard_general& ch9329::keyboard_general::release_control_key( + control_key p_key) +{ + hal::byte mask = ~(1 << hal::value(p_key)); + m_data[0] = m_data[0] & mask; + return *this; +} + +ch9329::keyboard_general& ch9329::keyboard_general::press_normal_key( + normal_key p_key, + uint8_t p_slot) +{ + constexpr std::uint8_t slot_min = 1; + constexpr std::uint8_t slot_max = 6; + p_slot = std::clamp(p_slot, slot_min, slot_max); + // first byte of m_data is reserved, first slot starts at position 2 + m_data[p_slot + 1] = static_cast(p_key); + return *this; +} + +ch9329::keyboard_general& ch9329::keyboard_general::release_normal_key( + normal_key p_key) +{ + for (int i = 2; i < 8; i++) { + if (m_data[i] == static_cast(p_key)) { + m_data[i] = 0x00; + } + } + return *this; +} + +ch9329::keyboard_general& ch9329::keyboard_general::release_all_keys() +{ + m_data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return *this; +} +} // namespace hal::input diff --git a/tests/ch9329.test.cpp b/tests/ch9329.test.cpp new file mode 100644 index 0000000..70948ea --- /dev/null +++ b/tests/ch9329.test.cpp @@ -0,0 +1,31 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +namespace hal::input { +void ch9329_test() +{ + using namespace boost::ut; + using namespace std::literals; + + "ch9329::ch9329()"_test = []() { + // Setup + // Exercise + // Verify + }; +}; +} // namespace hal::input