├── .gitignore ├── Client ├── Data │ ├── config.yaml │ ├── keyboard.yaml │ ├── keyboard_hid2code.yaml │ ├── keyboard_qt2hid.yaml │ └── keycode.yaml ├── main.py ├── module │ └── hid_def.py ├── requirements.txt ├── simple_api.py └── ui │ ├── device_setup.ui │ ├── device_setup_dialog.ui │ ├── images │ ├── 24 │ │ ├── calculator.png │ │ ├── fullscreen.png │ │ ├── import.png │ │ ├── keyboard-off.png │ │ ├── keyboard-outline.png │ │ ├── keyboard-settings-outline.png │ │ ├── keyboard-variant.png │ │ ├── keyboard.png │ │ ├── monitor-multiple.png │ │ ├── monitor-off.png │ │ ├── monitor-screenshot.png │ │ ├── monitor.png │ │ ├── mouse-off.png │ │ ├── mouse.png │ │ ├── notebook-edit.png │ │ ├── reload.png │ │ ├── resize.png │ │ ├── video-input-hdmi.png │ │ ├── video-off.png │ │ ├── video.png │ │ ├── window-close.png │ │ ├── window-maximize.png │ │ └── window-minimize.png │ └── 48 │ │ ├── camera-off.png │ │ ├── monitor-multiple -b.png │ │ └── monitor-multiple -l.png │ ├── main_ui.py │ ├── main_ui.ui │ └── shortcut_key.ui ├── Firmware ├── Lib │ ├── CH549.H │ ├── DEBUG.C │ ├── DEBUG.H │ ├── GPIO.C │ ├── GPIO.H │ ├── Timer.C │ ├── Timer.H │ ├── UART.C │ └── UART.H ├── ProJ.uvopt ├── ProJ.uvproj └── User │ └── main.C ├── LICENSE ├── PCB ├── BOM_Board1_PCB1_2022-11-08.xlsx ├── Gerber_PCB1_2022-11-08.zip ├── ProProject_KVM-over-USB_2022-11-08.zip ├── README.md ├── SCH_Schematic1_2022-11-08.pdf └── YuzukiHCC_MS2109_Firmware.bin ├── README.md └── document └── images ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── 5.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | #lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | venv-linux/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | 132 | # --------------------------------C---------------------- 133 | 134 | # Prerequisites 135 | *.d 136 | 137 | # Object files 138 | *.o 139 | *.ko 140 | *.obj 141 | *.elf 142 | 143 | # Linker output 144 | *.ilk 145 | *.map 146 | *.exp 147 | 148 | # Precompiled Headers 149 | *.gch 150 | *.pch 151 | 152 | # Libraries 153 | *.lib 154 | *.a 155 | *.la 156 | *.lo 157 | 158 | # Shared objects (inc. Windows DLLs) 159 | *.dll 160 | *.so 161 | *.so.* 162 | *.dylib 163 | 164 | # Executables 165 | *.exe 166 | *.out 167 | *.app 168 | *.i*86 169 | *.x86_64 170 | *.hex 171 | 172 | # Debug files 173 | *.dSYM/ 174 | *.su 175 | *.idb 176 | *.pdb 177 | 178 | # Kernel Module Compile Results 179 | *.mod* 180 | *.cmd 181 | .tmp_versions/ 182 | modules.order 183 | Module.symvers 184 | Mkfile.old 185 | dkms.conf 186 | 187 | # other 188 | *.bak 189 | 190 | # project 191 | .vscode/ 192 | .idea/ 193 | .idea/vcs.xml 194 | Objects/ 195 | Listings/ 196 | 197 | *.uvgui.* 198 | 199 | *.bat -------------------------------------------------------------------------------- /Client/Data/config.yaml: -------------------------------------------------------------------------------- 1 | 'shortcut_key': 2 | 'shortcut_key_name': 3 | [ "Ctrl+Alt+Del", 4 | "Alt+Tab", 5 | "Ctrl+Shift+Esc", 6 | "Alt+F4", 7 | "Meta+E", 8 | "Meta+R", 9 | "Meta+PrtSc", 10 | "Meta+Break", 11 | "Shift+F10", 12 | "Alt+Space", 13 | ] 14 | 'shortcut_key_hidcode': 15 | [ [ 4,1,5,0,76,0 ], #"Ctrl+Alt+Del", 16 | [ 4,1,4,0,43,0 ], #"Alt+Tab", 17 | [ 4,1,3,0,41,0 ], #"Ctrl+Shift+Esc", 18 | [ 4,1,4,0,61,0 ], #"Alt+F4", 19 | [ 4,1,8,0,8,0 ], #"Meta+E", 20 | [ 4,1,8,0,21,0 ], #"Meta+R", 21 | [ 4,1,8,0,70,0 ], #"Meta+PrtSc" 22 | [ 4,1,8,0,72,0 ], #"Meta+Break", 23 | [ 4,1,2,0,67,0 ], #"Shift+F10", 24 | [ 4,1,4,0,44,0 ], #"Alt+Space", 25 | ] 26 | 'camera_config': 27 | 'connected': False 28 | 'device_No': 1 29 | 'resolution_X': 1360 30 | 'resolution_Y': 768 31 | 'device_name': [ ] 32 | 'config': 33 | 'mouse_jump_frame_count': 20 34 | 'hide_mouse': True -------------------------------------------------------------------------------- /Client/Data/keyboard.yaml: -------------------------------------------------------------------------------- 1 | "A": "0X04" 2 | "B": "0X05" 3 | "C": "0X06" 4 | "D": "0X07" 5 | "E": "0X08" 6 | "F": "0X09" 7 | "G": "0X0A" 8 | "H": "0X0B" 9 | "I": "0X0C" 10 | "J": "0X0D" 11 | "K": "0X0E" 12 | "L": "0X0F" 13 | "M": "0X10" 14 | "N": "0X11" 15 | "O": "0X12" 16 | "P": "0X13" 17 | "Q": "0X14" 18 | "R": "0X15" 19 | "S": "0X16" 20 | "T": "0X17" 21 | "U": "0X18" 22 | "V": "0X19" 23 | "W": "0X1A" 24 | "X": "0X1B" 25 | "Y": "0X1C" 26 | "Z": "0X1D" 27 | 28 | "1": "0X1E" 29 | "2": "0X1F" 30 | "3": "0X20" 31 | "4": "0X21" 32 | "5": "0X22" 33 | "6": "0X23" 34 | "7": "0X24" 35 | "8": "0X25" 36 | "9": "0X26" 37 | "0": "0X27" 38 | ")": "0X27" 39 | "!": "0X1E" 40 | "@": "0X1F" 41 | "#": "0X20" 42 | "$": "0X21" 43 | "%": "0X22" 44 | "^": "0X23" 45 | "&": "0X24" 46 | "*": "0X25" 47 | "(": "0X26" 48 | 49 | "ENTER": "0X28" 50 | "ESC": "0X29" 51 | "BACKSPACE": "0X2A" 52 | "TAB": "0X2B" 53 | "SPACE": "0X2C" 54 | 55 | "Minus": "0X2D" 56 | "Equal": "0X2E" 57 | "-": "0X2D" 58 | "_": "0X2D" 59 | "=": "0X2E" 60 | "+": "0X2E" 61 | 62 | "LEFT BRACKET": "0X2F" 63 | "RIGHT BRACKET": "0X30" 64 | "BACKSLASH": "0X31" 65 | "[": "0X2F" 66 | "]": "0X30" 67 | "\\": "0X31" 68 | "{": "0X2F" 69 | "}": "0X30" 70 | "|": "0X31" 71 | 72 | "NUMBER": "0X32" 73 | "SEMICOLON": "0X33" 74 | "QUOTE": "0X34" 75 | "`": "0X32" 76 | ";": "0X33" 77 | "'": "0X34" 78 | "~": "0X32" 79 | ":": "0X33" 80 | "\"": "0X34" 81 | 82 | "BACKTICK": "0X35" 83 | "COMMA": "0X36" 84 | "PERIOD": "0X37" 85 | "SLASH": "0X38" 86 | ",": "0X36" 87 | ".": "0X37" 88 | "/": "0X38" 89 | "<": "0X36" 90 | ">": "0X37" 91 | "?": "0X38" 92 | 93 | "CAPSLOCK": "0X39" 94 | "F1": "0X3A" 95 | "F2": "0X3B" 96 | "F3": "0X3C" 97 | "F4": "0X3D" 98 | "F5": "0X3E" 99 | "F6": "0X3F" 100 | "F7": "0X40" 101 | "F8": "0X41" 102 | "F9": "0X42" 103 | "F10": "0X43" 104 | "F11": "0X44" 105 | "F12": "0X45" 106 | "PRINTSCREEN": "0X46" 107 | "SCROLLLOCK": "0X47" 108 | "PAUSE": "0X48" 109 | "INSERT": "0X49" 110 | "HOME": "0X4A" 111 | "PGUP": "0X4B" 112 | "DEL": "0X4C" 113 | "END": "0X4D" 114 | "PGDOWN": "0X4E" 115 | "RIGHT": "0X4F" 116 | "LEFT": "0X50" 117 | "DOWN": "0X51" 118 | "UP": "0X52" 119 | "NUMLOCK": "0X53" 120 | "KEYPAD SLASH": "0X54" 121 | "KEYPAD ASTERISK": "0X55" 122 | "KEYPAD MINUS": "0X56" 123 | "KEYPAD PLUS": "0X57" 124 | "KEYPAD ENTER": "0X58" 125 | "KEYPAD 1": "0X59" 126 | "KEYPAD 2": "0X5A" 127 | "KEYPAD 3": "0X5B" 128 | "KEYPAD 4": "0X5C" 129 | "KEYPAD 5": "0X5D" 130 | "KEYPAD 6": "0X5E" 131 | "KEYPAD 7": "0X5F" 132 | "KEYPAD 8": "0X60" 133 | "KEYPAD 9": "0X61" 134 | "KEYPAD 0": "0X62" 135 | "KEYPAD PERIOD": "0X63" 136 | "ISO SLASH": "0X64" 137 | "APP": "0X65" 138 | "KEYBOARD STATUS": "0X66" 139 | "KEYPAD EQUAL": "0X67" 140 | "F13": "0X68" 141 | "F14": "0X69" 142 | "F15": "0X6A" 143 | "F16": "0X6B" 144 | "F17": "0X6C" 145 | "F18": "0X6D" 146 | "F19": "0X6E" 147 | "F20": "0X6F" 148 | "F21": "0X70" 149 | "F22": "0X71" 150 | "F23": "0X72" 151 | "F24": "0X73" 152 | "EXEC": "0X74" 153 | "HELP": "0X75" 154 | "MENU": "0X76" 155 | "SELECT": "0X77" 156 | "STOP": "0X78" 157 | "AGAIN": "0X79" 158 | "UNDO": "0X7A" 159 | "CUT": "0X7B" 160 | "COPY": "0X7C" 161 | "PASTE": "0X7D" 162 | "FIND": "0X7E" 163 | "MUTE": "0X7F" 164 | "VOLUME UP": "0X80" 165 | "VOLUME DOWN": "0X81" 166 | "LOCKING CAPS LOCK": "0X82" 167 | "LOCKING NUM LOCK": "0X83" 168 | "LOCKING SCROLL LOCK": "0X84" 169 | "KEYPAD COMMA": "0X85" 170 | "KEYPAD EQUAL AS400": "0X86" 171 | "INTERNATIONAL1": "0X87" 172 | "INTERNATIONAL2": "0X88" 173 | "INTERNATIONAL3": "0X89" 174 | "INTERNATIONAL4": "0X8A" 175 | "INTERNATIONAL5": "0X8B" 176 | "INTERNATIONAL6": "0X8C" 177 | "INTERNATIONAL7": "0X8D" 178 | "INTERNATIONAL8": "0X8E" 179 | "INTERNATIONAL9": "0X8F" 180 | "LANG1": "0X90" 181 | "LANG2": "0X91" 182 | "LANG3": "0X92" 183 | "LANG4": "0X93" 184 | "LANG5": "0X94" 185 | "LANG6": "0X95" 186 | "LANG7": "0X96" 187 | "LANG8": "0X97" 188 | "LANG9": "0X98" 189 | "ALTERNATE ERASE": "0X99" 190 | "SYSREQ": "0X9A" 191 | "CANCEL": "0X9B" 192 | "CLEAR": "0X9C" 193 | "PRIOR": "0X9D" 194 | "RETURN": "0X9E" 195 | "SEPARATOR": "0X9F" 196 | "OUT": "0XA0" 197 | "OPER": "0XA1" 198 | "CLEAR AGAIN": "0XA2" 199 | "CRSEL PROPS": "0XA3" 200 | "EXSEL": "0XA4" 201 | "KEYPAD 00": "0XB0" 202 | "KEYPAD 000": "0XB1" 203 | "1000 SEPARATOR": "0XB2" 204 | "DECIMAL SEPARATOR": "0XB3" 205 | "CURRENCY UNIT": "0XB4" 206 | "CURRENCY SUBUNIT": "0XB5" 207 | "KEYPAD LEFT PARENTHESIS": "0XB6" 208 | "KEYPAD RIGHT PARENTHESIS": "0XB7" 209 | "KEYPAD LEFT BRACE": "0XB8" 210 | "KEYPAD RIGHT BRACE": "0XB9" 211 | "KEYPAD TAB": "0XBA" 212 | "KEYPAD BACKSPACE": "0XBB" 213 | "KEYPAD A": "0XBC" 214 | "KEYPAD B": "0XBD" 215 | "KEYPAD C": "0XBE" 216 | "KEYPAD D": "0XBF" 217 | "KEYPAD E": "0XC0" 218 | "KEYPAD F": "0XC1" 219 | "KEYPAD XOR": "0XC2" 220 | "KEYPAD CHEVRON": "0XC3" 221 | "KEYPAD PERCENT": "0XC4" 222 | "KEYPAD LESS THAN": "0XC5" 223 | "KEYPAD GREATER THAN": "0XC6" 224 | "KEYPAD BITAND": "0XC7" 225 | "KEYPAD AND": "0XC8" 226 | "KEYPAD BITOR": "0XC9" 227 | "KEYPAD OR": "0XCA" 228 | "KEYPAD COLON": "0XCB" 229 | "KEYPAD HASH": "0XCC" 230 | "KEYPAD SPACE": "0XCD" 231 | "KEYPAD AT": "0XCE" 232 | "KEYPAD EXCLAMATION": "0XCF" 233 | "KEYPAD MEMORY STORE": "0XD0" 234 | "KEYPAD MEMORY RECALL": "0XD1" 235 | "KEYPAD MEMORY CLEAR": "0XD2" 236 | "KEYPAD MEMORY ADD": "0XD3" 237 | "KEYPAD MEMORY SUBTRACT": "0XD4" 238 | "KEYPAD MEMORY MULTIPLY": "0XD5" 239 | "KEYPAD MEMORY DIVIDE": "0XD6" 240 | "KEYPAD PLUS MINUS": "0XD7" 241 | "KEYPAD CLEAR": "0XD8" 242 | "KEYPAD CLEAR ENTRY": "0XD9" 243 | "KEYPAD BINARY": "0XDA" 244 | "KEYPAD OCTAL": "0XDB" 245 | "KEYPAD DECIMAL": "0XDC" 246 | "KEYPAD HEXIDECIMAL": "0XDD" 247 | "LEFT CONTROL": "0XE0" 248 | "LEFT SHIFT": "0XE1" 249 | "LEFT ALT": "0XE2" 250 | "LEFT GUI": "0XE3" 251 | "RIGHT CONTROL": "0XE4" 252 | "RIGHT SHIFT": "0XE5" 253 | "RIGHT ALT": "0XE6" 254 | "RIGHT GUI": "0XE7" -------------------------------------------------------------------------------- /Client/Data/keyboard_hid2code.yaml: -------------------------------------------------------------------------------- 1 | 0x35: 41 #text: ` 2 | 0x1E: 2 #text: 1 3 | 0x1F: 3 #text: 2 4 | 0x20: 4 #text: 3 5 | 0x21: 5 #text: 4 6 | 0x22: 6 #text: 5 7 | 0x23: 7 #text: 6 8 | 0x24: 8 #text: 7 9 | 0x25: 9 #text: 8 10 | 0x26: 10 #text: 9 11 | 0x27: 11 #text: 0 12 | 0x2d: 12 #text: - 13 | 0x2e: 13 #text: = 14 | 0x2f: 26 #text: [ 15 | 0x30: 27 #text: ] 16 | 0x31: 43 #text: \ 17 | 0x33: 39 #text: ; 18 | 0x34: 40 #text: ' 19 | 0x36: 51 #text: , 20 | 0x37: 52 #text: . 21 | 0x38: 53 #text: / -------------------------------------------------------------------------------- /Client/Data/keyboard_qt2hid.yaml: -------------------------------------------------------------------------------- 1 | #HID:Qt_Key 2 | 0x04: 0x41 #"A", 3 | 0x05: 0x42 #"B", 4 | 0x06: 0x43 #"C", 5 | 0x07: 0x44 #"D", 6 | 0x08: 0x45 #"E", 7 | 0x09: 0x46 #"F", 8 | 0x0A: 0x47 #"G", 9 | 0x0B: 0x48 #"H", 10 | 0x0C: 0x49 #"I", 11 | 0x0D: 0x4a #"J", 12 | 0x0E: 0x4b #"K", 13 | 0x0F: 0x4c #"L", 14 | 0x10: 0x4d #"M", 15 | 0x11: 0x4e #"N", 16 | 0x12: 0x4f #"O", 17 | 0x13: 0x50 #"P", 18 | 0x14: 0x51 #"Q", 19 | 0x15: 0x52 #"R", 20 | 0x16: 0x53 #"S", 21 | 0x17: 0x54 #"T", 22 | 0x18: 0x55 #"U", 23 | 0x19: 0x56 #"V", 24 | 0x1A: 0x57 #"W", 25 | 0x1B: 0x58 #"X", 26 | 0x1C: 0x59 #"Y", 27 | 0x1D: 0x5a #"Z", 28 | 0x1E: 0x31 #"1", 29 | 0x1F: 0x32 #"2", 30 | 0x20: 0x33 #"3", 31 | 0x21: 0x34 #"4", 32 | 0x22: 0x35 #"5", 33 | 0x23: 0x36 #"6", 34 | 0x24: 0x37 #"7", 35 | 0x25: 0x38 #"8", 36 | 0x26: 0x39 #"9", 37 | 0x27: 0x30 #"0", 38 | 0x28: 0x01000004 #"ENTER", 39 | 0x29: 0x01000000 #"ESC", 40 | 0x2A: 0x01000003 #"BACKSPACE", 41 | 0x2B: 0x01000001 #"TAB", 42 | 0x2C: 0x01000003 #"SPACE", 43 | 0x3A: 0x01000030 #"F1", 44 | 0x3B: 0x01000031 #"F2", 45 | 0x3C: 0x01000032 #"F3", 46 | 0x3D: 0x01000033 #"F4", 47 | 0x3E: 0x01000034 #"F5", 48 | 0x3F: 0x01000035 #"F6", 49 | 0x40: 0x01000036 #"F7", 50 | 0x41: 0x01000037 #"F8", 51 | 0x42: 0x01000038 #"F9", 52 | 0x43: 0x01000039 #"F10", 53 | 0x44: 0x0100003a #"F11", 54 | 0x45: 0x0100003b #"F12", 55 | 0x2D: 0x2d #"Minus", 56 | 0x2E: 0x3d #"Equal", 57 | 0x2F: 0x5b #"Left Bracket", 58 | 0x30: 0x5d #"Right Bracket", 59 | 0x31: 0x5c #"Backslash", 60 | 0x32: 0x23 #"Number", 61 | 0x33: 0x3b #"`", 62 | 0x34: 0x27 #"'", 63 | 0x35: 0x60 #"Backtick", 64 | 0x36: 0x2c #"Comma", 65 | 0x37: 0x2e #"Period", 66 | 0x38: 0x2f #"Slash", 67 | 0x39: 0x01000024 #"CapsLock", 68 | 0x46: 0x01000009 #"PrintScreen", 69 | 0x47: 0x01000026 #"ScrollLock", 70 | 0x48: 0x01000008 #"Pause", 71 | 0x49: 0x01000006 #"Insert", 72 | 0x4A: 0x01000010 #"Home", 73 | 0x4B: 0x01000016 #"PgUp", 74 | 0x4C: 0x01000007 #"Del", 75 | 0x4D: 0x01000011 #"End", 76 | 0x4E: 0x01000017 #"PgDown", 77 | 0x4F: 0x01000014 #"Right", 78 | 0x50: 0x01000012 #"Left", 79 | 0x51: 0x01000015 #"Down", 80 | 0x52: 0x01000013 #"Up", 81 | 0x53: 0x01000025 #"NumLock", 82 | 0xE0: 0x01000021 #"Left Control", 83 | 0xE1: 0x01000020 #"Left Shift", 84 | 0xE2: 0x01000023 #"Left Alt", 85 | 0xE3: 0x01000022 #"Left GUI", 86 | 87 | #0xE4: 0x01000021 #"Right Control", 88 | #0xE5: 0x01000020 #"Right Shift", 89 | #0xE6: 0x01000023 #"Right Alt", 90 | #0xE7: 0x01000022 #"Right GUI" 91 | 92 | #0X54: , #"Keypad Slash", 93 | #0X55: , #"Keypad Asterisk", 94 | #0X56: , #"Keypad Minus", 95 | #0X57: , #"Keypad Plus", 96 | #0X58: , #"Keypad Enter", 97 | #0X59: , #"Keypad 1", 98 | #0X5A: , #"Keypad 2", 99 | #0X5B: , #"Keypad 3", 100 | #0X5C: , #"Keypad 4", 101 | #0X5D: , #"Keypad 5", 102 | #0X5E: , #"Keypad 6", 103 | #0X5F: , #"Keypad 7", 104 | #0X60: , #"Keypad 8", 105 | #0X61: , #"Keypad 9", 106 | #0X62: , #"Keypad 0", 107 | 108 | -------------------------------------------------------------------------------- /Client/Data/keycode.yaml: -------------------------------------------------------------------------------- 1 | "ESCAPE": 0x01 2 | "1": 0x02 3 | "2": 0x03 4 | "3": 0x04 5 | "4": 0x05 6 | "5": 0x06 7 | "6": 0x07 8 | "7": 0x08 9 | "8": 0x09 10 | "9": 0x0A 11 | "0": 0x0B 12 | "MINUS": 0x0C # - on main keyboard 13 | "EQUALS": 0x0D 14 | "BACK": 0x0E # backspace 15 | "TAB": 0x0F 16 | "Q": 0x10 17 | "W": 0x11 18 | "E": 0x12 19 | "R": 0x13 20 | "T": 0x14 21 | "Y": 0x15 22 | "U": 0x16 23 | "I": 0x17 24 | "O": 0x18 25 | "P": 0x19 26 | "LBRACKET": 0x1A 27 | "RBRACKET": 0x1B 28 | "RETURN": 0x1C # Enter on main keyboard 29 | "LCONTROL": 0x1D 30 | "A": 0x1E 31 | "S": 0x1F 32 | "D": 0x20 33 | "F": 0x21 34 | "G": 0x22 35 | "H": 0x23 36 | "J": 0x24 37 | "K": 0x25 38 | "L": 0x26 39 | "SEMICOLON": 0x27 40 | "APOSTROPHE": 0x28 41 | "GRAVE": 0x29 # accent grave 42 | "LSHIFT": 0x2A 43 | "BACKSLASH": 0x2B 44 | "Z": 0x2C 45 | "X": 0x2D 46 | "C": 0x2E 47 | "V": 0x2F 48 | "B": 0x30 49 | "N": 0x31 50 | "M": 0x32 51 | "COMMA": 0x33 52 | "PERIOD": 0x34 # . on main keyboard 53 | "SLASH": 0x35 # / on main keyboard 54 | "RSHIFT": 0x36 55 | "MULTIPLY": 0x37 # * on numeric keypad 56 | "LMENU": 0x38 # left Alt 57 | "SPACE": 0x39 58 | "CAPITAL": 0x3A 59 | "F1": 0x3B 60 | "F2": 0x3C 61 | "F3": 0x3D 62 | "F4": 0x3E 63 | "F5": 0x3F 64 | "F6": 0x40 65 | "F7": 0x41 66 | "F8": 0x42 67 | "F9": 0x43 68 | "F10": 0x44 69 | "NUMLOCK": 0x45 70 | "SCROLL": 0x46 # Scroll Lock 71 | "NUMPAD7": 0x47 72 | "NUMPAD8": 0x48 73 | "NUMPAD9": 0x49 74 | "SUBTRACT": 0x4A # - on numeric keypad 75 | "NUMPAD4": 0x4B 76 | "NUMPAD5": 0x4C 77 | "NUMPAD6": 0x4D 78 | "ADD": 0x4E # + on numeric keypad 79 | "NUMPAD1": 0x4F 80 | "NUMPAD2": 0x50 81 | "NUMPAD3": 0x51 82 | "NUMPAD0": 0x52 83 | "DECIMAL": 0x53 # . on numeric keypad 84 | "OEM_102": 0x56 # <> or \| on RT 102-key keyboard (Non-U.S.) 85 | "F11": 0x57 86 | "F12": 0x58 87 | "F13": 0x64 #(NEC PC98) 88 | "F14": 0x65 #(NEC PC98) 89 | "F15": 0x66 #(NEC PC98) 90 | "KANA": 0x70 # (Japanese keyboard) 91 | "ABNT_C1": 0x73 # /? on Brazilian keyboard 92 | "CONVERT": 0x79 # (Japanese keyboard) 93 | "NOCONVERT": 0x7B # (Japanese keyboard) 94 | "YEN": 0x7D # (Japanese keyboard) 95 | "ABNT_C2": 0x7E # Numpad . on Brazilian keyboard 96 | "NUMPADEQUALS": 0x8D # = on numeric keypad (NEC PC98) 97 | "PREVTRACK": 0x90 # Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) 98 | "AT": 0x91 #(NEC PC98) 99 | "COLON": 0x92 #(NEC PC98) 100 | "UNDERLINE": 0x93 #(NEC PC98) 101 | "KANJI": 0x94 # (Japanese keyboard) 102 | "STOP": 0x95 #(NEC PC98) 103 | "AX": 0x96 #(Japan AX) 104 | "UNLABELED": 0x97 #(J3100) 105 | "NEXTTRACK": 0x99 # Next Track 106 | "NUMPADENTER": 0x9C # Enter on numeric keypad 107 | "RCONTROL": 0x9D 108 | "MUTE": 0xA0 # Mute 109 | "CALCULATOR": 0xA1 # Calculator 110 | "PLAYPAUSE": 0xA2 # Play / Pause 111 | "MEDIASTOP": 0xA4 # Media Stop 112 | "VOLUMEDOWN": 0xAE # Volume - 113 | "VOLUMEUP": 0xB0 # Volume + 114 | "WEBHOME": 0xB2 # Web home 115 | "NUMPADCOMMA": 0xB3 # , on numeric keypad (NEC PC98) 116 | "DIVIDE": 0xB5 # / on numeric keypad 117 | "SYSRQ": 0xB7 118 | "RMENU": 0xB8 # right Alt 119 | "PAUSE": 0xC5 # Pause 120 | "HOME": 0xC7 # Home on arrow keypad 121 | "UP": 0xC8 # UpArrow on arrow keypad 122 | "PRIOR": 0xC9 # PgUp on arrow keypad 123 | "LEFT": 0xCB # LeftArrow on arrow keypad 124 | "RIGHT": 0xCD # RightArrow on arrow keypad 125 | "END": 0xCF # End on arrow keypad 126 | "DOWN": 0xD0 # DownArrow on arrow keypad 127 | "NEXT": 0xD1 # PgDn on arrow keypad 128 | "INSERT": 0xD2 # Insert on arrow keypad 129 | "DELETE": 0xD3 # Delete on arrow keypad 130 | "LWIN": 0xDB # Left Windows key 131 | "RWIN": 0xDC # Right Windows key 132 | "APPS": 0xDD # AppMenu key 133 | "POWER": 0xDE # System Power 134 | "SLEEP": 0xDF # System Sleep 135 | "WAKE": 0xE3 # System Wake 136 | "WEBSEARCH": 0xE5 # Web Search 137 | "WEBFAVORITES": 0xE6 # Web Favorites 138 | "WEBREFRESH": 0xE7 # Web Refresh 139 | "WEBSTOP": 0xE8 # Web Stop 140 | "WEBFORWARD": 0xE9 # Web Forward 141 | "WEBBACK": 0xEA # Web Back 142 | "MYCOMPUTER": 0xEB # My Computer 143 | "MAIL": 0xEC # Mail 144 | "MEDIASELECT": 0xED # Media Select 145 | -------------------------------------------------------------------------------- /Client/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys, yaml, re 3 | import time 4 | 5 | from PyQt5 import QtGui 6 | from PyQt5.QtCore import Qt 7 | from PyQt5.QtGui import QPixmap, QIcon, QCursor 8 | from PyQt5.QtMultimedia import QCameraInfo, QCamera, QCameraViewfinderSettings 9 | from PyQt5.QtMultimediaWidgets import QCameraViewfinder 10 | from PyQt5.QtWidgets import QApplication, QMainWindow, QErrorMessage, QLabel, QWidget, QDesktopWidget 11 | from PyQt5.uic import loadUi 12 | 13 | from module import hid_def 14 | 15 | from ui import main_ui 16 | 17 | buffer = [4, 1, 0, 0, 0, 0, 0, 0, 0, 0] 18 | mouse_buffer = [4, 3, 0, 0, 0, 0] 19 | mouse = [0, 0] # x1,y1 20 | 21 | 22 | class MyMainWindow(QMainWindow, main_ui.Ui_MainWindow): 23 | def __init__(self, parent=None): 24 | super(MyMainWindow, self).__init__(parent) 25 | self.setupUi(self) 26 | 27 | self.camera = None 28 | 29 | # 采集卡设备设置窗口 30 | self.device_setup_dialog = loadUi("ui/device_setup_dialog.ui") 31 | self.shortcut_key_dialog = loadUi("ui/shortcut_key.ui") 32 | 33 | # 导入外部数据 34 | with open("./Data/keyboard_qt2hid.yaml", 'r') as load_f: 35 | self.keyboard_qt2hid = yaml.safe_load(load_f) 36 | with open("./Data/keyboard_hid2code.yaml", 'r') as load_f: 37 | self.keyboard_hid2code = yaml.safe_load(load_f) 38 | with open("./Data/keyboard.yaml", 'r') as load_f: 39 | self.keyboard_hidcode = yaml.safe_load(load_f) 40 | with open("./Data/config.yaml", 'r') as load_f: 41 | self.configfile = yaml.safe_load(load_f) 42 | # 加载配置文件 43 | self.camera_config = self.configfile['camera_config'] 44 | self.config = self.configfile['config'] 45 | self.status = {'fullscreen': False, 'mouse_capture': False, 'mouse_jumpframe': 0} 46 | 47 | # 窗口图标 48 | self.setWindowIcon(QtGui.QIcon('ui/images/24/monitor-multiple.png')) 49 | self.device_setup_dialog.setWindowIcon(QtGui.QIcon('ui/images/24/import.png')) 50 | self.shortcut_key_dialog.setWindowIcon(QtGui.QIcon('ui/images/24/ikeyboard-settings-outline.png')) 51 | 52 | # 状态栏图标 53 | self.statusbar_lable1 = QLabel() 54 | self.statusbar_lable2 = QLabel() 55 | self.statusbar_lable3 = QLabel() 56 | self.statusbar_lable4 = QLabel() 57 | self.statusbar_lable1.setText("CTRL") 58 | self.statusbar_lable2.setText("SHIFT") 59 | self.statusbar_lable3.setText("ALT") 60 | self.statusbar_lable4.setText("META") 61 | self.statusbar_lable1.setStyleSheet('color: grey') 62 | self.statusbar_lable2.setStyleSheet('color: grey') 63 | self.statusbar_lable3.setStyleSheet('color: grey') 64 | self.statusbar_lable4.setStyleSheet('color: grey') 65 | self.statusBar().addPermanentWidget(self.statusbar_lable1) 66 | self.statusBar().addPermanentWidget(self.statusbar_lable2) 67 | self.statusBar().addPermanentWidget(self.statusbar_lable3) 68 | self.statusBar().addPermanentWidget(self.statusbar_lable4) 69 | 70 | self.statusbar_icon1 = QLabel() 71 | self.statusbar_icon2 = QLabel() 72 | self.statusbar_icon1.setPixmap(QPixmap('ui/images/24/video-off.png')) 73 | self.statusbar_icon2.setPixmap(QPixmap('ui/images/24/keyboard-off.png')) 74 | self.statusBar().addPermanentWidget(self.statusbar_icon1) 75 | self.statusBar().addPermanentWidget(self.statusbar_icon2) 76 | self.statusbar_icon1.setToolTip('Video capture card connected') 77 | self.statusbar_icon2.setToolTip('Keyboard Mouse device connected') 78 | 79 | # 菜单栏图标 80 | self.action_video_devices.setIcon(QIcon('ui/images/24/import.png')) 81 | self.action_video_device_connect.setIcon(QIcon('ui/images/24/video.png')) 82 | self.action_video_device_disconnect.setIcon(QIcon('ui/images/24/video-off.png')) 83 | self.actionMinimize.setIcon(QIcon('ui/images/24/window-minimize.png')) 84 | self.actionexit.setIcon(QIcon('ui/images/24/window-close.png')) 85 | self.actionReload_Key_Mouse.setIcon(QIcon('ui/images/24/reload.png')) 86 | self.actionfullscreen.setIcon(QIcon('ui/images/24/fullscreen.png')) 87 | self.actionResize_window.setIcon(QIcon('ui/images/24/resize.png')) 88 | self.actionResetKeyboard.setIcon(QIcon('ui/images/24/reload.png')) 89 | self.actionResetMouse.setIcon(QIcon('ui/images/24/reload.png')) 90 | self.menuShortcut_key.setIcon(QIcon('ui/images/24/keyboard-outline.png')) 91 | self.actionCustomKey.setIcon(QIcon('ui/images/24/keyboard-settings-outline.png')) 92 | self.actionCapture_mouse.setIcon(QIcon('ui/images/24/mouse.png')) 93 | self.actionRelease_mouse.setIcon(QIcon('ui/images/24/mouse-off.png')) 94 | self.actionOn_screen_Keyboard.setIcon(QIcon('ui/images/24/keyboard-variant.png')) 95 | self.actionCalculator.setIcon(QIcon('ui/images/24/calculator.png')) 96 | self.actionSnippingTool.setIcon(QIcon('ui/images/24/monitor-screenshot.png')) 97 | self.actionNotepad.setIcon(QIcon('ui/images/24/notebook-edit.png')) 98 | 99 | # 遍历相机设备 100 | cameras = QCameraInfo() 101 | for i in cameras.availableCameras(): 102 | self.camera_config['device_name'].append(i.description()) 103 | print(self.camera_config['device_name']) 104 | 105 | # 初始化相机 106 | self.online_webcams = QCameraInfo.availableCameras() 107 | if self.online_webcams: 108 | self.camerafinder = QCameraViewfinder() 109 | self.setCentralWidget(self.camerafinder) 110 | # set the default webcam. 111 | self.get_webcam(self.camera_config['device_No'], self.camera_config['resolution_X'], 112 | self.camera_config['resolution_Y']) 113 | self.camerafinder.show() 114 | 115 | # 快捷键菜单设置快捷键名称 116 | self.actionq1.setText(self.configfile['shortcut_key']['shortcut_key_name'][0]) 117 | self.actionq2.setText(self.configfile['shortcut_key']['shortcut_key_name'][1]) 118 | self.actionq3.setText(self.configfile['shortcut_key']['shortcut_key_name'][2]) 119 | self.actionq4.setText(self.configfile['shortcut_key']['shortcut_key_name'][3]) 120 | self.actionq5.setText(self.configfile['shortcut_key']['shortcut_key_name'][4]) 121 | self.actionq6.setText(self.configfile['shortcut_key']['shortcut_key_name'][5]) 122 | self.actionq7.setText(self.configfile['shortcut_key']['shortcut_key_name'][6]) 123 | self.actionq_8.setText(self.configfile['shortcut_key']['shortcut_key_name'][7]) 124 | self.actionq9.setText(self.configfile['shortcut_key']['shortcut_key_name'][8]) 125 | self.actionq10.setText(self.configfile['shortcut_key']['shortcut_key_name'][9]) 126 | 127 | # 按键绑定 128 | self.action_video_device_connect.triggered.connect(lambda: self.set_webcam(True)) 129 | self.action_video_device_disconnect.triggered.connect(lambda: self.set_webcam(False)) 130 | self.action_video_devices.triggered.connect(self.device_config) 131 | self.actionCustomKey.triggered.connect(self.shortcut_key_func) 132 | self.actionReload_Key_Mouse.triggered.connect(self.load_hid_device) 133 | self.actionMinimize.triggered.connect(self.window_minimized) 134 | self.actionexit.triggered.connect(sys.exit) 135 | self.device_setup_dialog.comboBox.currentIndexChanged.connect(self.update_device_setup_resolutions) 136 | self.actionfullscreen.triggered.connect(self.fullscreen_func) 137 | self.actionResize_window.triggered.connect(self.resize_window_func) 138 | self.actionRelease_mouse.triggered.connect(self.release_mouse) 139 | self.actionCapture_mouse.triggered.connect(self.capture_mouse) 140 | self.actionResetKeyboard.triggered.connect(lambda: self.reset_keymouse(1)) 141 | self.actionResetMouse.triggered.connect(lambda: self.reset_keymouse(3)) 142 | 143 | self.actionq1.triggered.connect(lambda: self.shortcut_key_action(0)) 144 | self.actionq2.triggered.connect(lambda: self.shortcut_key_action(1)) 145 | self.actionq3.triggered.connect(lambda: self.shortcut_key_action(2)) 146 | self.actionq4.triggered.connect(lambda: self.shortcut_key_action(3)) 147 | self.actionq5.triggered.connect(lambda: self.shortcut_key_action(4)) 148 | self.actionq6.triggered.connect(lambda: self.shortcut_key_action(5)) 149 | self.actionq7.triggered.connect(lambda: self.shortcut_key_action(6)) 150 | self.actionq_8.triggered.connect(lambda: self.shortcut_key_action(7)) 151 | self.actionq9.triggered.connect(lambda: self.shortcut_key_action(8)) 152 | self.actionq10.triggered.connect(lambda: self.shortcut_key_action(9)) 153 | 154 | self.actionOn_screen_Keyboard.triggered.connect(lambda: self.tools_actions(0)) 155 | self.actionCalculator.triggered.connect(lambda: self.tools_actions(1)) 156 | self.actionSnippingTool.triggered.connect(lambda: self.tools_actions(2)) 157 | self.actionNotepad.triggered.connect(lambda: self.tools_actions(3)) 158 | 159 | # 初始化hid设备 160 | self.load_hid_device() 161 | # 创建指针对象 162 | self.cursor = QCursor() 163 | 164 | # 弹出采集卡设备设置窗口,并打开采集卡设备 165 | def device_config(self): 166 | self.device_setup_dialog.comboBox.clear() 167 | 168 | # 遍历相机设备 169 | cameras = QCameraInfo() 170 | for i in cameras.availableCameras(): 171 | self.camera_config['device_name'].append(i.description()) 172 | self.device_setup_dialog.comboBox.addItem(i.description()) 173 | 174 | # 将设备设置为字典camera_config中指定的设备 175 | self.device_setup_dialog.comboBox.setCurrentIndex(self.camera_config['device_No']) 176 | resolution_str = str(self.camera_config['resolution_X']) + 'x' + str(self.camera_config['resolution_Y']) 177 | print(resolution_str) 178 | self.device_setup_dialog.comboBox_2.setCurrentText(resolution_str) 179 | 180 | # 如果选择设备 181 | info = self.device_setup_dialog.exec() 182 | 183 | if info == 1: 184 | print(self.device_setup_dialog.comboBox.currentIndex()) 185 | print(self.device_setup_dialog.comboBox_2.currentText().split('x')) 186 | 187 | self.camera_config['device_No'] = self.device_setup_dialog.comboBox.currentIndex() 188 | self.camera_config['resolution_X'] = int(self.device_setup_dialog.comboBox_2.currentText().split('x')[0]) 189 | self.camera_config['resolution_Y'] = int(self.device_setup_dialog.comboBox_2.currentText().split('x')[1]) 190 | 191 | print(self.camera_config) 192 | 193 | try: 194 | self.set_webcam(True) 195 | self.resize_window_func() 196 | except Exception as e: 197 | print(e) 198 | 199 | # 获取采集卡分辨率 200 | def update_device_setup_resolutions(self): 201 | self.device_setup_dialog.comboBox_2.clear() 202 | camera_info = QCamera(QCameraInfo.availableCameras()[self.device_setup_dialog.comboBox.currentIndex()]) 203 | camera_info.load() 204 | for i in camera_info.supportedViewfinderResolutions(): 205 | resolutions_str = str(i.width()) + 'x' + str(i.height()) 206 | self.device_setup_dialog.comboBox_2.addItem(resolutions_str) 207 | print(camera_info.supportedViewfinderResolutions()) 208 | camera_info.unload() 209 | 210 | # 初始化指定配置视频设备 211 | def get_webcam(self, i, x, y): 212 | self.camera = QCamera(self.online_webcams[i]) 213 | # self.camera = QCamera() 214 | self.camera.setViewfinder(self.camerafinder) 215 | self.camera.setCaptureMode(QCamera.CaptureStillImage) 216 | self.camera.error.connect(lambda: self.alert(self.camera.errorString())) 217 | view_finder_settings = QCameraViewfinderSettings() 218 | view_finder_settings.setResolution(x, y) 219 | self.camera.setViewfinderSettings(view_finder_settings) 220 | 221 | if not self.status['fullscreen']: 222 | self.resize(x, y + 60) 223 | # self.camera.start() 224 | 225 | # 视频设备错误提示 226 | def alert(self, s): 227 | """ 228 | This handle errors and displaying alerts. 229 | """ 230 | err = QErrorMessage(self) 231 | err.showMessage(s) 232 | self.device_event_handle("video_error") 233 | print(s) 234 | print(err) 235 | 236 | # 启用和禁用视频设备 237 | def set_webcam(self, s): 238 | if s: 239 | self.device_event_handle("video_ok") 240 | self.get_webcam(self.camera_config['device_No'], self.camera_config['resolution_X'], 241 | self.camera_config['resolution_Y']) 242 | self.camera.start() 243 | # print("[debug]video device connected") 244 | self.camerafinder.setMouseTracking(True) 245 | # self.camera_config['connected'] = True 246 | 247 | else: 248 | self.device_event_handle("video_close") 249 | self.camera.stop() 250 | # print("video device disconnect") 251 | self.camerafinder.setMouseTracking(False) 252 | # self.camera_config['connected'] = False 253 | 254 | # 当相机关闭时显示图标 255 | # try: 256 | # self.setCentralWidget(self.image_label) 257 | # self.image_label.setPixmap(self.camera_off_icon) 258 | # self.image_label.setAlignment(Qt.AlignCenter) 259 | # except Exception as e: 260 | # print(e) 261 | # sys.exit(app.exec_()) 262 | 263 | # 鼠标按下事件 264 | def mousePressEvent(self, event): 265 | if not self.status['mouse_capture']: 266 | return 267 | if event.button() == Qt.LeftButton: 268 | mouse_buffer[2] = mouse_buffer[2] | 1 269 | elif event.button() == Qt.RightButton: 270 | mouse_buffer[2] = mouse_buffer[2] | 2 271 | elif event.button() == Qt.MidButton: 272 | mouse_buffer[2] = mouse_buffer[2] | 4 273 | 274 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, mouse_buffer, False) 275 | if hidinfo == 1 or hidinfo == 4: 276 | self.device_event_handle("hid_error") 277 | 278 | # 鼠标松开事件 279 | def mouseReleaseEvent(self, event): 280 | if not self.status['mouse_capture']: 281 | return 282 | if event.button() == Qt.LeftButton and mouse_buffer[2] & 1: 283 | mouse_buffer[2] = mouse_buffer[2] ^ 1 284 | elif event.button() == Qt.RightButton and mouse_buffer[2] & 2: 285 | mouse_buffer[2] = mouse_buffer[2] ^ 2 286 | elif event.button() == Qt.MidButton and mouse_buffer[2] & 4: 287 | mouse_buffer[2] = mouse_buffer[2] ^ 4 288 | 289 | if mouse_buffer[2] < 0 or mouse_buffer[2] > 7: 290 | mouse_buffer[2] = 0 291 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, mouse_buffer, False) 292 | if hidinfo == 1 or hidinfo == 4: 293 | self.device_event_handle("hid_error") 294 | 295 | # 鼠标滚动事件 296 | def wheelEvent(self, event): 297 | if not self.status['mouse_capture']: 298 | return 299 | if event.angleDelta().y() == 120: 300 | mouse_buffer[5] = 0x01 301 | elif event.angleDelta().y() == -120: 302 | mouse_buffer[5] = 0xff 303 | else: 304 | mouse_buffer[5] = 0 305 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, mouse_buffer, False) 306 | if hidinfo == 1 or hidinfo == 4: 307 | self.device_event_handle("hid_error") 308 | mouse_buffer[5] = 0 309 | 310 | # 鼠标移动事件 311 | def mouseMoveEvent(self, event): 312 | if not self.status['mouse_capture']: 313 | return 314 | if self.status['mouse_jumpframe'] != 0: 315 | print("jump") 316 | self.status['mouse_jumpframe'] -= 1 317 | return 318 | # l1 = self.x() + 1 319 | # l2 = self.y() + 30 320 | # l3 = l1 + self.camerafinder.geometry().width() 321 | # l4 = l2 + self.camerafinder.geometry().height() + 30 322 | # 限制鼠标移动边界 323 | # win32api.ClipCursor((l1, l2, l3, l4)) 324 | 325 | if event.x() < 100 or event.x() > self.width() - 100: 326 | x = int(self.x() + self.width() / 2) 327 | y = int(self.y() + self.height() / 2) 328 | self.cursor.setPos(x, y) 329 | # win32api.SetCursorPos((x, y)) 330 | self.status['mouse_jumpframe'] = self.config['mouse_jump_frame_count'] 331 | return 332 | if event.y() < 100 or event.y() > self.height() - 100: 333 | x = int(self.x() + self.width() / 2) 334 | y = int(self.y() + self.height() / 2) 335 | self.cursor.setPos(x, y) 336 | # win32api.SetCursorPos((x, y)) 337 | self.status['mouse_jumpframe'] = self.config['mouse_jump_frame_count'] 338 | return 339 | 340 | if mouse[0] == 0 and mouse[1] == 1: 341 | mouse[0] = event.x() 342 | mouse[1] = event.y() 343 | return 344 | else: 345 | mouse_buffer[3] = (event.x() - mouse[0]) & 0xff 346 | mouse_buffer[4] = (event.y() - mouse[1]) & 0xff 347 | 348 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, mouse_buffer, False) 349 | if hidinfo == 1 or hidinfo == 4: 350 | self.device_event_handle("hid_error") 351 | 352 | mouse_buffer[3], mouse_buffer[4] = 0, 0 353 | mouse[0] = event.x() 354 | mouse[1] = event.y() 355 | 356 | # 键盘按下事件 357 | def keyPressEvent(self, event): 358 | if event.nativeScanCode() == 285: 359 | self.release_mouse() 360 | if event.isAutoRepeat(): 361 | return 362 | key = event.key() 363 | mapcode = [k for (k, v) in self.keyboard_qt2hid.items() if v == key] 364 | scancode2hid = [k for (k, v) in self.keyboard_hid2code.items() if v == event.nativeScanCode()] 365 | 366 | if mapcode: 367 | buffer[4] = mapcode[0] 368 | if scancode2hid: 369 | buffer[4] = scancode2hid[0] 370 | 371 | if key == Qt.Key_Control: # Ctrl 键被按下 372 | # print('"Control" pressed') 373 | # self.statusBar().showMessage('"Control" pressed') 374 | buffer[2] = buffer[2] | 1 375 | elif key == Qt.Key_Shift: # Shift 键被按下 376 | # print('"Shift" pressed') 377 | # self.statusBar().showMessage('"Shift" pressed') 378 | buffer[2] = buffer[2] | 2 379 | elif key == Qt.Key_Alt: # Alt 键被按下 380 | # print('"Alt" pressed') 381 | # self.statusBar().showMessage('"Alt" pressed') 382 | buffer[2] = buffer[2] | 4 383 | elif key == Qt.Key_Meta: # Meta 键被按下 384 | # print('"Meta" pressed') 385 | # self.statusBar().showMessage('"Meta" pressed') 386 | buffer[2] = buffer[2] | 8 387 | elif key == Qt.Key_Space: 388 | buffer[4] = 0x2c 389 | 390 | if buffer[2] < 0 or buffer[2] > 16: 391 | buffer[2] = 0 392 | 393 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, buffer, False) 394 | if hidinfo == 1 or hidinfo == 4: 395 | self.device_event_handle("hid_error") 396 | self.shortcut_status(buffer) 397 | 398 | ''' 399 | 其它常用按键: 400 | Qt.Key_Escape,Qt.Key_Tab,Qt.Key_Backspace,Qt.Key_Return,Qt.Key_Enter, 401 | Qt.Key_Insert,Qt.Key_Delet###.Key_9,Qt.Key_Colon,Qt.Key_Semicolon,Qt.Key_Equal 402 | ... 403 | ''' 404 | 405 | # 键盘松开事件 406 | def keyReleaseEvent(self, event): 407 | if event.isAutoRepeat(): 408 | return 409 | 410 | key = event.key() 411 | mapcode = [k for (k, v) in self.keyboard_qt2hid.items() if v == key] 412 | scancode2hid = [k for (k, v) in self.keyboard_hid2code.items() if v == event.nativeScanCode()] 413 | 414 | if mapcode or key == Qt.Key_Space or scancode2hid: 415 | buffer[4] = 0 416 | 417 | if key == Qt.Key_Control: # Ctrl 键被释放 418 | # print('"Control" Release') 419 | # self.statusBar().showMessage('"Control" Release') 420 | if buffer[2] & 1: 421 | buffer[2] = buffer[2] ^ 1 422 | elif key == Qt.Key_Shift: # Shift 键被释放 423 | # print('"Shift" Release') 424 | # self.statusBar().showMessage('"Shift" Release') 425 | if buffer[2] & 2: 426 | buffer[2] = buffer[2] ^ 2 427 | elif key == Qt.Key_Alt: # Alt 键被释放 428 | # print('"Alt" Release') 429 | # self.statusBar().showMessage('"Alt" Release') 430 | if buffer[2] & 4: 431 | buffer[2] = buffer[2] ^ 4 432 | elif key == Qt.Key_Meta: # Meta 键被释放 433 | # print('"Meta" pressed') 434 | # self.statusBar().showMessage('"Meta" Release') 435 | if buffer[2] & 8: 436 | buffer[2] = buffer[2] ^ 8 437 | 438 | # print(buffer_release,buffer) 439 | if buffer[2] < 0 or buffer[2] > 16: 440 | buffer[2] = 0 441 | 442 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, buffer, False) 443 | if hidinfo == 1 or hidinfo == 4: 444 | self.device_event_handle("hid_error") 445 | 446 | self.shortcut_status(buffer) 447 | 448 | # 捕获鼠标功能 449 | def capture_mouse(self): 450 | if self.config['hide_mouse']: 451 | self.camerafinder.setCursor(Qt.BlankCursor) 452 | self.setMouseTracking(True) 453 | self.camerafinder.setMouseTracking(True) 454 | self.status['mouse_capture'] = True 455 | 456 | x = int(self.x() + self.width() / 2) 457 | y = int(self.y() + self.height() / 2) 458 | self.cursor.setPos(x, y) 459 | 460 | self.statusBar().showMessage("Press the RIGHT CTRL key to release the mouse") 461 | 462 | # 释放鼠标功能 463 | def release_mouse(self): 464 | self.camerafinder.setCursor(Qt.BitmapCursor) 465 | self.setMouseTracking(False) 466 | self.camerafinder.setMouseTracking(False) 467 | # win32api.ClipCursor((0, 0, 0, 0)) 468 | self.status['mouse_capture'] = False 469 | 470 | # 全屏幕切换 471 | def fullscreen_func(self): 472 | self.status['fullscreen'] = ~ self.status['fullscreen'] 473 | if self.status['fullscreen']: 474 | self.showFullScreen() 475 | self.actionfullscreen.setChecked(True) 476 | else: 477 | self.showNormal() 478 | self.actionfullscreen.setChecked(False) 479 | 480 | # 通过视频设备分辨率调整窗口大小 481 | def resize_window_func(self): 482 | self.resize(self.camera_config['resolution_X'], self.camera_config['resolution_Y'] + 60) 483 | qr = self.frameGeometry() 484 | cp = QDesktopWidget().availableGeometry().center() 485 | qr.moveCenter(cp) 486 | self.move(qr.topLeft()) 487 | 488 | # 最小化窗口 489 | def window_minimized(self): 490 | self.showMinimized() 491 | 492 | # 重置键盘鼠标 493 | def reset_keymouse(self, s): 494 | if s == 1: # keyboard 495 | for i in range(2, len(buffer)): 496 | buffer[i] = 0 497 | print(buffer) 498 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, buffer, False) 499 | if hidinfo == 1 or hidinfo == 4: 500 | self.device_event_handle("hid_error") 501 | elif hidinfo == 0: 502 | self.device_event_handle("hid_ok") 503 | self.shortcut_status(buffer) 504 | 505 | elif s == 3: # mouse 506 | for i in range(2, len(mouse_buffer)): 507 | mouse_buffer[i] = 0 508 | print(mouse_buffer) 509 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, mouse_buffer, False) 510 | if hidinfo == 1 or hidinfo == 4: 511 | self.device_event_handle("hid_error") 512 | elif hidinfo == 0: 513 | self.device_event_handle("hid_ok") 514 | 515 | # 初始化hid设备 516 | def load_hid_device(self): 517 | hid_code = hid_def.init_usb(hid_def.vendor_id, hid_def.usage_page) 518 | if hid_code == 0: 519 | self.device_event_handle("hid_init_ok") 520 | else: 521 | self.device_event_handle("hid_init_error") 522 | 523 | # 自定义组合键窗口 524 | def shortcut_key_func(self): 525 | self.shortcut_key_dialog.pushButton_ctrl.clicked.connect(lambda: self.shortcut_key_handle(1)) 526 | self.shortcut_key_dialog.pushButton_alt.clicked.connect(lambda: self.shortcut_key_handle(4)) 527 | self.shortcut_key_dialog.pushButton_shift.clicked.connect(lambda: self.shortcut_key_handle(2)) 528 | self.shortcut_key_dialog.pushButton_meta.clicked.connect(lambda: self.shortcut_key_handle(8)) 529 | self.shortcut_key_dialog.pushButton_tab.clicked.connect(lambda: self.shortcut_key_handle(0x2b)) 530 | self.shortcut_key_dialog.pushButton_prtsc.clicked.connect(lambda: self.shortcut_key_handle(0x46)) 531 | 532 | self.shortcut_key_dialog.keySequenceEdit.keySequenceChanged.connect(lambda: self.shortcut_key_handle(0xff)) 533 | 534 | info = self.shortcut_key_dialog.exec() 535 | 536 | if info == 1: 537 | hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, buffer, False) 538 | for i in range(2, len(buffer)): 539 | buffer[i] = 0 540 | time.sleep(0.1) 541 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, buffer, False) 542 | if hidinfo == 1 or hidinfo == 4: 543 | self.device_event_handle("hid_error") 544 | 545 | # 自定义组合键窗口按钮对应功能 546 | def shortcut_key_handle(self, s): 547 | if s == 0xff: 548 | if self.shortcut_key_dialog.keySequenceEdit.keySequence().count() == 0: # 去除多个复合键 549 | keysequence = "" 550 | elif self.shortcut_key_dialog.keySequenceEdit.keySequence().count() == 1: 551 | keysequence = self.shortcut_key_dialog.keySequenceEdit.keySequence().toString() 552 | else: 553 | keysequence = self.shortcut_key_dialog.keySequenceEdit.keySequence().toString().split(",") 554 | self.shortcut_key_dialog.keySequenceEdit.setKeySequence(keysequence[0]) 555 | keysequence = keysequence[0] 556 | if keysequence == '': 557 | return 558 | shift_symbol = [")", "!", "@", "#", "$", "%", "^", "&", "*", "(", "~", "_", "+", "{", "}", "|", ":", "\"", 559 | "<", ">", "?"] 560 | if [s for s in shift_symbol if keysequence in s]: 561 | keysequence = "Shift+" + keysequence 562 | 563 | if len(re.findall("\+", keysequence)) == 0: # 没有匹配到+号,不是组合键 564 | self.shortcut_key_dialog.keySequenceEdit.setKeySequence(keysequence) 565 | else: 566 | if keysequence != '+': 567 | keysequence_list = keysequence.split("+").copy() # 将复合键转换为功能键 568 | if [s for s in keysequence_list if "Ctrl" in s]: 569 | self.shortcut_key_dialog.pushButton_ctrl.setChecked(True) 570 | buffer[2] = buffer[2] | 1 571 | else: 572 | self.shortcut_key_dialog.pushButton_ctrl.setChecked(False) 573 | 574 | if [s for s in keysequence_list if "Alt" in s]: 575 | self.shortcut_key_dialog.pushButton_alt.setChecked(True) 576 | buffer[2] = buffer[2] | 4 577 | else: 578 | self.shortcut_key_dialog.pushButton_alt.setChecked(False) 579 | 580 | if [s for s in keysequence_list if "Shift" in s]: 581 | self.shortcut_key_dialog.pushButton_shift.setChecked(True) 582 | buffer[2] = buffer[2] | 2 583 | else: 584 | self.shortcut_key_dialog.pushButton_shift.setChecked(False) 585 | 586 | self.shortcut_key_dialog.keySequenceEdit.setKeySequence(keysequence_list[-1]) 587 | keysequence = keysequence_list[-1] 588 | try: 589 | mapcode = self.keyboard_hidcode[keysequence.upper()] 590 | except Exception as e: 591 | print(e) 592 | self.shortcut_key_dialog.label.setText("Hid query error") 593 | return 594 | 595 | if not mapcode: 596 | self.shortcut_key_dialog.label.setText("Hid query error") 597 | else: 598 | self.shortcut_key_dialog.label.setText("") 599 | buffer[4] = int(mapcode, 16) # 功能位 600 | 601 | if self.shortcut_key_dialog.pushButton_ctrl.isChecked() and s == 1: 602 | buffer[2] = buffer[2] | 1 603 | elif (self.shortcut_key_dialog.pushButton_ctrl.isChecked() is False) and s == 1: 604 | buffer[2] = buffer[2] & 1 and buffer[2] ^ 1 605 | 606 | if self.shortcut_key_dialog.pushButton_alt.isChecked() and s == 4: 607 | buffer[2] = buffer[2] | 4 608 | elif (self.shortcut_key_dialog.pushButton_alt.isChecked() is False) and s == 4: 609 | buffer[2] = buffer[2] & 4 and buffer[2] ^ 4 610 | 611 | if self.shortcut_key_dialog.pushButton_shift.isChecked() and s == 2: 612 | buffer[2] = buffer[2] | 2 613 | elif (self.shortcut_key_dialog.pushButton_shift.isChecked() is False) and s == 2: 614 | buffer[2] = buffer[2] & 2 and buffer[2] ^ 2 615 | 616 | if self.shortcut_key_dialog.pushButton_meta.isChecked() and s == 8: 617 | buffer[2] = buffer[2] | 8 618 | elif (self.shortcut_key_dialog.pushButton_meta.isChecked() is False) and s == 8: 619 | buffer[2] = buffer[2] & 8 and buffer[2] ^ 8 620 | 621 | if self.shortcut_key_dialog.pushButton_tab.isChecked() and s == 0x2b: 622 | buffer[8] = 0x2b 623 | elif (self.shortcut_key_dialog.pushButton_tab.isChecked() is False) and s == 0x2b: 624 | buffer[8] = 0 625 | 626 | if self.shortcut_key_dialog.pushButton_prtsc.isChecked() and s == 0x46: 627 | buffer[9] = 0x46 628 | elif (self.shortcut_key_dialog.pushButton_prtsc.isChecked() is False) and s == 0x46: 629 | buffer[9] = 0 630 | 631 | # print(buffer) 632 | 633 | # 菜单快捷键发送hid报文 634 | def shortcut_key_action(self, s): 635 | if -1 < s < 10: 636 | hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, 637 | self.configfile['shortcut_key']['shortcut_key_hidcode'][s], False) 638 | time.sleep(0.1) 639 | hidinfo = hid_def.hid_report(hid_def.vendor_id, hid_def.usage_page, [4, 1, 0, 0, 0, 0], False) 640 | if hidinfo == 1 or hidinfo == 4: 641 | self.device_event_handle("hid_error") 642 | 643 | # 设备事件处理 644 | def device_event_handle(self, s): 645 | if s == "hid_error": 646 | self.statusBar().showMessage("Keyboard Mouse connect error") 647 | self.statusbar_icon2.setPixmap(QPixmap('ui/images/24/keyboard-off.png')) 648 | self.statusbar_icon2.setToolTip('Keyboard Mouse disconnect') 649 | elif s == "video_error": 650 | self.statusBar().showMessage("Video device error") 651 | self.statusbar_icon1.setPixmap(QPixmap('ui/images/24/video-off.png')) 652 | self.statusbar_icon1.setToolTip('Video capture card disconnect') 653 | elif s == "video_close": 654 | self.statusBar().showMessage("Video device close") 655 | self.statusbar_icon1.setPixmap(QPixmap('ui/images/24/video-off.png')) 656 | self.statusbar_icon1.setToolTip('Video capture card disconnect') 657 | elif s == "hid_init_error": 658 | self.statusBar().showMessage("Keyboard Mouse initialization error") 659 | self.statusbar_icon2.setPixmap(QPixmap('ui/images/24/keyboard-off.png')) 660 | self.statusbar_icon2.setToolTip('Keyboard Mouse initialization error') 661 | 662 | if s == "hid_init_ok": 663 | self.statusBar().showMessage("Keyboard Mouse initialization done") 664 | self.statusbar_icon2.setPixmap(QPixmap('ui/images/24/keyboard.png')) 665 | self.statusbar_icon2.setToolTip('Keyboard Mouse initialization done') 666 | elif s == "hid_ok": 667 | self.statusbar_icon2.setPixmap(QPixmap('ui/images/24/keyboard.png')) 668 | self.statusBar().addPermanentWidget(self.statusbar_icon2) 669 | self.statusbar_icon2.setToolTip('Keyboard Mouse device connected') 670 | elif s == "video_ok": 671 | self.statusBar().showMessage("Video device connected") 672 | self.statusbar_icon1.setPixmap(QPixmap('ui/images/24/video.png')) 673 | self.statusbar_icon1.setToolTip('Video capture card connected') 674 | 675 | # 菜单小工具 676 | def tools_actions(self, s): 677 | if s == 0: 678 | os.popen('osk') 679 | elif s == 1: 680 | os.popen('calc') 681 | elif s == 2: 682 | os.popen('SnippingTool') 683 | elif s == 3: 684 | os.popen('notepad') 685 | 686 | # 状态栏显示组合键状态 687 | def shortcut_status(self, s): 688 | if s[2] & 1: 689 | self.statusbar_lable1.setStyleSheet('color: black') 690 | else: 691 | self.statusbar_lable1.setStyleSheet('color: grey') 692 | 693 | if s[2] & 2: 694 | self.statusbar_lable2.setStyleSheet('color: black') 695 | else: 696 | self.statusbar_lable2.setStyleSheet('color: grey') 697 | 698 | if s[2] & 4: 699 | self.statusbar_lable3.setStyleSheet('color: black') 700 | else: 701 | self.statusbar_lable3.setStyleSheet('color: grey') 702 | 703 | if s[2] & 8: 704 | self.statusbar_lable4.setStyleSheet('color: black') 705 | else: 706 | self.statusbar_lable4.setStyleSheet('color: grey') 707 | 708 | 709 | if __name__ == '__main__': 710 | app = QApplication(sys.argv) 711 | myWin = MyMainWindow() 712 | myWin.show() 713 | sys.exit(app.exec_()) 714 | -------------------------------------------------------------------------------- /Client/module/hid_def.py: -------------------------------------------------------------------------------- 1 | import hid 2 | import time 3 | 4 | vendor_id = 0x2B86 5 | usage_page = 0xFFB1 6 | product_id = 0xE6E0 7 | 8 | 9 | # 初始化HID设备 10 | def init_usb(vendor_id, usage_page): 11 | global h 12 | h = hid.device() 13 | hid_enumerate = hid.enumerate() 14 | device_path = 0 15 | for i in range(len(hid_enumerate)): 16 | if (hid_enumerate[i]['usage_page'] == usage_page and hid_enumerate[i]['vendor_id'] == vendor_id): 17 | # if (hid_enumerate[i]['usage_page'] == usage_page and hid_enumerate[i]['vendor_id'] == vendor_id and hid_enumerate[i]['product_id'] == product_id ): 18 | device_path = hid_enumerate[i]['path'] 19 | if (device_path == 0): return "Device not found" 20 | h.open_path(device_path) 21 | h.set_nonblocking(1) # enable non-blocking mode 22 | return 0 23 | 24 | 25 | # 读写HID设备 26 | def hid_report(vendor_id, usage_page, buffer, rw_mode=True): 27 | print("<", buffer) 28 | try: 29 | h.write(buffer) 30 | except (OSError, ValueError): 31 | print("写入设备错误") 32 | return 1 33 | except NameError: 34 | print("未初始化设备") 35 | return 4 36 | if rw_mode: # 读取回复 37 | time_start = time.time() 38 | while 1: 39 | try: 40 | d = h.read(64) 41 | except (OSError, ValueError): 42 | print("读取数据错误") 43 | return 2 44 | if d: 45 | print(">", d) 46 | break 47 | if time.time() - time_start > 3: 48 | print("超时未回应") 49 | d = 3 50 | break 51 | else: 52 | d = 0 53 | return d 54 | -------------------------------------------------------------------------------- /Client/requirements.txt: -------------------------------------------------------------------------------- 1 | click==7.1.2 2 | hidapi==0.12.0.post2 3 | Nuitka==1.1.7 4 | PyQt5==5.15.4 5 | pyqt5-plugins==5.15.4.2.2 6 | PyQt5-Qt5==5.15.2 7 | PyQt5-sip==12.11.0 8 | PyQt5-stubs==5.15.6.0 9 | pyqt5-tools==5.15.4.3.2 10 | python-dotenv==0.21.0 11 | pywin32==304 12 | PyYAML==6.0 13 | qt5-applications==5.15.2.2.2 14 | qt5-tools==5.15.2.1.2 15 | -------------------------------------------------------------------------------- /Client/simple_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from flask import Flask, request 4 | from flask import jsonify 5 | 6 | import hid 7 | import time 8 | 9 | vendor_id_s = 0x2b86 10 | usage_page_s = 0xffb1 11 | # vendor_id = 11142 12 | # usage_page = 65457 13 | 14 | 15 | def init_usb(vendor_id, usage_page): 16 | global h 17 | h = hid.device() 18 | hid_enumerate = hid.enumerate() 19 | device_path = 0 20 | # print(hid_enumerate) 21 | for i in range(len(hid_enumerate)): 22 | if (hid_enumerate[i]['usage_page'] == usage_page and hid_enumerate[i]['vendor_id'] == vendor_id): 23 | device_path = hid_enumerate[i]['path'] 24 | if (device_path == 0): return "Device not found" 25 | h.open_path(device_path) 26 | h.set_nonblocking(1) # enable non-blocking mode 27 | 28 | 29 | def read_report(vendor_id, usage_page, buffer): 30 | print("------") 31 | print("<", buffer) 32 | time_start = time.time() 33 | try: 34 | h.write(buffer) 35 | except (OSError, ValueError): 36 | print("写入设备错误") 37 | return 1 38 | except NameError: 39 | print("未初始化设备") 40 | return 4 41 | while 1: 42 | try: 43 | d = h.read(64) 44 | except (OSError, ValueError): 45 | print("读取数据错误") 46 | return 2 47 | if d: 48 | print(">", d) 49 | break 50 | if time.time() - time_start > 3: 51 | d = 3 52 | break 53 | print("------") 54 | return d 55 | 56 | 57 | # 新建一个应用 58 | app = Flask(__name__) 59 | 60 | 61 | # 路由(请求处理) 62 | @app.route('/', methods=['GET']) 63 | def hello(): 64 | return jsonify({"msg": "hello world!"}) 65 | 66 | 67 | # 初始化设备 68 | @app.route('/device_init', methods=['POST']) 69 | def device_init(): 70 | global vendor_id_s 71 | global usage_page_s 72 | print(request.data.decode()) 73 | data = json.loads(request.data.decode()) 74 | try: 75 | vendor_id = data['vendor_id'] 76 | usage_page = data['usage_page'] 77 | except KeyError: 78 | vendor_id = vendor_id_s 79 | usage_page = usage_page_s 80 | 81 | # print(vendor_id, usage_page) 82 | report = init_usb(vendor_id, usage_page) 83 | 84 | if report == "Device not found": 85 | return jsonify({"msg": "device not found"}) 86 | else: 87 | vendor_id_s = vendor_id 88 | usage_page_s = usage_page 89 | return jsonify({"msg": "ok"}) 90 | 91 | 92 | # 关闭设备 93 | @app.route('/device_close', methods=['GET']) 94 | def device_close(): 95 | try: 96 | h.close() 97 | except: 98 | return jsonify({"msg": "close error"}) 99 | return jsonify({"msg": "closed"}) 100 | 101 | 102 | # 写入数据 103 | @app.route('/hid_report', methods=['POST']) 104 | def hid_report(): 105 | data = json.loads(request.data.decode()) 106 | try: 107 | buffer = data['report'] 108 | except KeyError: 109 | return jsonify({"msg": "report not found"}) 110 | 111 | try: 112 | vendor_id = data['vendor_id'] 113 | usage_page = data['usage_page'] 114 | except KeyError: 115 | vendor_id = vendor_id_s 116 | usage_page = usage_page_s 117 | 118 | if not isinstance(buffer, list): 119 | return jsonify({"msg": "report is not list. example: [4, 3]"}) 120 | 121 | print(vendor_id, usage_page, buffer) 122 | 123 | report = read_report(vendor_id, usage_page, buffer) 124 | 125 | if report == 1: 126 | return jsonify({"msg": "write error"}) 127 | elif report == 2: 128 | return jsonify({"msg": "read error"}) 129 | elif report == 3: 130 | return jsonify({"msg": "timeout"}) 131 | elif report == 4: 132 | return jsonify({"msg": "check initialized device"}) 133 | else: 134 | return jsonify({"msg": "ok"}, {"report": report}) 135 | 136 | 137 | if __name__ == '__main__': 138 | # 运行参数 139 | app.run(host='0.0.0.0', port=8001, debug=True) 140 | h.close() 141 | -------------------------------------------------------------------------------- /Client/ui/device_setup.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 244 10 | 75 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | TextLabel 27 | 28 | 29 | 30 | 31 | 32 | 33 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 34 | 35 | 36 | 37 | 38 | 39 | 40 | TextLabel 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | buttonBox 50 | accepted() 51 | Form 52 | close() 53 | 54 | 55 | 144 56 | 60 57 | 58 | 59 | 121 60 | 37 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Client/ui/device_setup_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 300 13 | 91 14 | 15 | 16 | 17 | 18 | 16777215 19 | 91 20 | 21 | 22 | 23 | Device setup 24 | 25 | 26 | Qt::LeftToRight 27 | 28 | 29 | 30 | 31 | 32 | Resolution 33 | 34 | 35 | 36 | 37 | 38 | 39 | Device 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | false 50 | 51 | 52 | 53 | 54 | 55 | 56 | Qt::Horizontal 57 | 58 | 59 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | buttonBox 69 | accepted() 70 | Dialog 71 | accept() 72 | 73 | 74 | 248 75 | 254 76 | 77 | 78 | 157 79 | 274 80 | 81 | 82 | 83 | 84 | buttonBox 85 | rejected() 86 | Dialog 87 | reject() 88 | 89 | 90 | 316 91 | 260 92 | 93 | 94 | 286 95 | 274 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Client/ui/images/24/calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/calculator.png -------------------------------------------------------------------------------- /Client/ui/images/24/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/fullscreen.png -------------------------------------------------------------------------------- /Client/ui/images/24/import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/import.png -------------------------------------------------------------------------------- /Client/ui/images/24/keyboard-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/keyboard-off.png -------------------------------------------------------------------------------- /Client/ui/images/24/keyboard-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/keyboard-outline.png -------------------------------------------------------------------------------- /Client/ui/images/24/keyboard-settings-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/keyboard-settings-outline.png -------------------------------------------------------------------------------- /Client/ui/images/24/keyboard-variant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/keyboard-variant.png -------------------------------------------------------------------------------- /Client/ui/images/24/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/keyboard.png -------------------------------------------------------------------------------- /Client/ui/images/24/monitor-multiple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/monitor-multiple.png -------------------------------------------------------------------------------- /Client/ui/images/24/monitor-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/monitor-off.png -------------------------------------------------------------------------------- /Client/ui/images/24/monitor-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/monitor-screenshot.png -------------------------------------------------------------------------------- /Client/ui/images/24/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/monitor.png -------------------------------------------------------------------------------- /Client/ui/images/24/mouse-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/mouse-off.png -------------------------------------------------------------------------------- /Client/ui/images/24/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/mouse.png -------------------------------------------------------------------------------- /Client/ui/images/24/notebook-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/notebook-edit.png -------------------------------------------------------------------------------- /Client/ui/images/24/reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/reload.png -------------------------------------------------------------------------------- /Client/ui/images/24/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/resize.png -------------------------------------------------------------------------------- /Client/ui/images/24/video-input-hdmi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/video-input-hdmi.png -------------------------------------------------------------------------------- /Client/ui/images/24/video-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/video-off.png -------------------------------------------------------------------------------- /Client/ui/images/24/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/video.png -------------------------------------------------------------------------------- /Client/ui/images/24/window-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/window-close.png -------------------------------------------------------------------------------- /Client/ui/images/24/window-maximize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/window-maximize.png -------------------------------------------------------------------------------- /Client/ui/images/24/window-minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/24/window-minimize.png -------------------------------------------------------------------------------- /Client/ui/images/48/camera-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/48/camera-off.png -------------------------------------------------------------------------------- /Client/ui/images/48/monitor-multiple -b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/48/monitor-multiple -b.png -------------------------------------------------------------------------------- /Client/ui/images/48/monitor-multiple -l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Client/ui/images/48/monitor-multiple -l.png -------------------------------------------------------------------------------- /Client/ui/main_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'main_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.4 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_MainWindow(object): 15 | def setupUi(self, MainWindow): 16 | MainWindow.setObjectName("MainWindow") 17 | MainWindow.resize(640, 480) 18 | self.centralwidget = QtWidgets.QWidget(MainWindow) 19 | self.centralwidget.setObjectName("centralwidget") 20 | MainWindow.setCentralWidget(self.centralwidget) 21 | self.menubar = QtWidgets.QMenuBar(MainWindow) 22 | self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 18)) 23 | self.menubar.setObjectName("menubar") 24 | self.menu_video_menu = QtWidgets.QMenu(self.menubar) 25 | self.menu_video_menu.setObjectName("menu_video_menu") 26 | self.menuView = QtWidgets.QMenu(self.menubar) 27 | self.menuView.setObjectName("menuView") 28 | self.menuKeyboard = QtWidgets.QMenu(self.menubar) 29 | self.menuKeyboard.setObjectName("menuKeyboard") 30 | self.menuShortcut_key = QtWidgets.QMenu(self.menuKeyboard) 31 | self.menuShortcut_key.setObjectName("menuShortcut_key") 32 | self.menuMouse = QtWidgets.QMenu(self.menubar) 33 | self.menuMouse.setObjectName("menuMouse") 34 | self.menuTools = QtWidgets.QMenu(self.menubar) 35 | self.menuTools.setObjectName("menuTools") 36 | MainWindow.setMenuBar(self.menubar) 37 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 38 | self.statusbar.setObjectName("statusbar") 39 | MainWindow.setStatusBar(self.statusbar) 40 | self.action_video_devices = QtWidgets.QAction(MainWindow) 41 | self.action_video_devices.setShortcut("") 42 | self.action_video_devices.setObjectName("action_video_devices") 43 | self.action_video_device_connect = QtWidgets.QAction(MainWindow) 44 | self.action_video_device_connect.setObjectName("action_video_device_connect") 45 | self.action_video_device_disconnect = QtWidgets.QAction(MainWindow) 46 | self.action_video_device_disconnect.setObjectName("action_video_device_disconnect") 47 | self.actionexit = QtWidgets.QAction(MainWindow) 48 | self.actionexit.setObjectName("actionexit") 49 | self.actionfullscreen = QtWidgets.QAction(MainWindow) 50 | self.actionfullscreen.setCheckable(True) 51 | self.actionfullscreen.setChecked(False) 52 | self.actionfullscreen.setShortcutVisibleInContextMenu(False) 53 | self.actionfullscreen.setObjectName("actionfullscreen") 54 | self.actionScreen_recording = QtWidgets.QAction(MainWindow) 55 | self.actionScreen_recording.setObjectName("actionScreen_recording") 56 | self.actionResize_window = QtWidgets.QAction(MainWindow) 57 | self.actionResize_window.setObjectName("actionResize_window") 58 | self.actionResetKeyboard = QtWidgets.QAction(MainWindow) 59 | self.actionResetKeyboard.setObjectName("actionResetKeyboard") 60 | self.actionResetMouse = QtWidgets.QAction(MainWindow) 61 | self.actionResetMouse.setObjectName("actionResetMouse") 62 | self.action_quickkey_1 = QtWidgets.QAction(MainWindow) 63 | self.action_quickkey_1.setObjectName("action_quickkey_1") 64 | self.action_quickkey_2 = QtWidgets.QAction(MainWindow) 65 | self.action_quickkey_2.setObjectName("action_quickkey_2") 66 | self.actionMinimize = QtWidgets.QAction(MainWindow) 67 | self.actionMinimize.setObjectName("actionMinimize") 68 | self.actionReload_Key_Mouse = QtWidgets.QAction(MainWindow) 69 | self.actionReload_Key_Mouse.setObjectName("actionReload_Key_Mouse") 70 | self.actionRelease_mouse = QtWidgets.QAction(MainWindow) 71 | self.actionRelease_mouse.setObjectName("actionRelease_mouse") 72 | self.actionDisplay_System_Mouse = QtWidgets.QAction(MainWindow) 73 | self.actionDisplay_System_Mouse.setObjectName("actionDisplay_System_Mouse") 74 | self.actionCapture_mouse = QtWidgets.QAction(MainWindow) 75 | self.actionCapture_mouse.setObjectName("actionCapture_mouse") 76 | self.actionCustomKey = QtWidgets.QAction(MainWindow) 77 | self.actionCustomKey.setObjectName("actionCustomKey") 78 | self.actionq1 = QtWidgets.QAction(MainWindow) 79 | self.actionq1.setObjectName("actionq1") 80 | self.actionq2 = QtWidgets.QAction(MainWindow) 81 | self.actionq2.setObjectName("actionq2") 82 | self.actionq3 = QtWidgets.QAction(MainWindow) 83 | self.actionq3.setObjectName("actionq3") 84 | self.actionq4 = QtWidgets.QAction(MainWindow) 85 | self.actionq4.setObjectName("actionq4") 86 | self.actionq5 = QtWidgets.QAction(MainWindow) 87 | self.actionq5.setObjectName("actionq5") 88 | self.actionq6 = QtWidgets.QAction(MainWindow) 89 | self.actionq6.setObjectName("actionq6") 90 | self.actionq7 = QtWidgets.QAction(MainWindow) 91 | self.actionq7.setObjectName("actionq7") 92 | self.actionq8 = QtWidgets.QAction(MainWindow) 93 | self.actionq8.setObjectName("actionq8") 94 | self.actionq_8 = QtWidgets.QAction(MainWindow) 95 | self.actionq_8.setObjectName("actionq_8") 96 | self.actionq9 = QtWidgets.QAction(MainWindow) 97 | self.actionq9.setObjectName("actionq9") 98 | self.actionq10 = QtWidgets.QAction(MainWindow) 99 | self.actionq10.setObjectName("actionq10") 100 | self.actionOn_screen_Keyboard = QtWidgets.QAction(MainWindow) 101 | self.actionOn_screen_Keyboard.setObjectName("actionOn_screen_Keyboard") 102 | self.actionCalculator = QtWidgets.QAction(MainWindow) 103 | self.actionCalculator.setObjectName("actionCalculator") 104 | self.actionSnippingTool = QtWidgets.QAction(MainWindow) 105 | self.actionSnippingTool.setObjectName("actionSnippingTool") 106 | self.actionNotepad = QtWidgets.QAction(MainWindow) 107 | self.actionNotepad.setObjectName("actionNotepad") 108 | self.menu_video_menu.addAction(self.action_video_devices) 109 | self.menu_video_menu.addAction(self.action_video_device_connect) 110 | self.menu_video_menu.addAction(self.action_video_device_disconnect) 111 | self.menu_video_menu.addSeparator() 112 | self.menu_video_menu.addAction(self.actionReload_Key_Mouse) 113 | self.menu_video_menu.addSeparator() 114 | self.menu_video_menu.addAction(self.actionMinimize) 115 | self.menu_video_menu.addAction(self.actionexit) 116 | self.menuView.addAction(self.actionfullscreen) 117 | self.menuView.addAction(self.actionResize_window) 118 | self.menuView.addSeparator() 119 | self.menuShortcut_key.addAction(self.actionq1) 120 | self.menuShortcut_key.addAction(self.actionq2) 121 | self.menuShortcut_key.addAction(self.actionq3) 122 | self.menuShortcut_key.addAction(self.actionq4) 123 | self.menuShortcut_key.addAction(self.actionq5) 124 | self.menuShortcut_key.addAction(self.actionq6) 125 | self.menuShortcut_key.addAction(self.actionq7) 126 | self.menuShortcut_key.addAction(self.actionq_8) 127 | self.menuShortcut_key.addAction(self.actionq9) 128 | self.menuShortcut_key.addAction(self.actionq10) 129 | self.menuKeyboard.addAction(self.actionResetKeyboard) 130 | self.menuKeyboard.addSeparator() 131 | self.menuKeyboard.addAction(self.menuShortcut_key.menuAction()) 132 | self.menuKeyboard.addAction(self.actionCustomKey) 133 | self.menuMouse.addAction(self.actionResetMouse) 134 | self.menuMouse.addSeparator() 135 | self.menuMouse.addAction(self.actionCapture_mouse) 136 | self.menuMouse.addAction(self.actionRelease_mouse) 137 | self.menuTools.addAction(self.actionOn_screen_Keyboard) 138 | self.menuTools.addAction(self.actionCalculator) 139 | self.menuTools.addAction(self.actionSnippingTool) 140 | self.menuTools.addAction(self.actionNotepad) 141 | self.menubar.addAction(self.menu_video_menu.menuAction()) 142 | self.menubar.addAction(self.menuView.menuAction()) 143 | self.menubar.addAction(self.menuKeyboard.menuAction()) 144 | self.menubar.addAction(self.menuMouse.menuAction()) 145 | self.menubar.addAction(self.menuTools.menuAction()) 146 | 147 | self.retranslateUi(MainWindow) 148 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 149 | 150 | def retranslateUi(self, MainWindow): 151 | _translate = QtCore.QCoreApplication.translate 152 | MainWindow.setWindowTitle(_translate("MainWindow", "Simple USB KVM")) 153 | self.menu_video_menu.setTitle(_translate("MainWindow", "Device")) 154 | self.menuView.setTitle(_translate("MainWindow", "Video")) 155 | self.menuKeyboard.setTitle(_translate("MainWindow", "Keyboard")) 156 | self.menuShortcut_key.setTitle(_translate("MainWindow", "Shortcut key")) 157 | self.menuMouse.setTitle(_translate("MainWindow", "Mouse")) 158 | self.menuTools.setTitle(_translate("MainWindow", "Tools")) 159 | self.action_video_devices.setText(_translate("MainWindow", "Video devices")) 160 | self.action_video_device_connect.setText(_translate("MainWindow", "Connect")) 161 | self.action_video_device_disconnect.setText(_translate("MainWindow", "Disconnect")) 162 | self.actionexit.setText(_translate("MainWindow", "Exit")) 163 | self.actionfullscreen.setText(_translate("MainWindow", "Full screen")) 164 | self.actionScreen_recording.setText(_translate("MainWindow", "Screen recording")) 165 | self.actionResize_window.setText(_translate("MainWindow", "Resize window")) 166 | self.actionResetKeyboard.setText(_translate("MainWindow", "Reset")) 167 | self.actionResetMouse.setText(_translate("MainWindow", "Reset")) 168 | self.action_quickkey_1.setText(_translate("MainWindow", "Ctrl+Alt+Del")) 169 | self.action_quickkey_2.setText(_translate("MainWindow", "Alt+Tab")) 170 | self.actionMinimize.setText(_translate("MainWindow", "Minimize")) 171 | self.actionReload_Key_Mouse.setText(_translate("MainWindow", "Reload Key/Mouse")) 172 | self.actionRelease_mouse.setText(_translate("MainWindow", "Release mouse")) 173 | self.actionDisplay_System_Mouse.setText(_translate("MainWindow", "Display system mouse")) 174 | self.actionCapture_mouse.setText(_translate("MainWindow", "Capture mouse")) 175 | self.actionCustomKey.setText(_translate("MainWindow", "Custom key")) 176 | self.actionq1.setText(_translate("MainWindow", "q1")) 177 | self.actionq2.setText(_translate("MainWindow", "q2")) 178 | self.actionq3.setText(_translate("MainWindow", "q3")) 179 | self.actionq4.setText(_translate("MainWindow", "q4")) 180 | self.actionq5.setText(_translate("MainWindow", "q5")) 181 | self.actionq6.setText(_translate("MainWindow", "q6")) 182 | self.actionq7.setText(_translate("MainWindow", "q7")) 183 | self.actionq8.setText(_translate("MainWindow", "q8")) 184 | self.actionq_8.setText(_translate("MainWindow", "q8")) 185 | self.actionq9.setText(_translate("MainWindow", "q9")) 186 | self.actionq10.setText(_translate("MainWindow", "q10")) 187 | self.actionOn_screen_Keyboard.setText(_translate("MainWindow", "On-screen Keyboard")) 188 | self.actionCalculator.setText(_translate("MainWindow", "Calculator")) 189 | self.actionSnippingTool.setText(_translate("MainWindow", "SnippingTool")) 190 | self.actionNotepad.setText(_translate("MainWindow", "Notepad")) 191 | -------------------------------------------------------------------------------- /Client/ui/main_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 640 10 | 480 11 | 12 | 13 | 14 | Simple USB KVM 15 | 16 | 17 | 18 | 19 | 20 | 0 21 | 0 22 | 640 23 | 18 24 | 25 | 26 | 27 | 28 | Device 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Video 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Keyboard 50 | 51 | 52 | 53 | Shortcut key 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Mouse 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Tools 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Video devices 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | Connect 107 | 108 | 109 | 110 | 111 | Disconnect 112 | 113 | 114 | 115 | 116 | Exit 117 | 118 | 119 | 120 | 121 | true 122 | 123 | 124 | false 125 | 126 | 127 | Full screen 128 | 129 | 130 | false 131 | 132 | 133 | 134 | 135 | Screen recording 136 | 137 | 138 | 139 | 140 | Resize window 141 | 142 | 143 | 144 | 145 | Reset 146 | 147 | 148 | 149 | 150 | Reset 151 | 152 | 153 | 154 | 155 | Ctrl+Alt+Del 156 | 157 | 158 | 159 | 160 | Alt+Tab 161 | 162 | 163 | 164 | 165 | Minimize 166 | 167 | 168 | 169 | 170 | Reload Key/Mouse 171 | 172 | 173 | 174 | 175 | Release mouse 176 | 177 | 178 | 179 | 180 | Display system mouse 181 | 182 | 183 | 184 | 185 | Capture mouse 186 | 187 | 188 | 189 | 190 | Custom key 191 | 192 | 193 | 194 | 195 | q1 196 | 197 | 198 | 199 | 200 | q2 201 | 202 | 203 | 204 | 205 | q3 206 | 207 | 208 | 209 | 210 | q4 211 | 212 | 213 | 214 | 215 | q5 216 | 217 | 218 | 219 | 220 | q6 221 | 222 | 223 | 224 | 225 | q7 226 | 227 | 228 | 229 | 230 | q8 231 | 232 | 233 | 234 | 235 | q8 236 | 237 | 238 | 239 | 240 | q9 241 | 242 | 243 | 244 | 245 | q10 246 | 247 | 248 | 249 | 250 | On-screen Keyboard 251 | 252 | 253 | 254 | 255 | Calculator 256 | 257 | 258 | 259 | 260 | SnippingTool 261 | 262 | 263 | 264 | 265 | Notepad 266 | 267 | 268 | 269 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /Client/ui/shortcut_key.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 252 10 | 107 11 | 12 | 13 | 14 | Custom Key 15 | 16 | 17 | 18 | 19 | 20 | Prt Sc 21 | 22 | 23 | true 24 | 25 | 26 | 27 | 28 | 29 | 30 | Qt::Horizontal 31 | 32 | 33 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 34 | 35 | 36 | 37 | 38 | 39 | 40 | Ctrl 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | 51 | 52 | 53 | Tab 54 | 55 | 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Shift 67 | 68 | 69 | true 70 | 71 | 72 | 73 | 74 | 75 | 76 | Meta 77 | 78 | 79 | true 80 | 81 | 82 | 83 | 84 | 85 | 86 | Alt 87 | 88 | 89 | true 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | buttonBox 106 | accepted() 107 | Dialog 108 | accept() 109 | 110 | 111 | 248 112 | 254 113 | 114 | 115 | 157 116 | 274 117 | 118 | 119 | 120 | 121 | buttonBox 122 | rejected() 123 | Dialog 124 | reject() 125 | 126 | 127 | 316 128 | 260 129 | 130 | 131 | 286 132 | 274 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /Firmware/Lib/DEBUG.C: -------------------------------------------------------------------------------- 1 | /********************************** (C) COPYRIGHT ******************************* 2 | * File Name : DEBUG.C 3 | * Author : WCH 4 | * Version : V1.0 5 | * Date : 2018/09/20 6 | * Description : CH5XX DEBUG Interface 7 | (1)、主频设置; 8 | (2)、us\ms基本延时函数; 9 | (3)、串口0输出打印信息,波特率可调; 10 | (4)、看门狗初始化和赋值函数; 11 | *******************************************************************************/ 12 | 13 | #include "CH549.H" 14 | #include "DEBUG.H" 15 | 16 | /******************************************************************************* 17 | * Function Name : CfgFsys( ) 18 | * Description : CH5XX时钟选择和配置函数,默认使用内部晶振24MHz,如果定义了FREQ_SYS可以 19 | 根据PLL_CFG和CLOCK_CFG配置得到,公式如下: 20 | Fsys = (Fosc * ( PLL_CFG & MASK_PLL_MULT ))/(CLOCK_CFG & MASK_SYS_CK_DIV); 21 | 具体时钟需要自己配置 22 | * Input : None 23 | * Output : None 24 | * Return : None 25 | *******************************************************************************/ 26 | void CfgFsys( ) 27 | { 28 | #if OSC_EN_XT 29 | SAFE_MOD = 0x55; 30 | SAFE_MOD = 0xAA; 31 | CLOCK_CFG |= bOSC_EN_XT; //使能外部晶振 32 | CLOCK_CFG &= ~bOSC_EN_INT; //关闭内部晶振 33 | #else 34 | SAFE_MOD = 0x55; 35 | SAFE_MOD = 0xAA; 36 | CLOCK_CFG |= bOSC_EN_INT; //使能内部晶振 37 | CLOCK_CFG &= ~bOSC_EN_XT; //关闭外部晶振 38 | #endif 39 | SAFE_MOD = 0x55; 40 | SAFE_MOD = 0xAA; 41 | #if FREQ_SYS == 48000000 42 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x07; // 48MHz 43 | #endif 44 | #if FREQ_SYS == 32000000 45 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x06; // 32MHz 46 | #endif 47 | #if FREQ_SYS == 24000000 48 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x05; // 24MHz 49 | #endif 50 | #if FREQ_SYS == 16000000 51 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x04; // 16MHz 52 | #endif 53 | #if FREQ_SYS == 12000000 54 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x03; // 12MHz 55 | #endif 56 | #if FREQ_SYS == 3000000 57 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x02; // 3MHz 58 | #endif 59 | #if FREQ_SYS == 750000 60 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x01; // 750KHz 61 | #endif 62 | #if FREQ_SYS == 187500 63 | CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x00; // 187.5KHz 64 | #endif 65 | SAFE_MOD = 0x00; 66 | } 67 | 68 | /******************************************************************************* 69 | * Function Name : mDelayus(UNIT16 n) 70 | * Description : us延时函数 71 | * Input : UNIT16 n 72 | * Output : None 73 | * Return : None 74 | *******************************************************************************/ 75 | void mDelayuS( UINT16 n ) // 以uS为单位延时 76 | { 77 | while ( n ) // total = 12~13 Fsys cycles, 1uS @Fsys=12MHz 78 | { 79 | ++ SAFE_MOD; // 2 Fsys cycles, for higher Fsys, add operation here 80 | #ifdef FREQ_SYS 81 | #if FREQ_SYS >= 14000000 82 | ++ SAFE_MOD; 83 | #endif 84 | #if FREQ_SYS >= 16000000 85 | ++ SAFE_MOD; 86 | #endif 87 | #if FREQ_SYS >= 18000000 88 | ++ SAFE_MOD; 89 | #endif 90 | #if FREQ_SYS >= 20000000 91 | ++ SAFE_MOD; 92 | #endif 93 | #if FREQ_SYS >= 22000000 94 | ++ SAFE_MOD; 95 | #endif 96 | #if FREQ_SYS >= 24000000 97 | ++ SAFE_MOD; 98 | #endif 99 | #if FREQ_SYS >= 26000000 100 | ++ SAFE_MOD; 101 | #endif 102 | #if FREQ_SYS >= 28000000 103 | ++ SAFE_MOD; 104 | #endif 105 | #if FREQ_SYS >= 30000000 106 | ++ SAFE_MOD; 107 | #endif 108 | #if FREQ_SYS >= 32000000 109 | ++ SAFE_MOD; 110 | #endif 111 | #if FREQ_SYS >= 34000000 112 | ++ SAFE_MOD; 113 | #endif 114 | #if FREQ_SYS >= 36000000 115 | ++ SAFE_MOD; 116 | #endif 117 | #if FREQ_SYS >= 38000000 118 | ++ SAFE_MOD; 119 | #endif 120 | #if FREQ_SYS >= 40000000 121 | ++ SAFE_MOD; 122 | #endif 123 | #if FREQ_SYS >= 42000000 124 | ++ SAFE_MOD; 125 | #endif 126 | #if FREQ_SYS >= 44000000 127 | ++ SAFE_MOD; 128 | #endif 129 | #if FREQ_SYS >= 46000000 130 | ++ SAFE_MOD; 131 | #endif 132 | #if FREQ_SYS >= 48000000 133 | ++ SAFE_MOD; 134 | #endif 135 | #if FREQ_SYS >= 50000000 136 | ++ SAFE_MOD; 137 | #endif 138 | #if FREQ_SYS >= 52000000 139 | ++ SAFE_MOD; 140 | #endif 141 | #if FREQ_SYS >= 54000000 142 | ++ SAFE_MOD; 143 | #endif 144 | #if FREQ_SYS >= 56000000 145 | ++ SAFE_MOD; 146 | #endif 147 | #endif 148 | -- n; 149 | } 150 | } 151 | 152 | /******************************************************************************* 153 | * Function Name : mDelayms(UNIT16 n) 154 | * Description : ms延时函数 155 | * Input : UNIT16 n 156 | * Output : None 157 | * Return : None 158 | *******************************************************************************/ 159 | void mDelaymS( UINT16 n ) // 以mS为单位延时 160 | { 161 | while ( n ) 162 | { 163 | mDelayuS( 1000 ); 164 | -- n; 165 | } 166 | } 167 | 168 | /******************************************************************************* 169 | * Function Name : CH549UART0Alter() 170 | * Description : CH549串口0引脚映射,串口映射到P0.2(R)和P0.3(T) 171 | * Input : None 172 | * Output : None 173 | * Return : None 174 | *******************************************************************************/ 175 | void CH549UART0Alter() 176 | { 177 | P0_MOD_OC |= (3 << 2); //准双向模式 178 | P0_DIR_PU |= (3 << 2); 179 | PIN_FUNC |= bUART0_PIN_X; //开启引脚复用功能 180 | } 181 | 182 | /******************************************************************************* 183 | * Function Name : mInitSTDIO() 184 | * Description : CH559串口0初始化,默认使用T1作UART0的波特率发生器,也可以使用T2 185 | 作为波特率发生器 186 | * Input : None 187 | * Output : None+ 188 | * Return : None 189 | *******************************************************************************/ 190 | void mInitSTDIO( ) 191 | { 192 | UINT32 x; 193 | UINT8 x2; 194 | 195 | SM0 = 0; 196 | SM1 = 1; 197 | SM2 = 0; //串口0使用模式1 198 | //使用Timer1作为波特率发生器 199 | RCLK = 0; //UART0接收时钟 200 | TCLK = 0; //UART0发送时钟 201 | PCON |= SMOD; 202 | x = 10 * FREQ_SYS / UART0BUAD / 16; //如果更改主频,注意x的值不要溢出 203 | x2 = x % 10; 204 | x /= 10; 205 | 206 | if ( x2 >= 5 ) x ++; //四舍五入 207 | 208 | TMOD = TMOD & ~ bT1_GATE & ~ bT1_CT & ~ MASK_T1_MOD | bT1_M1; //0X20,Timer1作为8位自动重载定时器 209 | T2MOD = T2MOD | bTMR_CLK | bT1_CLK; //Timer1时钟选择 210 | TH1 = 0 - x; //12MHz晶振,buad/12为实际需设置波特率 211 | TR1 = 1; //启动定时器1 212 | TI = 1; 213 | REN = 1; //串口0接收使能 214 | 215 | // #ifdef UART0_INTERRUPT 216 | // ES = 1; //串口0中断使能 217 | // #endif 218 | } 219 | 220 | /******************************************************************************* 221 | * Function Name : CH549SoftReset() 222 | * Description : CH549软复位 223 | * Input : None 224 | * Output : None 225 | * Return : None 226 | *******************************************************************************/ 227 | void CH549SoftReset( ) 228 | { 229 | SAFE_MOD = 0x55; 230 | SAFE_MOD = 0xAA; 231 | GLOBAL_CFG |= bSW_RESET; 232 | } 233 | /******************************************************************************* 234 | * Function Name : CH549WDTModeSelect(UINT8 mode) 235 | * Description : CH549看门狗模式选择 236 | * 8位计数器,溢出周期(秒): (131072/FREQ_SYS)*(256-WDOG_COUNT) 237 | * Input : UINT8 mode 238 | 0 timer 239 | 1 watchDog 240 | * Output : None 241 | * Return : None 242 | *******************************************************************************/ 243 | void CH549WDTModeSelect(UINT8 mode) 244 | { 245 | SAFE_MOD = 0x55; 246 | SAFE_MOD = 0xaa; //进入安全模式 247 | 248 | if(mode) 249 | { 250 | GLOBAL_CFG |= bWDOG_EN; //启动看门狗复位 251 | } 252 | else GLOBAL_CFG &= ~bWDOG_EN; //启动看门狗仅仅作为定时器 253 | 254 | SAFE_MOD = 0x00; //退出安全模式 255 | WDOG_COUNT = 0; //看门狗赋初值 256 | } 257 | 258 | /******************************************************************************* 259 | * Function Name : CH549WDTFeed(UINT8 tim) 260 | * Description : CH549看门狗定时时间设置 261 | * Input : UINT8 tim 看门狗复位时间设置 262 | 00H(12MHz)=2.8s 00H(24MHz)=1.4s 263 | 80H(12MHz)=1.4s 80H(24MHz)=0.7s 264 | * Output : None 265 | * Return : None 266 | *******************************************************************************/ 267 | void CH549WDTFeed(UINT8 tim) 268 | { 269 | WDOG_COUNT = tim; //看门狗计数器赋值 270 | } 271 | 272 | 273 | 274 | /******************************************************************************* 275 | * Function Name : CH549UART0RcvByte() 276 | * Description : CH549UART0接收一个字节 277 | * Input : None 278 | * Output : None 279 | * Return : SBUF 280 | *******************************************************************************/ 281 | UINT8 CH549UART0RcvByte( ) 282 | { 283 | while(RI == 0); //查询接收,中断方式可不用 284 | RI = 0; 285 | return SBUF; 286 | } 287 | 288 | /******************************************************************************* 289 | * Function Name : CH549UART0SendByte(UINT8 SendDat) 290 | * Description : CH549UART0发送一个字节 291 | * Input : UINT8 SendDat;要发送的数据 292 | * Output : None 293 | * Return : None 294 | *******************************************************************************/ 295 | void CH549UART0SendByte(char SendDat) 296 | { 297 | SBUF = SendDat; 298 | while(TI!=1); //等待发送成功 299 | TI = 0; //清除发送完成中断 300 | } 301 | //重写putchar函数 302 | //printf函数是调用putchar实现字符数据传送的 303 | char putchar(char c) 304 | { 305 | CH549UART0SendByte(c); 306 | return c; 307 | } -------------------------------------------------------------------------------- /Firmware/Lib/DEBUG.H: -------------------------------------------------------------------------------- 1 | /* 调试 */ 2 | /* 提供printf子程序和延时函数 */ 3 | 4 | #ifndef __DEBUG_H__ 5 | #define __DEBUG_H__ 6 | 7 | #include 8 | #include 9 | 10 | //定义函数返回值 11 | #ifndef SUCCESS 12 | #define SUCCESS 1 13 | #endif 14 | #ifndef FAIL 15 | #define FAIL 0 16 | #endif 17 | 18 | //定义定时器起始 19 | #ifndef START 20 | #define START 1 21 | #endif 22 | #ifndef STOP 23 | #define STOP 0 24 | #endif 25 | 26 | #ifndef DE_PRINTF //调试开关 27 | #define DE_PRINTF 1 28 | #endif 29 | 30 | //#define FREQ_SYS 24000000 //系统主频24MHz 31 | #define FREQ_SYS 48000000 //系统主频48MHz 32 | #define OSC_EN_XT 0 //外部晶振使能,默认开启内部晶振 33 | 34 | #ifndef UART0BUAD 35 | #define UART0BUAD 115200 36 | #endif 37 | 38 | 39 | #define UART0_INTERRUPT 0 //定义是否开启串口0中断 40 | 41 | 42 | void CfgFsys( ); // CH549时钟选择和配置 43 | void mDelayuS( UINT16 n ); // 以uS为单位延时 44 | void mDelaymS( UINT16 n ); // 以mS为单位延时 45 | void CH559UART0Alter(); // CH549串口0引脚映射到P0.2/P0.3 46 | void mInitSTDIO( ); // T1作为波特率发生器 47 | void CH549SoftReset( ); // CH549软复位 48 | void CH549WDTModeSelect(UINT8 mode); // 看门狗模式选择 49 | void CH549WDTFeed(UINT8 tim); // 喂狗 50 | 51 | extern UINT8 CH549UART0RcvByte( ); //UART0接收一个字节 52 | extern void CH549UART0SendByte(UINT8 SendDat); //UART-发送一个字节 53 | 54 | 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /Firmware/Lib/GPIO.C: -------------------------------------------------------------------------------- 1 | /********************************** (C) COPYRIGHT ******************************* 2 | * File Name : GPIO.C 3 | * Author : WCH 4 | * Version : V1.0 5 | * Date : 2018/08/09 6 | * Description : CH549 GPIO相关函数 7 | *******************************************************************************/ 8 | #include "GPIO.H" 9 | #pragma NOAREGS 10 | /******************************************************************************* 11 | * Function Name : GPIO_Init(UINT8 PORTx,UINT8 PINx,UINT8 MODEx) 12 | * Description : GPIO端口初始化函数 13 | * Input : PORTx:0~4 14 | * PINx:位域,每个位对应该Port的一个引脚 15 | * MODEx: 16 | * 0:高阻输入模式,引脚没有上拉电阻 17 | * 1:推挽输出模式,具有对称驱动能力,可以输出或者吸收较大电流 18 | * 2:开漏输出,支持高阻输入,引脚没有上拉电阻 19 | * 3:准双向模式(标准 8051),开漏输出,支持输入,引脚有上拉电阻(默认模式) 20 | * Output : None 21 | * Return : None 22 | *******************************************************************************/ 23 | void GPIO_Init(UINT8 PORTx, UINT8 PINx, UINT8 MODEx) 24 | { 25 | UINT8 Px_DIR_PU, Px_MOD_OC; 26 | 27 | switch(PORTx) //读出初始值 28 | { 29 | case PORT0: 30 | Px_MOD_OC = P0_MOD_OC; 31 | Px_DIR_PU = P0_DIR_PU; 32 | break; 33 | 34 | case PORT1: 35 | Px_MOD_OC = P1_MOD_OC; 36 | Px_DIR_PU = P1_DIR_PU; 37 | break; 38 | 39 | case PORT2: 40 | Px_MOD_OC = P2_MOD_OC; 41 | Px_DIR_PU = P2_DIR_PU; 42 | break; 43 | 44 | case PORT3: 45 | Px_MOD_OC = P3_MOD_OC; 46 | Px_DIR_PU = P3_DIR_PU; 47 | break; 48 | 49 | case PORT4: 50 | Px_MOD_OC = P4_MOD_OC; 51 | Px_DIR_PU = P4_DIR_PU; 52 | break; 53 | 54 | default : 55 | break; 56 | } 57 | 58 | switch(MODEx) 59 | { 60 | case MODE0: //高阻输入模式,引脚没有上拉电阻 61 | Px_MOD_OC &= ~PINx; 62 | Px_DIR_PU &= ~PINx; 63 | break; 64 | 65 | case MODE1: //推挽输出模式,具有对称驱动能力,可以输出或者吸收较大电流 66 | Px_MOD_OC &= ~PINx; 67 | Px_DIR_PU |= PINx; 68 | break; 69 | 70 | case MODE2: //开漏输出,支持高阻输入,引脚没有上拉电阻 71 | Px_MOD_OC |= PINx; 72 | Px_DIR_PU &= ~PINx; 73 | break; 74 | 75 | case MODE3: //准双向模式(标准 8051),开漏输出,支持输入,引脚有上拉电阻 76 | Px_MOD_OC |= PINx; 77 | Px_DIR_PU |= PINx; 78 | break; 79 | 80 | default : 81 | break; 82 | } 83 | 84 | switch(PORTx) //回写 85 | { 86 | case PORT0: 87 | P0_MOD_OC = Px_MOD_OC; 88 | P0_DIR_PU = Px_DIR_PU; 89 | break; 90 | 91 | case PORT1: 92 | P1_MOD_OC = Px_MOD_OC; 93 | P1_DIR_PU = Px_DIR_PU; 94 | break; 95 | 96 | case PORT2: 97 | P2_MOD_OC = Px_MOD_OC; 98 | P2_DIR_PU = Px_DIR_PU; 99 | break; 100 | 101 | case PORT3: 102 | P3_MOD_OC = Px_MOD_OC; 103 | P3_DIR_PU = Px_DIR_PU; 104 | break; 105 | 106 | case PORT4: 107 | P4_MOD_OC = Px_MOD_OC; 108 | P4_DIR_PU = Px_DIR_PU; 109 | break; 110 | 111 | default : 112 | break; 113 | } 114 | } 115 | /******************************************************************************* 116 | * Function Name : GPIO_INT_Init 117 | * Description : 可设置 RXD1_L、P15_L、P14_L、P03_L、P57_H、P46_L、RXD0_L 扩展引脚的外部中断 118 | * 同时还包含兼容C51的 INT1_L、INT0_L 的外部中断触发 119 | * (RXD1_L、RXD0_L具体是哪个引脚取决于引脚是否映射) 120 | * Input : IntSrc:共9个外部中断,按位域表示,具体定义见GPIO.H 121 | * Mode:0:电平模式 1:边沿模式 (注意扩展引脚的中断模式是统一配置的) 122 | * NewState:0:关闭对应外部中断使能 1:开启对应外部中断 123 | * Output : None 124 | * Return : None 125 | *******************************************************************************/ 126 | void GPIO_INT_Init( UINT16 IntSrc, UINT8 Mode, UINT8 NewState ) 127 | { 128 | /* 中断触发模式设置 */ 129 | if(Mode == INT_EDGE) //边沿触发模式 130 | { 131 | if(IntSrc & 0x7F) //存在扩展中断 132 | { 133 | GPIO_IE |= bIE_IO_EDGE; 134 | } 135 | 136 | if(IntSrc & INT_INT0_L) //存在外部中断0 137 | { 138 | IT0 = 1; 139 | } 140 | 141 | if(IntSrc & INT_INT1_L) //存在外部中断1 142 | { 143 | IT1 = 1; 144 | } 145 | } 146 | else //电平触发模式 147 | { 148 | if(IntSrc & 0x7F) //存在扩展中断 149 | { 150 | GPIO_IE &= ~bIE_IO_EDGE; 151 | } 152 | 153 | if(IntSrc & INT_INT0_L) //存在外部中断0 154 | { 155 | IT0 = 1; 156 | } 157 | 158 | if(IntSrc & INT_INT1_L) //存在外部中断1 159 | { 160 | IT1 = 1; 161 | } 162 | } 163 | 164 | /* 中断使能状态 */ 165 | if(NewState == Enable) //开启外部中断 166 | { 167 | GPIO_IE |= ((UINT8)IntSrc & 0x7F); 168 | 169 | if(IntSrc & INT_INT0_L) //存在外部中断0 170 | { 171 | EX0 = 1; 172 | } 173 | 174 | if(IntSrc & INT_INT1_L) //存在外部中断1 175 | { 176 | EX1 = 1; 177 | } 178 | 179 | if(GPIO_IE & 0x7F) 180 | { 181 | IE_GPIO = 1; //开启扩展GPIO中断 182 | } 183 | 184 | EA = 1; //开启总中断 185 | } 186 | else //关闭对应外部中断 187 | { 188 | GPIO_IE &= ~((UINT8)IntSrc & 0x7F); 189 | 190 | if(IntSrc & INT_INT0_L) //存在外部中断0 191 | { 192 | EX0 = 0; 193 | } 194 | 195 | if(IntSrc & INT_INT1_L) //存在外部中断1 196 | { 197 | EX1 = 0; 198 | } 199 | 200 | if((GPIO_IE & 0x7F) == 0) 201 | { 202 | IE_GPIO = 0; //关闭扩展GPIO中断 203 | } 204 | } 205 | } 206 | /******************************************************************************* 207 | * Function Name : GPIO_ISR 208 | * Description : RXD1、P15、P14、P03、P57、P46、RXD0 引脚外部中断服务函数 209 | * Input : None 210 | * Output : None 211 | * Return : None 212 | *******************************************************************************/ 213 | void GPIO_EXT_ISR(void) interrupt INT_NO_GPIO 214 | { 215 | if(AIN11 == 0) 216 | { 217 | mDelaymS(10); 218 | printf("P03 Falling\n"); 219 | } 220 | 221 | if(AIN5 == 0) 222 | { 223 | mDelaymS(10); 224 | printf("P15 Falling\n"); 225 | } 226 | 227 | 228 | } 229 | /******************************************************************************* 230 | * Function Name : GPIO_STD0_ISR 231 | * Description : INT0(P32) 引脚外部中断服务函数 232 | * Input : None 233 | * Output : None 234 | * Return : None 235 | *******************************************************************************/ 236 | void GPIO_STD0_ISR(void) interrupt INT_NO_INT0 237 | { 238 | mDelaymS(10); 239 | printf("P32 Falling\n"); 240 | 241 | } 242 | /******************************************************************************* 243 | * Function Name : GPIO_STD1_ISR 244 | * Description : INT1(P33) 引脚外部中断服务函数 245 | * Input : None 246 | * Output : None 247 | * Return : None 248 | *******************************************************************************/ 249 | void GPIO_STD1_ISR(void) interrupt INT_NO_INT1 250 | { 251 | mDelaymS(10); 252 | printf("P33 Falling\n"); 253 | 254 | } 255 | -------------------------------------------------------------------------------- /Firmware/Lib/GPIO.H: -------------------------------------------------------------------------------- 1 | #ifndef __GPIO_H__ 2 | #define __GPIO_H__ 3 | #include "CH549.H" 4 | #include "DEBUG.H" 5 | /****************** GPIO_Init 参数定义 ******************/ 6 | /* 端口定义 */ 7 | #define PORT0 0x00 8 | #define PORT1 0x01 9 | #define PORT2 0x02 10 | #define PORT3 0x03 11 | #define PORT4 0x04 12 | /* 引脚定义(可以组合) */ 13 | #define PIN0 0x01 14 | #define PIN1 0x02 15 | #define PIN2 0x04 16 | #define PIN3 0x08 17 | #define PIN4 0x10 18 | #define PIN5 0x20 19 | #define PIN6 0x40 20 | #define PIN7 0x80 21 | /* 模式定义 */ 22 | #define MODE0 0x00 //高阻输入模式,引脚没有上拉电阻 23 | #define MODE1 0x01 //推挽输出模式,具有对称驱动能力,可以输出或者吸收较大电流 24 | #define MODE2 0x02 //开漏输出,支持高阻输入,引脚没有上拉电阻 25 | #define MODE3 0x03 //准双向模式(标准 8051),开漏输出,支持输入,引脚有上拉电阻(默认模式) 26 | /****************** GPIO_INT_Init 参数定义 ********************/ 27 | /* IntSrc 外部中断(可组合) */ 28 | #define INT_RXD0_L 0x0001 29 | #define INT_P46_L 0x0002 30 | #define INT_P57_H 0x0004 //唯一一个高电平(上升沿)有效中断 31 | #define INT_P03_L 0x0008 32 | #define INT_P14_L 0x0010 33 | #define INT_P15_L 0x0020 34 | #define INT_RXD1_L 0x0040 35 | #define INT_INT0_L 0x0080 36 | #define INT_INT1_L 0x0100 37 | /* 中断触发模式 */ 38 | #define INT_LEVEL 0x00 //电平模式 39 | #define INT_EDGE 0x01 //边沿模式 40 | /* 中断状态 */ 41 | #define Disable 0x00 //中断无效 42 | #define Enable 0x01 //中断使能 43 | /****************** 外部调用子函数 ****************************/ 44 | extern void GPIO_Init(UINT8 PORTx, UINT8 PINx, UINT8 MODEx); //GPIO初始化 45 | extern void GPIO_INT_Init( UINT16 IntSrc, UINT8 Mode, UINT8 NewState ); //外部中断初始化 46 | #endif 47 | -------------------------------------------------------------------------------- /Firmware/Lib/Timer.C: -------------------------------------------------------------------------------- 1 | /********************************** (C) COPYRIGHT ******************************* 2 | * File Name : Timer.C 3 | * Author : WCH 4 | * Version : V1.0 5 | * Date : 2018/08/21 6 | * Description : CH549 Time 初始化、定时器、计数器赋值、T2捕捉功能开启函数等 7 | 定时器中断函数 8 | *******************************************************************************/ 9 | #include "Timer.H" 10 | #pragma NOAREGS 11 | #ifdef T2_CAP 12 | UINT16 Cap2[2] = {0}; 13 | UINT16 Cap1[2] = {0}; 14 | UINT16 Cap0[2] = {0}; 15 | #endif 16 | /******************************************************************************* 17 | * Function Name : mTimer_x_ModInit(UINT8 x ,UINT8 mode) 18 | * Description : CH549定时计数器x模式设置 19 | * Input : UINT8 mode,Timer模式选择 20 | 0:模式0,13位定时器,TLn的高3位无效 21 | 1:模式1,16位定时器 22 | 2:模式2,8位自动重装定时器 23 | 3:模式3,两个8位定时器 Timer0 24 | 3:模式3,Timer1停止 25 | * Output : None 26 | * Return : 成功 SUCCESS 27 | 失败 FAIL 28 | *******************************************************************************/ 29 | UINT8 mTimer_x_ModInit(UINT8 x ,UINT8 mode) 30 | { 31 | if(x == 0) 32 | { 33 | TMOD = TMOD & 0xf0 | mode; 34 | } 35 | else if(x == 1) 36 | { 37 | TMOD = TMOD & 0x0f | (mode<<4); 38 | } 39 | else if(x == 2) 40 | { 41 | RCLK = 0; //16位自动重载定时器 42 | TCLK = 0; 43 | CP_RL2 = 0; 44 | } 45 | else 46 | { 47 | return FAIL; 48 | } 49 | return SUCCESS; 50 | } 51 | /******************************************************************************* 52 | * Function Name : mTimer_x_SetData(UINT8 x,UINT16 dat) 53 | * Description : CH549Timer0 TH0和TL0赋值 54 | * Input : UINT16 dat;定时器赋值 55 | * Output : None 56 | * Return : None 57 | *******************************************************************************/ 58 | void mTimer_x_SetData(UINT8 x,UINT16 dat) 59 | { 60 | UINT16 tmp; 61 | tmp = 65536 - dat; 62 | if(x == 0) 63 | { 64 | TL0 = tmp & 0xff; 65 | TH0 = (tmp>>8) & 0xff; 66 | } 67 | else if(x == 1) 68 | { 69 | TL1 = tmp & 0xff; 70 | TH1 = (tmp>>8) & 0xff; 71 | } 72 | else if(x == 2) 73 | { 74 | RCAP2L = TL2 = tmp & 0xff; //16位自动重载定时器 75 | RCAP2H = TH2 = (tmp>>8) & 0xff; 76 | } 77 | } 78 | /******************************************************************************* 79 | * Function Name : CAP2Init(UINT8 mode) 80 | * Description : CH549定时计数器2 T2EX引脚捕捉功能初始化(CAP2 P11) 81 | UINT8 mode,边沿捕捉模式选择 82 | 0:T2ex从下降沿到下一个下降沿 83 | 1:T2ex任意边沿之间 84 | 3:T2ex从上升沿到下一个上升沿 85 | * Input : None 86 | * Output : None 87 | * Return : None 88 | *******************************************************************************/ 89 | void CAP2Init(UINT8 mode) 90 | { 91 | RCLK = 0; 92 | TCLK = 0; 93 | C_T2 = 0; 94 | EXEN2 = 1; 95 | CP_RL2 = 1; //启动T2ex的捕捉功能 96 | T2MOD |= mode << 2; //边沿捕捉模式选择 97 | } 98 | /******************************************************************************* 99 | * Function Name : CAP1Init(UINT8 mode) 100 | * Description : CH549定时计数器2 T2引脚捕捉功能初始化T2(CAP1 P10) 101 | UINT8 mode,边沿捕捉模式选择 102 | 0:T2ex从下降沿到下一个下降沿 103 | 1:T2ex任意边沿之间 104 | 3:T2ex从上升沿到下一个上升沿 105 | * Input : None 106 | * Output : None 107 | * Return : None 108 | *******************************************************************************/ 109 | void CAP1Init(UINT8 mode) 110 | { 111 | RCLK = 0; 112 | TCLK = 0; 113 | CP_RL2 = 1; 114 | C_T2 = 0; 115 | T2MOD = T2MOD & ~T2OE | (mode << 2) | bT2_CAP1_EN; //使能T2引脚捕捉功能,边沿捕捉模式选择 116 | } 117 | /******************************************************************************* 118 | * Function Name : CAP0Init(UINT8 mode) 119 | * Description : CH549定时计数器2 CAP0引脚捕捉功能初始化(CAP0 P36) 120 | UINT8 mode,边沿捕捉模式选择 121 | 0:T2ex从下降沿到下一个下降沿 122 | 1:T2ex任意边沿之间 123 | 3:T2ex从上升沿到下一个上升沿 124 | * Input : None 125 | * Output : None 126 | * Return : None 127 | *******************************************************************************/ 128 | void CAP0Init(UINT8 mode) 129 | { 130 | RCLK = 0; 131 | TCLK = 0; 132 | CP_RL2 = 1; 133 | C_T2 = 0; 134 | T2MOD |= mode << 2; //边沿捕捉模式选择 135 | T2CON2 = bT2_CAP0_EN; 136 | } 137 | #ifdef T0_INT 138 | /******************************************************************************* 139 | * Function Name : mTimer0Interrupt() 140 | * Description : CH549定时计数器0定时计数器中断处理函数 1ms中断 141 | *******************************************************************************/ 142 | void mTimer0Interrupt( void ) interrupt INT_NO_TMR0 using 1 //timer0中断服务程序,使用寄存器组1 143 | { 144 | mTimer_x_SetData(0,2000); //模式1 需重新给TH0和TL0赋值 145 | SCK = ~SCK; //大约1ms 146 | } 147 | #endif 148 | #ifdef T1_INT 149 | /******************************************************************************* 150 | * Function Name : mTimer1Interrupt() 151 | * Description : CH549定时计数器1定时计数器中断处理函数 100us中断 152 | *******************************************************************************/ 153 | void mTimer1Interrupt( void ) interrupt INT_NO_TMR1 using 2 //timer1中断服务程序,使用寄存器组2 154 | { 155 | //方式2时,Timer1自动重装 156 | static UINT16 tmr1 = 0; 157 | tmr1++; 158 | if(tmr1 == 2000) //100us*2000 = 200ms 159 | { 160 | tmr1 = 0; 161 | SCK = ~SCK; 162 | } 163 | } 164 | #endif 165 | /******************************************************************************* 166 | * Function Name : mTimer2Interrupt() 167 | * Description : CH549定时计数器0定时计数器中断处理函数 168 | *******************************************************************************/ 169 | void mTimer2Interrupt( void ) interrupt INT_NO_TMR2 using 3 //timer2中断服务程序,使用寄存器组3 170 | { 171 | #ifdef T2_INT 172 | static UINT8 tmr2 = 0; 173 | if(TF2) 174 | { 175 | TF2 = 0; //清空定时器2溢出中断,需手动请 176 | tmr2++; 177 | if(tmr2 == 100) //200ms时间到 178 | { 179 | tmr2 = 0; 180 | SCK = ~SCK; //P17电平指示监控 181 | } 182 | } 183 | #endif 184 | 185 | #ifdef T2_CAP 186 | if(EXF2) //T2ex电平变化中断中断标志 187 | { 188 | Cap2[0] = RCAP2; //T2EX 189 | printf("CAP2 %04x\n",Cap2[0]-Cap2[1]); 190 | Cap2[1] = Cap2[0]; 191 | EXF2 = 0; //清空T2ex捕捉中断标志 192 | } 193 | if(CAP1F) //T2电平捕捉中断标志 194 | { 195 | Cap1[0] = T2CAP1; //T2; 196 | printf("CAP1 %04x\n",Cap1[0]-Cap1[1]); 197 | Cap1[1] = Cap1[0]; 198 | CAP1F = 0; //清空T2捕捉中断标志 199 | } 200 | if(T2CON2&bT2_CAP0F) 201 | { 202 | Cap0[0] = T2CAP0; 203 | printf("CAP0 %04x\n",Cap0[0]-Cap0[1]); 204 | Cap0[1] = Cap0[0]; 205 | T2CON2 &= ~bT2_CAP0F; 206 | } 207 | #endif 208 | 209 | } 210 | -------------------------------------------------------------------------------- /Firmware/Lib/Timer.H: -------------------------------------------------------------------------------- 1 | #ifndef __TIMER_H__ 2 | #define __TIMER_H__ 3 | #include "CH549.H" 4 | #include "DEBUG.H" 5 | #define T0_INT 1 6 | //#define T1_INT 1 7 | //#define T2_INT 1 8 | #define T2_CAP 1 9 | /******************* CH549 Timer0相关 ************************/ 10 | //bTMR_CLK同时影响Timer0&1&2,使用时要注意 (除定时使用标准时钟) 11 | #define mTimer0Clk12DivFsys( ) (T2MOD &= ~bT0_CLK) //定时器,时钟=Fsys/12 T0标准时钟 12 | #define mTimer0ClkFsys( ) (T2MOD |= bTMR_CLK | bT0_CLK) //定时器,时钟=Fsys 13 | #define mTimer0Clk4DivFsys( ) (T2MOD &= ~bTMR_CLK;T2MOD |= bT0_CLK) //定时器,时钟=Fsys/4(此时需同步调整DEBUG.C的mInitSTDIO) 14 | #define mTimer0CountClk( ) (TMOD |= bT0_CT) //选择工作在计数器模式(T0引脚(P34)的下降沿有效) 15 | #define mTimer0RunCTL( SS ) (TR0 = SS ? START : STOP) //CH549 Timer0 开始(SS=1)/结束(SS=0) 16 | /***** CH549 Timer1相关(DEBUG.C的mInitSTDIO使用T1模式2) *****/ 17 | #define mTimer1Clk12DivFsys( ) (T2MOD &= ~bT1_CLK) //定时器,时钟=Fsys/12 T1标准时钟 18 | #define mTimer1ClkFsys( ) (T2MOD |= bTMR_CLK | bT1_CLK) //定时器,时钟=Fsys 19 | #define mTimer1Clk4DivFsys( ) (T2MOD &= ~bTMR_CLK;T2MOD |= bT1_CLK) //定时器,时钟=Fsys/4 20 | #define mTimer1CountClk( ) (TMOD |= bT1_CT) //选择工作在计数器模式(T1引脚(P35)的下降沿有效) 21 | #define mTimer1RunCTL( SS ) (TR1 = SS ? START : STOP) //CH549 Timer1 开始(SS=1)/结束(SS=0) 22 | /******************* CH549 Timer2相关 ************************/ 23 | #define mTimer2Clk12DivFsys( ) {T2MOD &= ~ bT2_CLK;C_T2 = 0;} //定时器,时钟=Fsys/12 T2标准时钟 24 | #define mTimer2ClkFsys( ) {T2MOD |= (bTMR_CLK | bT2_CLK);C_T2=0;} //定时器,时钟=Fsys 25 | #define mTimer2Clk4DivFsys( ) {T2MOD &= ~bTMR_CLK;T2MOD |= bT2_CLK;C_T2 = 0;}//定时器,时钟=Fsys/4(此时需同步调整DEBUG.C的mInitSTDIO) 26 | #define mTimer2CountClk( ) {C_T2 = 1;} //选择工作在计数器模式(T2引脚(P10)的下降沿有效) 27 | #define mTimer2RunCTL( SS ) {TR2 = SS ? START : STOP;} //CH549 Timer2 开始(SS=1)/结束(SS=0) 28 | #define mTimer2OutCTL( ) (T2MOD |= T2OE) //T2输出 频率TF2/2 29 | #define CAP1Alter( ) (PIN_FUNC |= bT2_PIN_X;) //CAP1由P10 映射到P14 30 | #define CAP2Alter( ) (PIN_FUNC |= bT2EX_PIN_X;) //CAP2由P11 映射RST 31 | /******************************************************************************* 32 | * Function Name : mTimer_x_ModInit(UINT8 x ,UINT8 mode) 33 | * Description : CH549定时计数器x模式设置 34 | * Input : UINT8 mode,Timer模式选择 35 | 0:模式0,13位定时器,TLn的高3位无效 36 | 1:模式1,16位定时器 37 | 2:模式2,8位自动重装定时器 38 | 3:模式3,两个8位定时器 Timer0 39 | 3:模式3,Timer1停止 40 | UINT8 x 定时器 0 1 2 41 | * Output : None 42 | * Return : 成功 SUCCESS 43 | 失败 FAIL 44 | *******************************************************************************/ 45 | UINT8 mTimer_x_ModInit(UINT8 x ,UINT8 mode); 46 | /******************************************************************************* 47 | * Function Name : mTimer_x_SetData(UINT8 x,UINT16 dat) 48 | * Description : CH549Timer 49 | * Input : UINT16 dat;定时器赋值 50 | UINT8 x 定时器 0 1 2 51 | * Output : None 52 | * Return : None 53 | *******************************************************************************/ 54 | void mTimer_x_SetData(UINT8 x,UINT16 dat); 55 | /******************************************************************************* 56 | * Function Name : CAP2Init(UINT8 mode) 57 | * Description : CH549定时计数器2 T2EX引脚捕捉功能初始化(CAP2 P11) 58 | UINT8 mode,边沿捕捉模式选择 59 | 0:T2ex从下降沿到下一个下降沿 60 | 1:T2ex任意边沿之间 61 | 3:T2ex从上升沿到下一个上升沿 62 | * Input : None 63 | * Output : None 64 | * Return : None 65 | *******************************************************************************/ 66 | void CAP2Init(UINT8 mode); 67 | /******************************************************************************* 68 | * Function Name : CAP1Init(UINT8 mode) 69 | * Description : CH549定时计数器2 T2引脚捕捉功能初始化T2(CAP1 P10) 70 | UINT8 mode,边沿捕捉模式选择 71 | 0:T2ex从下降沿到下一个下降沿 72 | 1:T2ex任意边沿之间 73 | 3:T2ex从上升沿到下一个上升沿 74 | * Input : None 75 | * Output : None 76 | * Return : None 77 | *******************************************************************************/ 78 | void CAP1Init(UINT8 mode); 79 | /******************************************************************************* 80 | * Function Name : CAP0Init(UINT8 mode) 81 | * Description : CH549定时计数器2 CAP0引脚捕捉功能初始化(CAP0 P36) 82 | UINT8 mode,边沿捕捉模式选择 83 | 0:T2ex从下降沿到下一个下降沿 84 | 1:T2ex任意边沿之间 85 | 3:T2ex从上升沿到下一个上升沿 86 | * Input : None 87 | * Output : None 88 | * Return : None 89 | *******************************************************************************/ 90 | void CAP0Init(UINT8 mode); 91 | #endif 92 | -------------------------------------------------------------------------------- /Firmware/Lib/UART.C: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Firmware/Lib/UART.C -------------------------------------------------------------------------------- /Firmware/Lib/UART.H: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/Firmware/Lib/UART.H -------------------------------------------------------------------------------- /Firmware/ProJ.uvopt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.0 5 | 6 |
### uVision Project, (C) Keil Software
7 | 8 | 9 | *.c 10 | *.s*; *.src; *.a* 11 | *.obj; *.o 12 | *.lib 13 | *.txt; *.h; *.inc; *.md 14 | *.plm 15 | *.cpp; *.cc; *.cxx 16 | 0 17 | 18 | 19 | 20 | 0 21 | 0 22 | 23 | 24 | 25 | Target 1 26 | 0x0 27 | MCS-51 28 | 29 | 48000000 30 | 31 | 1 32 | 1 33 | 1 34 | 0 35 | 0 36 | 37 | 38 | 0 39 | 65535 40 | 0 41 | 0 42 | 0 43 | 44 | 45 | 120 46 | 65 47 | 8 48 | .\Listings\ 49 | 50 | 51 | 1 52 | 1 53 | 1 54 | 0 55 | 1 56 | 1 57 | 0 58 | 1 59 | 0 60 | 0 61 | 0 62 | 0 63 | 64 | 65 | 1 66 | 1 67 | 1 68 | 1 69 | 1 70 | 1 71 | 1 72 | 0 73 | 0 74 | 75 | 76 | 1 77 | 0 78 | 1 79 | 80 | 0 81 | 82 | 1 83 | 0 84 | 1 85 | 1 86 | 1 87 | 1 88 | 1 89 | 1 90 | 1 91 | 1 92 | 0 93 | 1 94 | 1 95 | 1 96 | 0 97 | 1 98 | 1 99 | 1 100 | 1 101 | 0 102 | 0 103 | 1 104 | 0 105 | 0 106 | -1 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 0 122 | 123 | 124 | 0 125 | 1 126 | 0 127 | 0 128 | 0 129 | 0 130 | 0 131 | 0 132 | 0 133 | 0 134 | 0 135 | 0 136 | 0 137 | 0 138 | 0 139 | 0 140 | 0 141 | 0 142 | 0 143 | 0 144 | 0 145 | 0 146 | 0 147 | 0 148 | 149 | 150 | 151 | 0 152 | 0 153 | 0 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | User 167 | 1 168 | 0 169 | 0 170 | 0 171 | 172 | 1 173 | 1 174 | 1 175 | 0 176 | 0 177 | 0 178 | .\User\main.C 179 | main.C 180 | 0 181 | 0 182 | 183 | 184 | 185 | 186 | Lib 187 | 1 188 | 0 189 | 0 190 | 0 191 | 192 | 2 193 | 2 194 | 1 195 | 0 196 | 0 197 | 0 198 | .\Lib\DEBUG.C 199 | DEBUG.C 200 | 0 201 | 0 202 | 203 | 204 | 2 205 | 3 206 | 1 207 | 0 208 | 0 209 | 0 210 | .\Lib\GPIO.C 211 | GPIO.C 212 | 0 213 | 0 214 | 215 | 216 | 2 217 | 4 218 | 1 219 | 0 220 | 0 221 | 0 222 | .\Lib\Timer.C 223 | Timer.C 224 | 0 225 | 0 226 | 227 | 228 | 229 |
230 | -------------------------------------------------------------------------------- /Firmware/ProJ.uvproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.1 5 | 6 |
### uVision Project, (C) Keil Software
7 | 8 | 9 | 10 | Target 1 11 | 0x0 12 | MCS-51 13 | 0 14 | 15 | 16 | CH549 17 | WCH 18 | IRAM(0-0xFF) XRAM(0-0x3FFF) IROM(0-0x3FFF) CLOCK(48000000) 19 | 20 | "LIB\STARTUP.A51" ("Standard 8051 Startup Code") 21 | 22 | 0 23 | CH549.H 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 0 35 | 0 36 | 37 | 38 | 39 | WCH\ 40 | WCH\ 41 | 42 | 0 43 | 0 44 | 0 45 | 0 46 | 1 47 | 48 | .\Objects\ 49 | ProJ 50 | 1 51 | 0 52 | 1 53 | 1 54 | 1 55 | .\Listings\ 56 | 0 57 | 0 58 | 0 59 | 60 | 0 61 | 0 62 | 63 | 64 | 0 65 | 0 66 | 0 67 | 0 68 | 69 | 70 | 0 71 | 0 72 | 73 | 74 | 0 75 | 0 76 | 0 77 | 0 78 | 79 | 80 | 0 81 | 0 82 | 83 | 84 | 0 85 | 0 86 | 0 87 | 0 88 | 89 | 0 90 | 91 | 92 | 93 | 0 94 | 0 95 | 0 96 | 0 97 | 0 98 | 1 99 | 0 100 | 0 101 | 0 102 | 0 103 | 3 104 | 105 | 106 | 1 107 | 65535 108 | 109 | 110 | S8051.DLL 111 | 112 | DP51.DLL 113 | -pDR8051 114 | S8051.DLL 115 | 116 | TP51.DLL 117 | -p51 118 | 119 | 120 | 121 | 0 122 | 0 123 | 0 124 | 0 125 | 16 126 | 127 | 128 | 1 129 | 1 130 | 1 131 | 1 132 | 1 133 | 1 134 | 1 135 | 1 136 | 0 137 | 1 138 | 139 | 140 | 0 141 | 1 142 | 0 143 | 1 144 | 1 145 | 1 146 | 0 147 | 1 148 | 1 149 | 1 150 | 151 | 0 152 | -1 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 0 172 | 0 173 | 0 174 | 0 175 | 0 176 | -1 177 | 178 | 0 179 | 180 | "" () 181 | 182 | 183 | 184 | 185 | 0 186 | 187 | 188 | 189 | 2 190 | 0 191 | 2 192 | 0 193 | 0 194 | 0 195 | 0 196 | 0 197 | 0 198 | 1 199 | 1 200 | 1 201 | 0 202 | 0 203 | 0 204 | 0 205 | 0 206 | 0 207 | 0 208 | 0 209 | 0 210 | 0 211 | 0 212 | 0 213 | 0 214 | 0 215 | 0 216 | 0 217 | 0 218 | 0 219 | 0 220 | 0 221 | 0 222 | 0 223 | 0 224 | 0 225 | 0 226 | 0 227 | 0 228 | 0 229 | 0 230 | 0 231 | 0 232 | 233 | 234 | 0 235 | 0x0 236 | 0xffff 237 | 238 | 239 | 0 240 | 0x0 241 | 0x0 242 | 243 | 244 | 0 245 | 0x0 246 | 0x0 247 | 248 | 249 | 0 250 | 0x0 251 | 0x0 252 | 253 | 254 | 0 255 | 0x0 256 | 0x0 257 | 258 | 259 | 0 260 | 0x0 261 | 0x0 262 | 263 | 264 | 0 265 | 0x0 266 | 0x0 267 | 268 | 269 | 0 270 | 0x0 271 | 0x0 272 | 273 | 274 | 1 275 | 0x0 276 | 0x4000 277 | 278 | 279 | 0 280 | 0x0 281 | 0x100 282 | 283 | 284 | 0 285 | 0x0 286 | 0x4000 287 | 288 | 289 | 0 290 | 0x0 291 | 0x0 292 | 293 | 294 | 0 295 | 0x0 296 | 0x0 297 | 298 | 299 | 0 300 | 0x0 301 | 0x0 302 | 303 | 304 | 0 305 | 0x0 306 | 0x0 307 | 308 | 309 | 310 | 311 | 0 312 | 0 313 | 1 314 | 0 315 | 1 316 | 3 317 | 8 318 | 2 319 | 1 320 | 1 321 | 0 322 | 0 323 | 324 | 325 | 326 | 327 | .\Lib 328 | 329 | 330 | 331 | 0 332 | 1 333 | 0 334 | 0 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 0 344 | 0 345 | 1 346 | 0 347 | 2 348 | 1 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | User 378 | 379 | 380 | main.C 381 | 1 382 | .\User\main.C 383 | 384 | 385 | 386 | 387 | Lib 388 | 389 | 390 | DEBUG.C 391 | 1 392 | .\Lib\DEBUG.C 393 | 394 | 395 | GPIO.C 396 | 1 397 | .\Lib\GPIO.C 398 | 399 | 400 | Timer.C 401 | 1 402 | .\Lib\Timer.C 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 |
411 | -------------------------------------------------------------------------------- /Firmware/User/main.C: -------------------------------------------------------------------------------- 1 | /********************************** (i) information 2 | ******************************** File Name : main.C Author : E Version 3 | *: V1.0 Date : 2022/09/22 Description : 4 | *USB键盘鼠标收发程序,串口传输HID报文 5 | *******************************************************************************/ 6 | #include "CH549.H" 7 | #include "DEBUG.H" 8 | #include "GPIO.H" 9 | 10 | #define Fullspeed 11 | 12 | #ifdef Fullspeed 13 | #define THIS_ENDP0_SIZE 64 14 | #else 15 | #define THIS_ENDP0_SIZE 8 //低速USB,中断传输、控制传输最大包长度为8 16 | #endif 17 | 18 | UINT8X Ep0Buffer[THIS_ENDP0_SIZE + 19 | 2] _at_ 0x0000; //端点0 OUT&IN缓冲区,必须是偶地址 20 | UINT8X Ep2Buffer[2 * MAX_PACKET_SIZE + 4] _at_ THIS_ENDP0_SIZE + 21 | 2; //端点2 IN&OUT缓冲区,必须是偶地址 22 | UINT8 SetupReq, Ready, UsbConfig; 23 | UINT16 SetupLen; 24 | PUINT8 pDescr; // USB配置标志 25 | USB_SETUP_REQ SetupReqBuf; //暂存Setup包 26 | #define UsbSetupBuf ((PUSB_SETUP_REQ)Ep0Buffer) 27 | #pragma NOAREGS 28 | 29 | /*字符串描述符 略*/ 30 | /*HID类报表描述符*/ 31 | UINT8C HIDRepDesc[] = 32 | { 33 | 34 | /* keyboared */ 35 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 36 | 0x09, 0x06, // USAGE (Keyboard) 37 | 0xa1, 0x01, // COLLECTION (Application) 38 | 0x85, 0x01, // Report ID (1) 39 | 0x05, 0x07, // USAGE_PAGE (Keyboard) 40 | 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 41 | 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 42 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 43 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 44 | 0x75, 0x01, // REPORT_SIZE (1) 45 | 0x95, 0x08, // REPORT_COUNT (8) 46 | 0x81, 0x02, // INPUT (Data,Var,Abs) 47 | 0x95, 0x01, // REPORT_COUNT (1) 48 | 0x75, 0x08, // REPORT_SIZE (8) 49 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 50 | 0x95, 0x05, // REPORT_COUNT (5) 51 | 0x75, 0x01, // REPORT_SIZE (1) 52 | 0x05, 0x08, // USAGE_PAGE (LEDs) 53 | 0x19, 0x01, // USAGE_MINIMUM (Num Lock) 54 | 0x29, 0x05, // USAGE_MAXIMUM (Kana) 55 | 0x91, 0x02, // OUTPUT (Data,Var,Abs) 56 | 0x95, 0x01, // REPORT_COUNT (1) 57 | 0x75, 0x03, // REPORT_SIZE (3) 58 | 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) 59 | 0x95, 0x06, // REPORT_COUNT (6) 60 | 0x75, 0x08, // REPORT_SIZE (8) 61 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 62 | 0x25, 0xFF, // LOGICAL_MAXIMUM (255) 63 | 0x05, 0x07, // USAGE_PAGE (Keyboard) 64 | 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 65 | 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) 66 | 0x81, 0x00, // INPUT (Data,Ary,Abs) 67 | 0xc0, // END_COLLECTION /* 65 */ 68 | 69 | /* consumer */ 70 | 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) 71 | 0x09, 0x01, // USAGE (Consumer Control) 72 | 0xa1, 0x01, // COLLECTION (Application) 73 | 0x85, 0x02, // REPORT_ID (2) 74 | 0x19, 0x00, // USAGE_MINIMUM (Unassigned) 75 | 0x2a, 0x3c, 0x03, // USAGE_MAXIMUM 76 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 77 | 0x26, 0x3c, 0x03, // LOGICAL_MAXIMUM (828) 78 | 0x95, 0x01, // REPORT_COUNT (1) 79 | 0x75, 0x10, // REPORT_SIZE (16) 80 | 0x81, 0x00, // INPUT (Data,Ary,Abs) 81 | 0xc0, // END_COLLECTION /* 25 */ 82 | 83 | /* mouse */ 84 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 85 | 0x09, 0x02, // USAGE (Mouse) 86 | 0xa1, 0x01, // COLLECTION (Application) 87 | 0x85, 0x03, // REPORT_ID (3) 88 | 0x09, 0x01, // USAGE (Pointer) 89 | 0xa1, 0x00, // COLLECTION (Physical) 90 | 0x05, 0x09, // USAGE_PAGE (Button) 91 | 0x19, 0x01, // USAGE_MINIMUM (Button 1) 92 | 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 93 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 94 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 95 | 0x95, 0x03, // REPORT_COUNT (3) 96 | 0x75, 0x01, // REPORT_SIZE (1) 97 | 0x81, 0x02, // INPUT (Data,Var,Abs) 98 | 0x95, 0x01, // REPORT_COUNT (1) 99 | 0x75, 0x05, // REPORT_SIZE (5) 100 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 101 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 102 | 0x09, 0x30, // USAGE (X) 103 | 0x09, 0x31, // USAGE (Y) 104 | 0x09, 0x38, // Usage (Wheel) 105 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) 106 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 107 | 0x75, 0x08, // REPORT_SIZE (8) 108 | 0x95, 0x03, // REPORT_COUNT (3) 109 | 0x81, 0x06, // INPUT (Data,Var,Rel) 110 | 0xc0, // END_COLLECTION 111 | 0xc0, // END_COLLECTION 112 | /* 113 | 114 | 115 | /* Vendor */ 116 | 0x06, 0xB1, 0xFF, // Usage Page (Vendor-Defined 178) 117 | 0x09, 0x01, // Usage (Vendor-Defined 1) 118 | 0xA1, 0x01, // Collection (Application) 119 | 120 | 0x85, 0x04, // REPORT_ID (4) 121 | 0x09, 0x04, // Usage (Vendor-Defined 4) 122 | 0x15, 0x00, // Logical Minimum (0) 123 | 0x26, 0xFF, 0x00, // Logical Maximum (255) 124 | 0x75, 0x08, // Report Size (8) 125 | 0x95, 0x20, // Report Count (10) 126 | 0x91, 0x02, // Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 127 | 128 | 0x09, 0x01, // Usage (0x01) 129 | 0x25, 0x00, // Logical Maximum (0) 130 | 0x26, 0xFF, 0x00, // Logical Maximum (255) 131 | 0x75, 0x08, // Report Size (10) 132 | 0x95, 0x20, // Report Count (32) 133 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null 134 | // Position) 135 | 136 | 0xC0, // End Collection /* 37 */ 137 | 138 | }; 139 | 140 | /*设备描述符*/ 141 | UINT8C DevDesc[] = 142 | { 143 | 0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, THIS_ENDP0_SIZE, 144 | // 0x86, 0x1A, 0xE0, 0xE6, 0x00, 0x00, 0x01, 0x02, 145 | // //0x86, 0x1A,VID 0xE0, 0xE6,PID 146 | 0x86, 0x2B, 0xE0, 0xE6, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01 147 | }; 148 | UINT8C CfgDesc[] = 149 | { 150 | 0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0xA0, 0x32, //配置描述符 151 | 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x05, //接口描述符 152 | // 0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x22, 0x00, //HID类描述符 153 | // wDescriptorLength = 0x22 154 | 0x09, 0x21, 0x01, 0x01, 0x21, 0x01, 0x22, sizeof(HIDRepDesc) & 0xFF, 155 | sizeof(HIDRepDesc) >> 8, // HID类描述符 修改wDescriptorLength 长度为0x10B 156 | 157 | #ifdef Fullspeed 158 | 0x07, 0x05, 0x82, 0x03, THIS_ENDP0_SIZE, 0x00, 159 | 0x01, //端点描述符(全速间隔时间改成1ms) 160 | 0x07, 0x05, 0x02, 0x03, THIS_ENDP0_SIZE, 0x00, 0x01, //端点描述符 161 | #else 162 | 0x07, 0x05, 0x82, 0x03, THIS_ENDP0_SIZE, 0x00, 163 | 0x0A, //端点描述符(低速间隔时间最小10ms) 164 | 0x07, 0x05, 0x02, 0x03, THIS_ENDP0_SIZE, 0x00, 0x0A, //端点描述符 165 | #endif 166 | }; 167 | 168 | // 语言描述符 169 | UINT8C MyLangDescr[] = {0x04, 0x03, 0x09, 0x04}; 170 | // 厂家信息 171 | UINT8C 172 | MyManuInfo[] = {0x30, 0x03, 'M', 0, 'o', 0, 'y', 0, 'u', 0, ' ', 0, 'a', 0, 173 | 't', 0, ' ', 0, 'w', 0, 'o', 0, 'r', 0, 'k', 0, ' ', 0, 174 | 'T', 0, 'e', 0, 'c', 0, 'h', 0, 'n', 0, 'o', 0, 'l', 0, 175 | 'o', 0, 'g', 0, 'y', 0 176 | }; // MoYu at work Technology 177 | // 产品信息 178 | UINT8C MyProdInfo[] = 179 | { 180 | 0x1C, 0x03, 'K', 0, 'V', 0, 'M', 0, ' ', 0, 'o', 0, 'v', 0, 181 | 'e', 0, 'r', 0, ' ', 0, 'U', 0, 'S', 0, 'B', 0, ' ', 0, 182 | }; //字符串描述符 13*2bit + 2bit 183 | 184 | sbit MODE_PORT_0 = P3 ^ 4; 185 | BOOL MODE_STATUS = 0; 186 | 187 | UINT8X i = 0; 188 | 189 | UINT8X HID_OUT_report[33] = {0}; //接收到的上位机数据 190 | BOOL HID_OUT_report_FLAG = 0; //接收到上位机FLAG 191 | 192 | UINT8X SendHID[33] = {0}; //发送HID报文数组 193 | UINT8X UserEp2Buf[64];//用户数据定义 194 | UINT8 Endp2Busy = 0; 195 | 196 | #define S_RECEVIER_SIZE 32 197 | UINT8X RevBuffer[S_RECEVIER_SIZE]; 198 | UINT8 revDataCount = 0; 199 | UINT8 revTempLength = 0; 200 | /******************************************************************************* 201 | * Function Name : USBDeviceInit() 202 | * Description : USB设备模式配置,设备模式启动,收发端点配置,中断开启 203 | * Input : None 204 | * Output : None 205 | * Return : None 206 | *******************************************************************************/ 207 | void USBDeviceInit() 208 | { 209 | IE_USB = 0; 210 | USB_CTRL = 0x00; // 先设定USB设备模式 211 | UDEV_CTRL = bUD_PD_DIS; // 禁止DP/DM下拉电阻 212 | #ifndef Fullspeed 213 | UDEV_CTRL |= bUD_LOW_SPEED; //选择低速1.5M模式 214 | USB_CTRL |= bUC_LOW_SPEED; 215 | #else 216 | UDEV_CTRL &= ~bUD_LOW_SPEED; //选择全速12M模式,默认方式 217 | USB_CTRL &= ~bUC_LOW_SPEED; 218 | #endif 219 | UEP2_T_LEN = 0; //预使用发送长度一定要清空 220 | UEP2_DMA = Ep2Buffer; //端点2数据传输地址 221 | UEP2_3_MOD |= bUEP2_TX_EN | bUEP2_RX_EN; //端点2发送接收使能 222 | UEP2_3_MOD &= ~bUEP2_BUF_MOD; //端点2收发各64字节缓冲区 223 | UEP2_CTRL = 224 | bUEP_AUTO_TOG | UEP_T_RES_NAK | 225 | UEP_R_RES_ACK; //端点2自动翻转同步标志位,IN事务返回NAK,OUT返回ACK 226 | UEP0_DMA = Ep0Buffer; //端点0数据传输地址 227 | UEP4_1_MOD &= ~(bUEP4_RX_EN | bUEP4_TX_EN); //端点0单64字节收发缓冲区 228 | UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; // OUT事务返回ACK,IN事务返回NAK 229 | USB_DEV_AD = 0x00; 230 | USB_CTRL |= 231 | bUC_DEV_PU_EN | bUC_INT_BUSY | 232 | bUC_DMA_EN; // 启动USB设备及DMA,在中断期间中断标志未清除前自动返回NAK 233 | UDEV_CTRL |= bUD_PORT_EN; // 允许USB端口 234 | USB_INT_FG = 0xFF; // 清中断标志 235 | USB_INT_EN = bUIE_SUSPEND | bUIE_TRANSFER | bUIE_BUS_RST; 236 | IE_USB = 1; 237 | } 238 | /******************************************************************************* 239 | * Function Name : Enp2BlukIn() 240 | * Description : USB设备模式端点2的批量上传 241 | * Input : None 242 | * Output : None 243 | * Return : None 244 | *******************************************************************************/ 245 | void Enp2BlukIn(UINT8 *buf, UINT8 len) 246 | { 247 | memcpy(Ep2Buffer + MAX_PACKET_SIZE, buf, len); //加载上传数据 248 | UEP2_T_LEN = len; //上传最大包长度 249 | UEP2_CTRL = 250 | UEP2_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_ACK; //有数据时上传数据并应答ACK 251 | } 252 | /******************************************************************************* 253 | * Function Name : DeviceInterrupt() 254 | * Description : CH559USB中断处理函数 255 | *******************************************************************************/ 256 | void DeviceInterrupt(void) interrupt INT_NO_USB 257 | using 1 // USB中断服务程序,使用寄存器组1 258 | { 259 | UINT8 i; 260 | UINT16 len; 261 | 262 | if (UIF_TRANSFER) // USB传输完成标志 263 | { 264 | switch (USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP)) 265 | { 266 | case UIS_TOKEN_IN | 2: // endpoint 2# 端点批量上传 267 | UEP2_T_LEN = 0; //预使用发送长度一定要清空 268 | // UEP1_CTRL ^= bUEP_T_TOG; //如果不设置自动翻转则需要手动翻转 269 | Endp2Busy = 0; 270 | UEP2_CTRL = UEP2_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_NAK; //默认应答NAK 271 | break; 272 | 273 | case UIS_TOKEN_OUT | 2: // endpoint 2# 端点批量下传 274 | if (U_TOG_OK) // 不同步的数据包将丢弃 275 | { 276 | 277 | len = USB_RX_LEN; //接收数据长度,数据从Ep2Buffer首地址开始存放 278 | #ifdef DE_PRINTF 279 | // printf("[HID]OUT "); 280 | #endif 281 | 282 | for (i = 0; i < len; i++) 283 | { 284 | #ifdef DE_PRINTF 285 | // printf("0x%02x ", (UINT16)Ep2Buffer[i]); //遍历OUT数据 286 | #endif 287 | 288 | HID_OUT_report[i] = Ep2Buffer[i]; //赋值到HID_OUT_report 289 | 290 | // Ep2Buffer[MAX_PACKET_SIZE + i] = Ep2Buffer[i] ^ 0xFF; // 291 | // OUT数据取反到IN由计算机验证 292 | } 293 | 294 | HID_OUT_report_FLAG = 1; 295 | 296 | #ifdef DE_PRINTF 297 | // printf("len=%u\r\n", len); 298 | #endif 299 | 300 | // UEP2_T_LEN = len; 301 | // UEP2_CTRL = UEP2_CTRL & ~MASK_UEP_T_RES | 302 | // UEP_T_RES_ACK; // 允许上传 303 | } 304 | 305 | break; 306 | 307 | case UIS_TOKEN_SETUP | 0: // SETUP事务 308 | UEP0_CTRL = UEP0_CTRL & (~MASK_UEP_T_RES) | 309 | UEP_T_RES_NAK; //预置NAK,防止stall之后不及时清除响应方式 310 | len = USB_RX_LEN; 311 | 312 | if (len == (sizeof(USB_SETUP_REQ))) 313 | { 314 | SetupLen = ((UINT16)UsbSetupBuf->wLengthH << 8) + UsbSetupBuf->wLengthL; 315 | len = 0; // 默认为成功并且上传0长度 316 | SetupReq = UsbSetupBuf->bRequest; 317 | 318 | if ((UsbSetupBuf->bRequestType & USB_REQ_TYP_MASK) != 319 | USB_REQ_TYP_STANDARD) /*HID类命令*/ 320 | { 321 | Ready = 1; // HID类命令一般代表usb枚举完成的标志 322 | 323 | switch (SetupReq) 324 | { 325 | // Ready = 1; 326 | // //HID类命令一般代表usb枚举完成的标志 327 | 328 | case 0x01: // GetReport 329 | pDescr = UserEp2Buf; //控制端点上传输据 330 | 331 | if (SetupLen >= THIS_ENDP0_SIZE) //大于端点0大小,需要特殊处理 332 | { 333 | len = THIS_ENDP0_SIZE; 334 | } 335 | else 336 | { 337 | len = SetupLen; 338 | } 339 | 340 | break; 341 | 342 | case 0x02: // GetIdle 343 | break; 344 | 345 | case 0x03: // GetProtocol 346 | break; 347 | 348 | case 0x09: // SetReport 349 | break; 350 | 351 | case 0x0A: // SetIdle 352 | break; 353 | 354 | case 0x0B: // SetProtocol 355 | break; 356 | 357 | default: 358 | len = 0xFFFF; /*命令不支持*/ 359 | break; 360 | } 361 | 362 | if (SetupLen > len) 363 | { 364 | SetupLen = len; //限制总长度 365 | } 366 | 367 | len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE 368 | : SetupLen; //本次传输长度 369 | memcpy(Ep0Buffer, pDescr, len); //加载上传数据 370 | SetupLen -= len; 371 | pDescr += len; 372 | } 373 | else //标准请求 374 | { 375 | switch (SetupReq) //请求码 376 | { 377 | case USB_GET_DESCRIPTOR: 378 | switch (UsbSetupBuf->wValueH) 379 | { 380 | case 1: //设备描述符 381 | pDescr = DevDesc; //把设备描述符送到要发送的缓冲区 382 | len = sizeof(DevDesc); 383 | break; 384 | 385 | case 2: //配置描述符 386 | pDescr = CfgDesc; //把设备描述符送到要发送的缓冲区 387 | len = sizeof(CfgDesc); 388 | break; 389 | 390 | case 3: 391 | switch (UsbSetupBuf->wValueL) 392 | { 393 | case 1: 394 | pDescr = (PUINT8)(&MyManuInfo[0]); 395 | len = sizeof(MyManuInfo); 396 | break; 397 | 398 | case 2: 399 | pDescr = (PUINT8)(&MyProdInfo[0]); 400 | len = sizeof(MyProdInfo); 401 | break; 402 | 403 | case 0: 404 | pDescr = (PUINT8)(&MyLangDescr[0]); 405 | len = sizeof(MyLangDescr); 406 | break; 407 | 408 | default: 409 | len = 0xFFFF; // 不支持的字符串描述符 410 | break; 411 | } 412 | 413 | break; 414 | 415 | case 0x22: //报表描述符 416 | pDescr = HIDRepDesc; //数据准备上传 417 | len = sizeof(HIDRepDesc); 418 | break; 419 | 420 | default: 421 | len = 0xFFFF; //不支持的命令或者出错 422 | break; 423 | } 424 | 425 | if (SetupLen > len) 426 | { 427 | SetupLen = len; //限制总长度 428 | } 429 | 430 | len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE 431 | : SetupLen; //本次传输长度 432 | memcpy(Ep0Buffer, pDescr, len); //加载上传数据 433 | SetupLen -= len; 434 | pDescr += len; 435 | break; 436 | 437 | case USB_SET_ADDRESS: 438 | SetupLen = UsbSetupBuf->wValueL; //暂存USB设备地址 439 | break; 440 | 441 | case USB_GET_CONFIGURATION: 442 | Ep0Buffer[0] = UsbConfig; 443 | 444 | if (SetupLen >= 1) 445 | { 446 | len = 1; 447 | } 448 | 449 | break; 450 | 451 | case USB_SET_CONFIGURATION: 452 | UsbConfig = UsbSetupBuf->wValueL; 453 | 454 | if (UsbConfig) 455 | { 456 | Ready = 1; // set config命令一般代表usb枚举完成的标志 457 | } 458 | 459 | break; 460 | 461 | case 0x0A: 462 | break; 463 | 464 | case USB_CLEAR_FEATURE: // Clear Feature 465 | if ((UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK) == 466 | USB_REQ_RECIP_ENDP) // 端点 467 | { 468 | switch (UsbSetupBuf->wIndexL) 469 | { 470 | case 0x82: 471 | UEP2_CTRL = 472 | UEP2_CTRL & ~(bUEP_T_TOG | MASK_UEP_T_RES) | UEP_T_RES_NAK; 473 | break; 474 | 475 | case 0x81: 476 | UEP1_CTRL = 477 | UEP1_CTRL & ~(bUEP_T_TOG | MASK_UEP_T_RES) | UEP_T_RES_NAK; 478 | break; 479 | 480 | case 0x02: 481 | UEP2_CTRL = 482 | UEP2_CTRL & ~(bUEP_R_TOG | MASK_UEP_R_RES) | UEP_R_RES_ACK; 483 | break; 484 | 485 | default: 486 | len = 0xFFFF; // 不支持的端点 487 | break; 488 | } 489 | } 490 | else 491 | { 492 | len = 0xFFFF; // 不是端点不支持 493 | } 494 | 495 | break; 496 | 497 | case USB_SET_FEATURE: /* Set Feature */ 498 | if ((UsbSetupBuf->bRequestType & 0x1F) == 0x00) /* 设置设备 */ 499 | { 500 | if ((((UINT16)UsbSetupBuf->wValueH << 8) | 501 | UsbSetupBuf->wValueL) == 0x01) 502 | { 503 | if (CfgDesc[7] & 0x20) 504 | { 505 | /* 设置唤醒使能标志 */ 506 | } 507 | else 508 | { 509 | len = 0xFFFF; /* 操作失败 */ 510 | } 511 | } 512 | else 513 | { 514 | len = 0xFFFF; /* 操作失败 */ 515 | } 516 | } 517 | else if ((UsbSetupBuf->bRequestType & 0x1F) == 518 | 0x02) /* 设置端点 */ 519 | { 520 | if ((((UINT16)UsbSetupBuf->wValueH << 8) | 521 | UsbSetupBuf->wValueL) == 0x00) 522 | { 523 | switch (((UINT16)UsbSetupBuf->wIndexH << 8) | 524 | UsbSetupBuf->wIndexL) 525 | { 526 | case 0x82: 527 | UEP2_CTRL = UEP2_CTRL & (~bUEP_T_TOG) | 528 | UEP_T_RES_STALL; /* 设置端点2 IN STALL */ 529 | break; 530 | 531 | case 0x02: 532 | UEP2_CTRL = UEP2_CTRL & (~bUEP_R_TOG) | 533 | UEP_R_RES_STALL; /* 设置端点2 OUT Stall */ 534 | break; 535 | 536 | case 0x81: 537 | UEP1_CTRL = UEP1_CTRL & (~bUEP_T_TOG) | 538 | UEP_T_RES_STALL; /* 设置端点1 IN STALL */ 539 | break; 540 | 541 | default: 542 | len = 0xFFFF; /* 操作失败 */ 543 | break; 544 | } 545 | } 546 | else 547 | { 548 | len = 0xFFFF; /* 操作失败 */ 549 | } 550 | } 551 | else 552 | { 553 | len = 0xFFFF; /* 操作失败 */ 554 | } 555 | 556 | break; 557 | 558 | case USB_GET_STATUS: 559 | Ep0Buffer[0] = 0x00; 560 | Ep0Buffer[1] = 0x00; 561 | 562 | if (SetupLen >= 2) 563 | { 564 | len = 2; 565 | } 566 | else 567 | { 568 | len = SetupLen; 569 | } 570 | 571 | break; 572 | 573 | default: 574 | len = 0xFFFF; //操作失败 575 | break; 576 | } 577 | } 578 | } 579 | else 580 | { 581 | len = 0xFFFF; //包长度错误 582 | } 583 | 584 | if (len == 0xFFFF) 585 | { 586 | SetupReq = 0xFF; 587 | UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_STALL | 588 | UEP_T_RES_STALL; // STALL 589 | } 590 | else if (len <= THIS_ENDP0_SIZE) //上传数据或者状态阶段返回0长度包 591 | { 592 | UEP0_T_LEN = len; 593 | UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | 594 | UEP_T_RES_ACK; //默认数据包是DATA1,返回应答ACK 595 | } 596 | else 597 | { 598 | UEP0_T_LEN = 599 | 0; //虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段 600 | UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | 601 | UEP_T_RES_ACK; //默认数据包是DATA1,返回应答ACK 602 | } 603 | 604 | break; 605 | 606 | case UIS_TOKEN_IN | 0: // endpoint0 IN 607 | switch (SetupReq) 608 | { 609 | case USB_GET_DESCRIPTOR: 610 | case HID_GET_REPORT: 611 | len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE 612 | : SetupLen; //本次传输长度 613 | memcpy(Ep0Buffer, pDescr, len); //加载上传数据 614 | SetupLen -= len; 615 | pDescr += len; 616 | UEP0_T_LEN = len; 617 | UEP0_CTRL ^= bUEP_T_TOG; //同步标志位翻转 618 | break; 619 | 620 | case USB_SET_ADDRESS: 621 | USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | SetupLen; 622 | UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; 623 | break; 624 | 625 | default: 626 | UEP0_T_LEN = 0; //状态阶段完成中断或者是强制上传0长度数据包结束控制传输 627 | UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; 628 | break; 629 | } 630 | 631 | break; 632 | 633 | case UIS_TOKEN_OUT | 0: // endpoint0 OUT 634 | len = USB_RX_LEN; 635 | UEP0_CTRL ^= bUEP_R_TOG; //同步标志位翻转 636 | break; 637 | 638 | default: 639 | break; 640 | } 641 | 642 | UIF_TRANSFER = 0; //写0清空中断 643 | } 644 | else if (UIF_BUS_RST) //设备模式USB总线复位中断 645 | { 646 | UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; 647 | UEP2_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK; 648 | USB_DEV_AD = 0x00; 649 | UIF_SUSPEND = 0; 650 | UIF_TRANSFER = 0; 651 | Endp2Busy = 0; 652 | Ready = 0; 653 | UIF_BUS_RST = 0; //清中断标志 654 | } 655 | else if (UIF_SUSPEND) // USB总线挂起/唤醒完成 656 | { 657 | UIF_SUSPEND = 0; 658 | 659 | if (USB_MIS_ST & bUMS_SUSPEND) //挂起 660 | { 661 | #ifdef DE_PRINTF 662 | printf("[USB]sleep...\r\n"); //睡眠状态 663 | #endif 664 | // while ( XBUS_AUX & bUART0_TX ) 665 | // { 666 | // ; //等待发送完成 667 | // } 668 | // SAFE_MOD = 0x55; 669 | // SAFE_MOD = 0xAA; 670 | // WAKE_CTRL = bWA K_BY_USB | bWAK_RXD0_LO; 671 | // //USB或者RXD0有信号时可被唤醒 PCON |= PD; //睡眠 SAFE_MOD = 672 | // 0x55; SAFE_MOD = 0xAA; WAKE_CTRL = 0x00; 673 | } 674 | else // 唤醒 675 | { 676 | #ifdef DE_PRINTF 677 | printf("[USB]Wake up!\r\n"); 678 | #endif 679 | } 680 | } 681 | else //意外的中断,不可能发生的情况 682 | { 683 | USB_INT_FG = 0xFF; //清中断标志 684 | #ifdef DE_PRINTF 685 | printf("[USB]UnknownInt \r\n"); 686 | #endif 687 | } 688 | } 689 | 690 | void REPORT_HANDLE(void) 691 | { 692 | 693 | HID_OUT_report_FLAG = 0; 694 | 695 | if (HID_OUT_report[0] == 4) //将HID报文转发到串口 696 | { 697 | 698 | CH549UART0SendByte(0xf0); //使用0xf0作为起始符 699 | for (i = 0; i < sizeof(HID_OUT_report); i++) 700 | { 701 | CH549UART0SendByte(HID_OUT_report[i]); //遍历OUT数据 702 | } 703 | CH549UART0SendByte(0xf1);//使用0xf1作为终止符 704 | 705 | } 706 | 707 | if (HID_OUT_report[0] == 4 && HID_OUT_report[1] == 4) //自定义功能 708 | { 709 | 710 | if (HID_OUT_report[1] == 1) // reset 711 | { 712 | #ifdef DE_PRINTF 713 | printf("[SYS]Reset\r\n"); 714 | #endif 715 | CH549SoftReset(); 716 | } 717 | else if (HID_OUT_report[1] == 2) // reload 718 | { 719 | #ifdef DE_PRINTF 720 | printf("[SYS]CONFIG RELOADED\r\n"); 721 | #endif 722 | } 723 | } 724 | } 725 | void main() 726 | { 727 | #if (UART0_INTERRUPT == 0) 728 | UINT8 dat; 729 | #endif 730 | CfgFsys(); // CH549时钟选择配置 731 | mDelaymS(10); //修改主频等待内部晶振稳定,必加 732 | mInitSTDIO(); //串口0初始化 733 | #ifdef DE_PRINTF 734 | printf("[SYS]CLOCK_CFG=0x%02x\r\n", (UINT16)CLOCK_CFG); 735 | printf("[SYS]PowerOn ... Ver1.1\r\n"); 736 | #endif 737 | USBDeviceInit(); // USB设备模式初始化 738 | EA = 1; //允许单片机中断 739 | 740 | GPIO_Init(PORT3, PIN4, MODE3); // P3.4上拉输入,用于主从检测 741 | mDelaymS(600); //等待USB中断完成 742 | 743 | if (MODE_PORT_0) 744 | { 745 | #ifdef DE_PRINTF 746 | printf("[SYS]Slave Mode\r\n"); 747 | #endif 748 | MODE_STATUS = 0; 749 | printf("<=1\r\n"); //串口发送在线消息 750 | 751 | } 752 | else 753 | { 754 | #ifdef DE_PRINTF 755 | printf("[SYS]Master Mode\r\n"); 756 | #endif 757 | MODE_STATUS = 1; 758 | } 759 | 760 | while (1) 761 | { 762 | #if (UART0_INTERRUPT == 0) //检测是否为非中断模式 763 | 764 | if (RI) 765 | { 766 | dat = SBUF; 767 | RI = 0; 768 | 769 | if (dat != 0xf1) // 以0xf1做为接收字符串结束标志 770 | { 771 | RevBuffer[revDataCount] = dat; 772 | revDataCount++; 773 | } 774 | else 775 | { 776 | // printf("%s\n", &RevBuffer[0]); 777 | revTempLength = revDataCount; 778 | revDataCount = 0; 779 | 780 | if (RevBuffer[0] == '<' && RevBuffer[1] == '=') //检测到从设备上线 781 | { 782 | #ifdef DE_PRINTF 783 | printf("[UART]Slave Online"); 784 | #endif 785 | 786 | } 787 | 788 | if (RevBuffer[0] == 0xf0 && MODE_STATUS == 0) //从设备接收HID报文,并发送到USB 789 | { 790 | for (i = 0; i < sizeof(RevBuffer); i++) 791 | { 792 | SendHID[i] = RevBuffer[i + 2]; 793 | } 794 | 795 | Enp2BlukIn(SendHID, sizeof(SendHID)); 796 | } 797 | 798 | } 799 | } 800 | 801 | #endif 802 | 803 | if (HID_OUT_report_FLAG) 804 | { 805 | REPORT_HANDLE(); 806 | } 807 | } 808 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jancgk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PCB/BOM_Board1_PCB1_2022-11-08.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/PCB/BOM_Board1_PCB1_2022-11-08.xlsx -------------------------------------------------------------------------------- /PCB/Gerber_PCB1_2022-11-08.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/PCB/Gerber_PCB1_2022-11-08.zip -------------------------------------------------------------------------------- /PCB/ProProject_KVM-over-USB_2022-11-08.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/PCB/ProProject_KVM-over-USB_2022-11-08.zip -------------------------------------------------------------------------------- /PCB/README.md: -------------------------------------------------------------------------------- 1 | 您也可以使用立创EDA对原理图和PCB进行在线查看和编辑 2 | 3 | https://oshwhub.com/jancgk/kvm-over-usb -------------------------------------------------------------------------------- /PCB/SCH_Schematic1_2022-11-08.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/PCB/SCH_Schematic1_2022-11-08.pdf -------------------------------------------------------------------------------- /PCB/YuzukiHCC_MS2109_Firmware.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/PCB/YuzukiHCC_MS2109_Firmware.bin -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ⚠️此项目已停止更新⚠️ 2 | 3 | 后续项目开发转移至->[KVM-Card-Mini](https://github.com/Jackadminx/KVM-Card-Mini) 4 | 5 | # KVM-Card 6 | 🖥️ Simple KVM Console to USB 7 | 8 | 9 | 10 | 实现一个简单的 KVM (Keyboard Video Mouse)功能,通过上位机程序实现对客户端的屏幕监控(HDMI)和键鼠控制(USB) 11 | ~~捡垃圾必备~~ 12 | ![image.png](./document/images/1.png) 13 | >图片来自网络 14 | 15 | ## 简单硬件分析 16 | 17 | ![2](./document/images/2.png) 18 | - 两颗CH549实现数据传送和USB键鼠功能(技术太菜搞不定双USB的STM32,所以选了这个方案)。 19 | - MS2109实现视频采集卡功能,在上位机显示被控端屏幕。 20 | - SL2.1A将采集卡、USB串口、CH549的USB连接到一起。 21 | - CH340G USB转串口可不安装,用于软件调试。 22 | 23 | ## 控制端软件 24 | 简单的KVM客户端 25 | ![4](./document/images/4.png) 26 | 27 | ![3](./document/images/3.png) 28 | 29 | - 实现屏幕显示(支持输出切换分辨率) 30 | - 客户端键盘控制、自定义快捷键 31 | - 客户端鼠标捕捉和控制,捕获鼠标后按下键盘右CTRL键释放,操作逻辑类似于VirtualBox( 32 | 33 | ## 固件刷入 34 | ### CH549 35 | 按住Flash键并将USB插入即可刷入固件 36 | 使用[WCHISPTool](https://www.wch.cn/downloads/WCHISPTool_Setup_exe.html)刷入固件 37 | 两颗CH549都需要刷入固件,固件自动识别主从 38 | ![5](./document/images/5.jpg) 39 | 40 | ### MS2109 41 | 42 | 配套的AT24C16 EEPROM可以直接买套片,或者用EEPROM编程器烧录。 43 | 可以使用烧录夹在板子断电状态进行烧录。 44 | 45 | MS2109的固件来自 [Yuzuki HCC HDMI](https://oshwhub.com/gloomyghost/yuzuki-hcc) 项目,可通过HEX文件编辑器编辑固件实现修改设备名。 46 | 47 | ## 感谢 48 | https://oshwhub.com/gloomyghost/yuzuki-hcc 49 | 50 | https://materialdesignicons.com/icon/ 51 | 52 | https://www.riverbankcomputing.com/software/pyqt/ 53 | 54 | https://github.com/apmorton/pyhidapi 55 | 56 | https://www2.keil.com/mdk5 57 | 58 | 和其他开源项目 59 | -------------------------------------------------------------------------------- /document/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/document/images/1.png -------------------------------------------------------------------------------- /document/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/document/images/2.png -------------------------------------------------------------------------------- /document/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/document/images/3.png -------------------------------------------------------------------------------- /document/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/document/images/4.png -------------------------------------------------------------------------------- /document/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackadminx/KVM-Card/9ab797d7761f4947798d1615dd8b41611b949efc/document/images/5.jpg --------------------------------------------------------------------------------