├── LICENSE ├── .gitattributes ├── CHANGELOG ├── media ├── cmake_main_file.png ├── cmake_project_variables.png ├── cube_encoder_mode_config.png ├── cube_encoder_mode_settings.png ├── cube_encoder_mode_gpio_settings.png ├── msys2_1.png ├── cube_home.png ├── cube_path.png ├── msys2_2.png ├── msys2_3.png ├── msys2_4.png ├── set_path_1.png ├── set_path_2.png ├── set_path_3.png ├── set_path_4.png ├── stm32guide.png ├── stmstudio_1.png ├── stmstudio_2.png ├── stmstudio_5.png ├── stmstudio_6.png ├── stmstudio_7.png ├── stmstudio_9.png ├── cube_adc_config.png ├── cube_adc_config2.png ├── cube_clock_conf.png ├── cube_main_window.png ├── cube_select_mcu.png ├── stmstudio_10.png ├── stmstudio_11.png ├── stmstudio_12.png ├── stmstudio_13.png ├── stmstudio_14.png ├── stmstudio_3.png ├── stmstudio_4.png ├── stmstudio_8.png ├── cube_adc_add_dma.png ├── cube_adc_nvic_enable.png ├── cube_adc_set_in1.png ├── cube_adc_set_in2.png ├── cube_generated_files.png ├── cube_gpio_select_mode.png ├── cube_i2c_set_config_1.png ├── cube_new_project.png ├── cube_programmer_path.png ├── cube_project_manager.png ├── cube_timer_config.png ├── interrupt_registers.png ├── cube_adc_configuring_1.png ├── cube_adc_configuring_2.png ├── cube_adc_configuring_3.png ├── cube_adc_configuring_4.png ├── cube_generate_code_button.png ├── cube_gpio_set_exti_nvic.png ├── cube_i2c_set_config_2.png ├── cube_installation_path.png ├── cube_microcontroller_image.png ├── cube_pwm_set_config_1.png ├── cube_pwm_set_config_2.png ├── cube_pwm_set_config_3.png ├── cube_pwm_set_config_4.png ├── cube_pwm_set_config_5.png ├── cube_pwm_set_config_6.png ├── cube_pwm_set_pin_timer.png ├── cube_uart_configuring_1.png ├── cube_uart_configuring_2.png ├── cube_uart_configuring_3.png ├── cube_uart_configuring_4.png ├── cube_ppm_read_enable_interrupt.png ├── cube_ppm_read_set_config_1.png ├── cube_ppm_read_set_config_2.png ├── cube_ppm_read_set_config_3.png ├── cube_project_manager_project.png ├── cube_select_pin_function_adc.png ├── cube_select_pin_function_exti.png ├── cube_select_pin_function_gpio.png ├── cube_timer_interrupt_enable.png ├── cube_timer_set_internal_clock.png ├── cube_programmer_installation_path.png └── cube_project_manager_code_generator.png └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | media/* filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 02/2019 2 | - Change to markdown and move to GitHub 3 | 4 | 05/2018 5 | - Initial Release 6 | -------------------------------------------------------------------------------- /media/cmake_main_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThundeRatz/STM32Guide/HEAD/media/cmake_main_file.png -------------------------------------------------------------------------------- /media/cmake_project_variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThundeRatz/STM32Guide/HEAD/media/cmake_project_variables.png -------------------------------------------------------------------------------- /media/cube_encoder_mode_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThundeRatz/STM32Guide/HEAD/media/cube_encoder_mode_config.png -------------------------------------------------------------------------------- /media/cube_encoder_mode_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThundeRatz/STM32Guide/HEAD/media/cube_encoder_mode_settings.png -------------------------------------------------------------------------------- /media/cube_encoder_mode_gpio_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThundeRatz/STM32Guide/HEAD/media/cube_encoder_mode_gpio_settings.png -------------------------------------------------------------------------------- /media/msys2_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9007df1249a5180744a0ea542fabdb35656d925bd50a6e053385ec0202581391 3 | size 2391 4 | -------------------------------------------------------------------------------- /media/cube_home.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1114b442d912585a0ea22e6a9bda1670728f1eafa248b3f0852621ea66c0d8c0 3 | size 91791 4 | -------------------------------------------------------------------------------- /media/cube_path.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:09266e89d3bf75d0b0e1bf52c3ee225868edd376d9b4fd2850d378f8550657b5 3 | size 40485 4 | -------------------------------------------------------------------------------- /media/msys2_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d82f0ca64a8e26e43f489535ae93f93464f54f68e829be1f61c810d9491d13b4 3 | size 13939 4 | -------------------------------------------------------------------------------- /media/msys2_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:875149bc4077244157faaa8449036a2bd2d550aa5fc48373262ac808c6b1c6bd 3 | size 23539 4 | -------------------------------------------------------------------------------- /media/msys2_4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1e576f1e8973aa34712144e3263aa8abe1ab07b35d78193dd4c80c12242182c2 3 | size 27382 4 | -------------------------------------------------------------------------------- /media/set_path_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f82fa607c562da56e0612c92c9f08f2e2c228d549e25705165ebf36ac711ef35 3 | size 12901 4 | -------------------------------------------------------------------------------- /media/set_path_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ce8c4b5239284714bbee0765e57fa259ec61c9b497d9cb8aced7bd191d28dd8f 3 | size 12113 4 | -------------------------------------------------------------------------------- /media/set_path_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:abcb43906d882e4f3cba2ed7e56320aa0b78c54ba9dbe0aeb081a44f037e884c 3 | size 19387 4 | -------------------------------------------------------------------------------- /media/set_path_4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a43a6ca63d0ca3c378fb42a498ffba63ec4968ba9c50938e832feff4b94bbda9 3 | size 22278 4 | -------------------------------------------------------------------------------- /media/stm32guide.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bb49e1a2897fe39c663f1a0374c8e60bf92f7f2f3ecd119faf774550c50d9937 3 | size 29432 4 | -------------------------------------------------------------------------------- /media/stmstudio_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d5dc01f417cc2fbc108d1a0567da14dea4d5024edaa2a85764e04d29fde74bd5 3 | size 65464 4 | -------------------------------------------------------------------------------- /media/stmstudio_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:18b91ac06610faecca3222776dc0265e984e0562fe79354c14c3cfbc52c9e754 3 | size 69646 4 | -------------------------------------------------------------------------------- /media/stmstudio_5.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:997081b843f9d655a9a25ef61b01baf05f53ba638b5d85bf21d3fdffcbaa3763 3 | size 67341 4 | -------------------------------------------------------------------------------- /media/stmstudio_6.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b1fa657473b13e59454082632df30e1c0a30b3fa596798f1c8e0dc229bc54e7 3 | size 28832 4 | -------------------------------------------------------------------------------- /media/stmstudio_7.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:efa2406bfd9a547024c0fbc2132ff90638b25a0c7a40d0e7edef2fb78886d1a7 3 | size 72565 4 | -------------------------------------------------------------------------------- /media/stmstudio_9.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:307d1b8400ff0fcd7f1d3f28ea950c8257fc58c930c9ab1afeef4063ca114ea9 3 | size 29663 4 | -------------------------------------------------------------------------------- /media/cube_adc_config.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:34c444230b1c2fc44c56c261d717991450d2686737ea9b709350d20d0c9baa1a 3 | size 97852 4 | -------------------------------------------------------------------------------- /media/cube_adc_config2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7a2c6bab76f0841b2d03588c1bca52d85c77343e976ec38ad5f26ea751d4eab2 3 | size 64738 4 | -------------------------------------------------------------------------------- /media/cube_clock_conf.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7968380e1960e35167e847ecdad9cfb3794587e11ff8421f072ccc063e781df6 3 | size 98934 4 | -------------------------------------------------------------------------------- /media/cube_main_window.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1a1e3c7330c9cca9dcbdce44ba44121cb2e086f5fb1ebab5c2c9994f8eb4f3d2 3 | size 70816 4 | -------------------------------------------------------------------------------- /media/cube_select_mcu.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:efd8ea0f58cfdce8ea023199fa2c52b6180509e0462e11d20934c584b97607b1 3 | size 77069 4 | -------------------------------------------------------------------------------- /media/stmstudio_10.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ec36e3333b5ada58bc76080b365ce411125a75f57bf9e25ab49f7c46bc76eb2b 3 | size 124082 4 | -------------------------------------------------------------------------------- /media/stmstudio_11.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a400b65490827a5968257c51f4574f15f19879b6f396d603475a58074860b5ac 3 | size 54345 4 | -------------------------------------------------------------------------------- /media/stmstudio_12.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e48edcaad898a39d74974ffe915a898d1a066d8615b0eec5fd2ee6a3a6ff917d 3 | size 86759 4 | -------------------------------------------------------------------------------- /media/stmstudio_13.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6e542c69acef0cee9eb13cb9fe536d4b51ac2d10f27a87802aa75ab3e2bf0790 3 | size 33314 4 | -------------------------------------------------------------------------------- /media/stmstudio_14.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c0a536403f5c37a88749b829810018ba5fbb78dae67088474e9a32c86f50b011 3 | size 59958 4 | -------------------------------------------------------------------------------- /media/stmstudio_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1c0ea482265e53a22e0f021619e2ba8728f91947d8d7583c69359b30615d6113 3 | size 231919 4 | -------------------------------------------------------------------------------- /media/stmstudio_4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b6793edcbb81965ecdc13d3ea039ca7f1beb8b419916ba8bb660a2f88824df47 3 | size 121836 4 | -------------------------------------------------------------------------------- /media/stmstudio_8.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c8c144f7d6ee8d810be9612ebcbd010871d6651147829c94a284cbcf734532db 3 | size 125162 4 | -------------------------------------------------------------------------------- /media/cube_adc_add_dma.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:63caad8b6b20f7833bed588453caee194ee37ad50c85f87f331f2168c1812132 3 | size 108117 4 | -------------------------------------------------------------------------------- /media/cube_adc_nvic_enable.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4c4f5deb47cd2448184b64d6baace6c32bd8ca1ef46561c710146c4dc237fb1e 3 | size 101548 4 | -------------------------------------------------------------------------------- /media/cube_adc_set_in1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:79248b3d1ef5c698d76482013175892f4b4f54d2315e5fe72d7ca8b2de81a58f 3 | size 114118 4 | -------------------------------------------------------------------------------- /media/cube_adc_set_in2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0434a051ae77410496fc1688ca6feef12b503c16cfe75fd17025188e1bf04a81 3 | size 110259 4 | -------------------------------------------------------------------------------- /media/cube_generated_files.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c51fda9733f279915bccca86b31be46a5122a5c1e77b59f1c51bd7ef3bed45b1 3 | size 27036 4 | -------------------------------------------------------------------------------- /media/cube_gpio_select_mode.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9fb2735ded66315c5f8b8db25a1a482e520ec961c99425989843d272d18c69e0 3 | size 99879 4 | -------------------------------------------------------------------------------- /media/cube_i2c_set_config_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8b94f8e9b5a39a4fcb8bb67486283a68d92c2f4088a2afc29764e70e03d3efa3 3 | size 93018 4 | -------------------------------------------------------------------------------- /media/cube_new_project.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5f441daf9a33b14c874bf4efa635f9f1c100ba0dec522223fe670ac6c2ac1925 3 | size 160709 4 | -------------------------------------------------------------------------------- /media/cube_programmer_path.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d45a13ac0c1e64cd7d35d3e18be91ea458567e59818a13a190802a18e8b7d51f 3 | size 47343 4 | -------------------------------------------------------------------------------- /media/cube_project_manager.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:32667d8ed2f37121f898294eda5e74d43a42655d1fb4e91b70b771b89aa07a0c 3 | size 59477 4 | -------------------------------------------------------------------------------- /media/cube_timer_config.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:52ed6ccc65bc1b939b4913e96f5c8c2fe86b3ae813945820801030f5de8f5001 3 | size 114228 4 | -------------------------------------------------------------------------------- /media/interrupt_registers.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:84c69d129fe0022d15556f104bdf1f25303bfcec8e4aaba73bcfae76eab4bc9b 3 | size 102956 4 | -------------------------------------------------------------------------------- /media/cube_adc_configuring_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:65985a55acf8c86d9d8e4b7020a5826deb93f16278397d030edb7208b4eeb45e 3 | size 102315 4 | -------------------------------------------------------------------------------- /media/cube_adc_configuring_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7af143de64bf78403b9c0e6b04b10ed6508732ef81e3dff89f7c5d360a2fa77e 3 | size 114087 4 | -------------------------------------------------------------------------------- /media/cube_adc_configuring_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:899e1756b73a9cbe38cb14f85e09a1de3a1c8f4c277d030b5e3d6b1873e43ae7 3 | size 112807 4 | -------------------------------------------------------------------------------- /media/cube_adc_configuring_4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c04f1c8e6a95d873a0d316f76ccfa63febf8f06afd264f3333c05847bf5b03b6 3 | size 111179 4 | -------------------------------------------------------------------------------- /media/cube_generate_code_button.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7ea706703dfd5bbde338e61eab4e44ae469ce927f43ceece1ab9cb6a671d6053 3 | size 2588 4 | -------------------------------------------------------------------------------- /media/cube_gpio_set_exti_nvic.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7e97e73249f79fabf67b66eadb4e1c83626e9021687910ad96e9bb306a4d1a42 3 | size 96381 4 | -------------------------------------------------------------------------------- /media/cube_i2c_set_config_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d0f49c3160572d2ccf4eecb2b9b7b7806c00d1bfeaf9167dbb9c48b83d2d64c9 3 | size 106346 4 | -------------------------------------------------------------------------------- /media/cube_installation_path.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6ef7b97781c1edefb33c9a99c225eebe3fedaf18b405cf3843e3699f0b8a1cce 3 | size 53559 4 | -------------------------------------------------------------------------------- /media/cube_microcontroller_image.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:18ee0323fb3ff49ba15c1766d78e8d52233da108ea5be60aa94bc5e237a4df76 3 | size 25657 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_config_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6e8b25f196cccb58ff894a959624448b3f7132bd37ea3420852a26f06135b96b 3 | size 104152 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_config_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:79ffa43f3ccc0a3c25bc9398b0cb893cd3be3f1b32177c79f0b601c92de67ea2 3 | size 109785 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_config_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6cae99cbbce0b0e88c16de787c4e34248211a72731d9fa662f6f9b70cd7a8e1e 3 | size 110095 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_config_4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0c116fb5c88b4be18f467bd259f67b21692f6fc20e6c64f8228e5dd9c2094c05 3 | size 107192 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_config_5.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2952c68bff96820adcd0c612953de205ea6dbaad88f00d9a3e9c8887aab7ffb3 3 | size 104862 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_config_6.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d3f9e180720a1a8934ff85dd830544035bba1ac658b96bfab030d324e310a64d 3 | size 105575 4 | -------------------------------------------------------------------------------- /media/cube_pwm_set_pin_timer.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d38a1c5fa2486e3f79da3ad835a2caaac6f16d8cb1687b695a0bd7eb8642bc52 3 | size 27138 4 | -------------------------------------------------------------------------------- /media/cube_uart_configuring_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:746cd450a2f59002a8b2c95008779fc722fa4db197f30d7e31c2c2536d4d5fef 3 | size 100528 4 | -------------------------------------------------------------------------------- /media/cube_uart_configuring_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dd2f04403a0713aa38f6a3cdbfbc9a4a19107a44c629e51d9a7cb1d77507872d 3 | size 108572 4 | -------------------------------------------------------------------------------- /media/cube_uart_configuring_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:47ff1f98cd014630e5f42facbb88df15a9d03b27de91705655f00db57ef44155 3 | size 96349 4 | -------------------------------------------------------------------------------- /media/cube_uart_configuring_4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8de9e6fcd0ffdf9603068472d4761497ed00811fb40fd3f078ce82df383ca25a 3 | size 85093 4 | -------------------------------------------------------------------------------- /media/cube_ppm_read_enable_interrupt.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2be5d5465184cb510f2802ee822ee2517bdea5a5551eedfe9ee801b8e0d12ec0 3 | size 93923 4 | -------------------------------------------------------------------------------- /media/cube_ppm_read_set_config_1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:36544aa4abd9acd6ddcc046df0182072d9687f518c776c87c7f03a7abb2b5a65 3 | size 103566 4 | -------------------------------------------------------------------------------- /media/cube_ppm_read_set_config_2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0b08c476271c35f552f187973b0726fa517718930d7b1c4c3c262f9605419c6b 3 | size 108569 4 | -------------------------------------------------------------------------------- /media/cube_ppm_read_set_config_3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:58b57b911749fac847cf25f0587de93fc9e3003cc1dd3cc9a74dc54c28923df8 3 | size 107358 4 | -------------------------------------------------------------------------------- /media/cube_project_manager_project.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b467ccf0034524c75d772f7a2d823cb7a73c2bab2ec509070d657b05f53a7595 3 | size 62051 4 | -------------------------------------------------------------------------------- /media/cube_select_pin_function_adc.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:debe6c42d93a555d36e8acb90765ef3e30cf1c928e85cdc1ce0d48a3236df66e 3 | size 26154 4 | -------------------------------------------------------------------------------- /media/cube_select_pin_function_exti.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a802d476d929f2ed70b23997ec32cd3e2c965d26fb6e123ddb991c601367a62b 3 | size 23795 4 | -------------------------------------------------------------------------------- /media/cube_select_pin_function_gpio.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d639037437113cc5ddecb80af7cb280b7db784ee4b26ac94d0b6a10b86aebce8 3 | size 20257 4 | -------------------------------------------------------------------------------- /media/cube_timer_interrupt_enable.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4ab799dcf06c8e2df1d32e46fe2074d0a2cb914c547ae5a25ca0060915bfbac2 3 | size 99749 4 | -------------------------------------------------------------------------------- /media/cube_timer_set_internal_clock.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bb2e89264b11a6011c935d233746c812e9867864c4f46a1cb7bbb60dc6bbdd13 3 | size 110585 4 | -------------------------------------------------------------------------------- /media/cube_programmer_installation_path.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:aae5233dec0be3073c23f3d7bddbbd22625f2b56d0d6bee1e7ede906b7a7ebca 3 | size 17735 4 | -------------------------------------------------------------------------------- /media/cube_project_manager_code_generator.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6daec7f58c41bd48c83efe6c26491d5734d523c62a6934c3725c0ac70b5dbb44 3 | size 61561 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![STM32Guide logo](media/stm32guide.png) 2 | 3 | --- 4 | 5 | # Índice 6 | 7 | - [Índice](#Índice) 8 | - [Introdução](#introdução) 9 | - [Requisitos](#requisitos) 10 | - [Windows Subsystem for Linux](#windows-subsystem-for-linux) 11 | - [Visual Studio Code](#visual-studio-code) 12 | - [Git](#git) 13 | - [Ferramentas](#ferramentas) 14 | - [STM32 Cube MX](#stm32-cube-mx) 15 | - [STM32 Cube Programmer](#stm32-cube-programmer) 16 | - [STM32 Project Template](#stm32-project-template) 17 | - [Cube MX](#cube-mx) 18 | - [Estrutura de Código](#estrutura-de-código) 19 | - [HAL](#hal) 20 | - [GPIO](#gpio) 21 | - [ADC e DMA](#adc-e-dma) 22 | - [UART e DMA](#uart-e-dma) 23 | - [Recebendo dados](#recebendo-dados) 24 | - [Pacote de 1 byte](#pacote-de-1-byte) 25 | - [Pacote de mais de 1 byte](#pacote-de-mais-de-1-byte) 26 | - [Transmitindo dados](#transmitindo-dados) 27 | - [Interrupções](#interrupções) 28 | - [NVIC](#nvic) 29 | - [Interrupções externas](#interrupções-externas) 30 | - [Timers](#timers) 31 | - [PWM](#pwm) 32 | - [Geração de PPM](#geração-de-ppm) 33 | - [Leitura de PPM](#leitura-de-ppm) 34 | - [Encoders](#encoders) 35 | - [I²C](#ic) 36 | - [STM Studio](#stm-studio) 37 | - [Leitura de variáveis](#leitura-de-variáveis) 38 | - [Escrita de variáveis](#escrita-de-variáveis) 39 | - [Extras](#extras) 40 | - [CMake e Makefile](#cmake-e-makefile) 41 | - [Make/Makefile](#makemakefile) 42 | - [CMake](#cmake) 43 | - [Instalando CMake no Linux](#instalando-cmake-no-linux) 44 | - [Configuração inicial do CMake](#configuração-inicial-do-cmake) 45 | - [Compilando e executando o projeto](#compilando-e-executando-o-projeto) 46 | - [Apêndices](#apêndices) 47 | - [Colocando caminhos no PATH](#colocando-caminhos-no-path) 48 | - [Criando variáveis no PATH](#criando-variáveis-no-path) 49 | - [Virtualização da BIOS](#habilitando-a-virtualização-na-bios) 50 | --- 51 | 52 | # Introdução 53 | 54 | Este documento tem como objetivo explicar como é feita a programação de 55 | microcontroladores da STMicroelectronics, voltado especialmente para os robôs da 56 | equipe ThundeRatz que usam placas com esses microcontroladores. 57 | 58 | Nas próximas páginas serão apresentados os softwares necessários para isso, e 59 | uma explicação de como programar algumas das principais funcionalidades desses 60 | microcontroladores. 61 | 62 | # Requisitos 63 | 64 | Para poder escrever programas para os microcontroladores da ST é necessário 65 | baixar alguns softwares e compiladores, bem como um editor de texto. 66 | 67 | Neste guia, apresentamos uma maneira de como instalar esses requisitos em sistemas 68 | baseados em Debian/Ubuntu. 69 | 70 | Para usuários de Windows recomendamos fortemente o dual-boot para dar mais 71 | facilidade e agilidade na hora da execução de projetos ou então o uso do 72 | Windows Subsystem for Linux. 73 | 74 | ## Windows Subsystem for Linux 75 | Windows Subsystem for Linux (WSL) é uma funcionalidade do Windows que permite a 76 | execução de aplicativos e comandos Linux dentro do sistema Windows. 77 | 78 | ### Instalação 79 | 0. Primeiro, verifique se a virtualização da sua máquina está habilitada. 80 | Clique [aqui](#Habilitando-a-virtualização-na-BIOS) para ver o passo a passo. 81 | 82 | 1. Com o passo anterior concluido, vamos ativar o WSL. Vá em: 83 | 84 | painel de controle -> programas -> ativar ou desativar recursos do Windows 85 | Essa janela que queremos: 86 | 87 | ![{19C896B0-BB52-433C-9EC1-6171DF9FAE59}](https://github.com/user-attachments/assets/b5a22fe1-3dbf-4a2e-aabb-a130933dafd1) 88 | 89 | 90 | Habilite estas 3 opções: 91 | - [X] Plataforma de máquina virtual 92 | - [X] Plataforma do hipervisor do Windows 93 | - [X] Subsistema do Windows para Linux 94 | 95 | Reinicie seu computador. 96 | 97 | 2. Depois de reiniciado, abra o PowerShell como Administrador, insira o comando 98 | e aguarde a instalação: 99 | 100 | ```powershell 101 | wsl --install -d ubuntu 102 | ``` 103 | 104 | 3. Agora com o Ubuntu (nossa distribuição de sistema Linux) instalado, você verá 105 | ainda no PowerShell mensagens pedindo nome de usuário e senha para configurar 106 | seu ambiente Linux. Preencha de acordo para continuarmos. 107 | 108 | 4. Abra o Ubuntu pesquisando-o no menu Iniciar. Quando aberto, insira a seguinte 109 | linha de comando para atualizar os pacotes do sistema. 110 | 111 | ```bash 112 | sudo apt update && sudo apt upgrade -y 113 | ``` 114 | 115 | Pronto! Você já tem um ambiente Linux instalado dentro do seu sistema Windows :) 116 | 117 | A partir de agora, quando utilizarmos o termo "terminal" neste guia, vamos estar nos referindo 118 | ao terminal do Ubuntu instalado. 119 | 120 | ## Visual Studio Code 121 | 122 | >[!NOTE] 123 | >Se estiver usando WSL, faça a instalação no Windows 124 | 125 | Para baixar, acesse [esse link](https://code.visualstudio.com/) e escolha o 126 | instalador para seu sistema operacional. 127 | 128 | Faça a instalação padrão. 129 | 130 | ## Git 131 | 132 | Git é um sistema de controle de versão distribuído. Ele serve para rastrear 133 | alterações em arquivos, especialmente em projetos de desenvolvimento de 134 | software, permitindo que várias pessoas trabalhem simultaneamente sem perder 135 | o histórico. 136 | 137 | >[!Warning] 138 | >Se você está no Windows e ainda não instalou o WSL, clique [aqui](#windows-subsystem-for-linux). 139 | 140 | ### Instalação 141 | 142 | Abra o terminal do Ubuntu e insira o comando: 143 | 144 | ```bash 145 | sudo apt install git 146 | ``` 147 | 148 | Para certificar que o git foi instalado corretamente, verifique a versão: 149 | 150 | ```bash 151 | git --version 152 | ``` 153 | 154 | ## Ferramentas 155 | 156 | ### Make, CMake e arm-none-eabi-gcc 157 | 158 | **Make** é usado para automatizar o processo de compilação e construção de 159 | projetos de software. 160 | 161 | **CMake** é uma ferramenta de automação de construção, semelhante ao make, mas 162 | com maior flexibilidade e modernidade. 163 | 164 | **arm-none-eabi-gcc** é usado para compilar programas para microcontroladores em 165 | ambientes de baixo nível, como no desenvolvimento de firmware para dispositivos 166 | embarcados com o STM32. 167 | 168 | >[!Warning] 169 | >Se você está no Windows e ainda não instalou o WSL, clique [aqui](#windows-subsystem-for-linux). 170 | 171 | ### Instalação 172 | 173 | Abra o terminal e insira o seguinte comando: 174 | 175 | ```bash 176 | sudo apt install -y cmake make gcc-arm-none-eabi 177 | ``` 178 | 179 | Depois de concluido, você pode verificar a versão de cada compilador para 180 | certificar que tudo ocorreu bem: 181 | 182 | ```bash 183 | cmake --version 184 | ``` 185 | 186 | ```bash 187 | make --version 188 | ``` 189 | 190 | ```bash 191 | arm-none-eabi-gcc --version 192 | ``` 193 | 194 | Deve estar parecido com a imagem a seguir. 195 | 196 | ![{B5782A61-20D6-425B-9CAF-82E2A8BAEDB1}](https://github.com/user-attachments/assets/b101e354-601f-4249-a825-54713acf720e) 197 | 198 | ## STM32 Cube MX 199 | 200 | Esse software é bem importante quando se está no começo do desenvolvimento e é 201 | utilizado desde o projeto de placa. Nele é possível setar várias configurações 202 | de pinos e periféricos por meio de uma interface gráfica e gerar o código disso 203 | automaticamente. 204 | 205 | Para baixá-lo, acesse [este 206 | link](http://www.st.com/en/development-tools/stm32cubemx.html). 207 | 208 | > [!NOTE] 209 | > Se estiver usando WSL, faça a instalação no Windows 210 | 211 | ### Instalação 212 | 213 | Faça o download de acordo com o sistema operacional que estiver usando. 214 | 215 | Extraia os arquivos da pasta .zip e execute o arquivo 216 | SetupSTM32CubeMX-X.X.X.linux ou SetupSTM32CubeMX-X.X.X.exe se estiver 217 | usando Windows. 218 | 219 | Durante a instalação, o instalador irá pedir um diretório onde o programa 220 | irá ser instalado. Deixe como padrão e copie o caminho para podermos criar 221 | uma variável no PATH. 222 | 223 | Exemplo de caminho: 224 | ```bash 225 | /home/eu/STM32CubeMX 226 | ``` 227 | 228 | Após a instalação, crie uma variável chamada CUBE_PATH com o local de instalação 229 | que você copiou. Para isso, consulte o [Apêndice 2](#criando-variáveis-no-path) 230 | 231 | Para verificar se a variável foi criada, execute no terminal: 232 | 233 | ```bash 234 | echo $CUBE_PATH 235 | ``` 236 | 237 | O caminho que você colocou na variável deve aparecer. 238 | 239 | ## STM32 Cube Programmer 240 | 241 | O STM32CubeProgrammer é uma ferramenta para programar microcontroladores 242 | STM32. É possível ver e apagar o conteúdo da memória flash, além de escrever 243 | arquivos binários. Para baixar, acesse [esse 244 | link](https://www.st.com/en/development-tools/stm32cubeprog.html). 245 | 246 | > [!NOTE] 247 | > Se estiver usando WSL, faça a instalação no Windows 248 | 249 | ### Instalação 250 | 251 | Faça o download do Cube Programmer de acordo com o sistema que estiver 252 | utilizando. Note que há duas opções de instalação para Windows, selecione 253 | a opção "Win64" se seu computador for de 64 bits (provavelmente sim). 254 | 255 | Extraia os arquivos da pasta zip baixada e execute o instalador. 256 | 257 | Durante a instalação, o instalador irá pedir um diretório onde o programa 258 | irá ser instalado. Deixe como padrão e copie o caminho para configurar o 259 | Cube Programmer no PATH. 260 | 261 | Exemplo de caminho: 262 | ```bash 263 | /home/eu/STMicroelectronics/STM32Cube/STM32CubeProgrammer 264 | ``` 265 | 266 | Após isso, será necessário configurar o diretório onde está a pasta bin 267 | do Cube Programmer no PATH do sistema. Para isso, adicione `/bin` no final 268 | caminho que você copiou anteriormente. Consulte o 269 | [Apêndice 1](#colocando-caminhos-no-path) para demais dúvidas. 270 | 271 | Finalmente, para verificar a instalação, execute o seguinte comando: 272 | 273 | ```bash 274 | STM32_Programmer_CLI -l 275 | ``` 276 | 277 | Algo parecido deve aparecer: 278 | 279 | ```bash 280 | ------------------------------------------------------------------- 281 | STM32CubeProgrammer v2.2.1 282 | ------------------------------------------------------------------- 283 | 284 | STM32CubeProgrammer version: 2.2.1 285 | ``` 286 | 287 | > [!NOTE] 288 | > Para verificar a instalação usando WSL, use: 289 | > ```bash 290 | > STM32_Programmer_CLI.exe -l 291 | > ``` 292 | 293 | # STM32 Project Template 294 | 295 | Vamos utilizar o template disponível [nesse 296 | repositório](https://github.com/ThundeRatz/STM32ProjectTemplate). 297 | 298 | O README nesse repositório explica bem como utilizar, mas explicaremos um pouco 299 | nesse documento também. 300 | 301 | A estrutura de pastas no template é a seguinte: 302 | 303 | ``` 304 | ├── cube 305 | │   └── stm32_project_template.ioc 306 | ├── inc 307 | │   └── mcu.h 308 | ├── LICENSE 309 | ├── Makefile 310 | ├── README.md 311 | ├── src 312 | │   ├── main.c 313 | │   └── mcu.c 314 | └── uncrustify.cfg 315 | ``` 316 | 317 | Na pasta cube, ficará o projeto do Cube e os arquivos gerados ao gerar o código. 318 | Na pasta `inc` ficam os headers (arquivos .h) e na pasta `src`, os arquivos .c. 319 | A forma de utilizar o Cube para esse template será explicada na próxima seção. 320 | 321 | Para gerar o código, utilizamos o comando `make cube`. Depois disso, utilizamos 322 | o comando `make prepare` para apagar os arquivos do Cube desnecessários. 323 | 324 | No caso de estar pegando o código em um repositório que já utiliza esse 325 | template, precisamos, logo após o checkout ou o pull, executar os comandos `make 326 | cube` para gerar os arquivos a partir do `.ioc` e em seguida `make prepare`. 327 | Isso porque os arquivos gerados pelo Cube não vão para o repositório. 328 | 329 | Os comandos do Makefile estão bem documentados no README. Mencionaremos aqui 330 | alguns mais fundamentais: 331 | 332 | * `make`: compila o programa, gerando os arquivos .elf, .bin e .hex. 333 | * `make cube`: gera o código a partir do projeto .ioc do Cube. 334 | * `make prepare`: apaga os arquivos desnecessários do Cube. 335 | * `make flash` ou `make load`: grava o programa no microcontrolador. 336 | * `make clean_cube`: apaga os arquivos gerados pelo Cube. 337 | * `make clean`: apaga os arquivos objeto personalizados (não gerados pelo Cube). 338 | * `make clean_all`: apaga todos os arquivos objeto (inclusive os gerados pelo 339 | Cube). 340 | 341 | # Cube MX 342 | 343 | Ao abrir o Cube, verá essa tela: 344 | 345 | ![Cube home](media/cube_home.png) 346 | 347 | Para criar um novo projeto, escolha “New Project”. Ao fazer isso, verá essa 348 | tela: 349 | 350 | ![Cube new project](media/cube_new_project.png) 351 | 352 | Aqui você pode digitar o nome do microcontrolador usado na placa para qual você 353 | estará programando, nesse documento, será usado o STM32F303C6. Basta clicar duas 354 | vezes no nome que aparece na lista inferior para selecionar. 355 | 356 | ![Cube select MCU](media/cube_select_mcu.png) 357 | 358 | Após selecionado, a seguinte tela aparecerá: 359 | 360 | ![Cube main window](media/cube_main_window.png) 361 | 362 | Como pode ser visto, na parte superior existem alguns menus e 4 abas, a aba 363 | Pinout & Configuration será mostrada aos poucos ao longo do documento, a aba 364 | Clock Configuration configura a árvore de clocks, a aba Project Manager 365 | configura o projeto e a aba Tools possui uma ferramenta de simular consumo de 366 | energia, que não costumamos usar na equipe. 367 | 368 | A aba Clock Configuration normalmente é alterada apenas uma vez no projeto para 369 | alterar a frequência do clock: 370 | 371 | ![Cube clock configuration](media/cube_clock_conf.png) 372 | 373 | Nessa tela, apenas mudamos o clock nesse quadrado que está em destaque, não faz 374 | muita diferença, mas costumamos deixar no máximo possível, para isso, basta 375 | digitar o valor “max” que tem logo abaixo, sem um clock externo, é bem provável 376 | que ele não possa ser alcançado, mas o programa vai dar uns avisos e colocar no 377 | máximo possível real, basta aceitar (nesse caso, 64 MHz). 378 | 379 | Na aba Pinout & Configuration, precisamos setar os pinos de gravação. Para isso, 380 | na parte esquerda, localize o periférico SYS (na seção System Core) e, em Serial 381 | Wire, selecione Debug Serial Wire. 382 | 383 | Com isso, os pinos de gravação ficarão com a cor verde. Pinos com a cor verde no 384 | pinout indicam que as configurações mínimas necessárias para utilizar o pino 385 | fora completadas. 386 | 387 | Após isso, já é possível gerar o código pela primeira vez, mesmo que vazio, 388 | primeiro, vá nas configurações, na aba Project Manager. 389 | 390 | Ao clicar nessa aba, a seguinte tela aparecerá, raramente será necessário mexer 391 | nela novamente: 392 | 393 | ![Cube project manager](media/cube_project_manager.png) 394 | 395 | Na seção Project, coloque um nome para o projeto, cheque a opção "Do not 396 | generate the main()" e mude o Toolchain para Makefile, o template usado pela 397 | equipe está configurado para essa estrutura de código. 398 | 399 | ![Cube project section of project 400 | manager](media/cube_project_manager_project.png) 401 | 402 | Então clique na seção Code Generator: 403 | 404 | ![Cube code generator section of project 405 | manager](media/cube_project_manager_code_generator.png) 406 | 407 | Marque a opção "Copy only the necessary library files" e a opção de gerar 408 | arquivos .c/.h separados para cada periférico. 409 | 410 | Após isso seu projeto estará configurado, agora, é possível gerar o código 411 | clicando no botão "Generate code" na parte superior: 412 | 413 | ![Cube generate code button](media/cube_generate_code_button.png) 414 | 415 | **Observação**: gere o código em algum lugar e copie apenas o arquivo 416 | .ioc para a pasta cube do template. 417 | 418 | # Estrutura de Código 419 | 420 | Ao gerar o código pelo Cube, vários arquivos e pastas serão criados: 421 | 422 | ![Cube generated files](media/cube_generated_files.png) 423 | 424 | Não mexa nos arquivos gerados. 425 | 426 | # HAL 427 | 428 | A HAL (Hardware Abstraction Layer) é uma biblioteca feita pela ST composta por 429 | várias funções que ajudam na manipulação dos periféricos e pinos, é possível ver 430 | os arquivos dela na pasta Drivers, o Cube apenas copia para a pasta do seu 431 | projeto o que estiver sendo utilizado nele. 432 | 433 | Uma característica das funções é que todas elas começam com `HAL_`, e a 434 | `HAL_Init()` está presente em todos os programas, inclusive nesse vazio criado 435 | na seção anterior. 436 | 437 | Mais exemplos serão dados nas seções seguintes. 438 | 439 | # GPIO 440 | 441 | A configuração de pinos como GPIO é a mais simples, já que raramente requer 442 | ajustes extras, além de que praticamente todos os pinos podem ser configurados 443 | como GPIO (input ou output). Para selecionar a função de um pino, basta clicar 444 | nele na primeira tela do Cube e na função desejada (serve para todas as 445 | funções): 446 | 447 | ![Cube select GPIO pin function](media/cube_select_pin_function_gpio.png) 448 | 449 | Para a maioria dos usos de GPIO, apenas selecionar a função nessa lista é 450 | suficiente, após gerar o código novamente, é possível ver as mudanças nos 451 | arquivos, um arquivo gpio.c (e .h) foi criado, com apenas uma função de 452 | inicialização de GPIO – `MX_GPIO_Init()`. Essa função precisa ser chamada no 453 | início da main ou em alguma função de inicialização. 454 | 455 | Será possível perceber um padrão a partir de agora na parte de configurações, 456 | para praticamente todos os periféricos, toda configuração feita no Cube é 457 | convertida em uma função `MX_<>_Init()`, que precisa ser chamada no início do 458 | programa. 459 | 460 | As funções para manipulação das GPIO estão no arquivo `stm32f3xx_hal_gpio.c` na 461 | pasta Drivers, as principais são: 462 | 463 | ```c 464 | File: stm32f3xx_hal_gpio.h 465 | 282: /* IO operation functions *****************************************************/ 466 | 284: GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 467 | 285: void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState); 468 | 286: void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 469 | ``` 470 | 471 | Para utilizá-las, basta passar a porta e o pino como parâmetros (além do estado 472 | na função de escrita), por exemplo, no pino PA10, do exemplo, sua porta é a A e 473 | seu pino é o 10, existem defines pra isso, então ficaria, para leitura: 474 | 475 | ```c 476 | int valor = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_10); 477 | ``` 478 | 479 | Isso retorna 1 ou 0 dependendo da leitura do pino, caso o pino seja de saída, é 480 | só fazer: 481 | 482 | ```c 483 | HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET); // Ou _RESET 484 | ``` 485 | 486 | A função de toggle também escreve no pino, mudando o estado de SET (1) para 487 | RESET (0) ou vice-versa. 488 | 489 | # ADC e DMA 490 | 491 | O ADC (Analog to Digital Converter) serve basicamente para pegarmos, em intervalos 492 | regulares, os valores contínuos de tensão (em nosso caso normalmente retornados por 493 | sensores) e transformá-los em números que podemos trabalhar (valores digitais). 494 | O DMA ajuda a fazer isso de maneira mais rápida e automática. Aqui está um 495 | [vídeo](https://youtu.be/xy9mMh7KYE8?si=4CUXsLmqLkxEOFI9) para você aprender mais sobre o ADC. 496 | 497 | Começando pelo Cube, algumas configurações adicionais serão necessárias, mas prestem 498 | atenção, pois nem todos os pinos podem executar todas as funções, portanto, é importante 499 | checar na hora de projetar uma placa para que todos os pinos tenham as funções desejadas. 500 | 501 | Como exemplo, vamos configurar o **ADC1**, por isso vamos chamar as funções relacionadas a esse ADC, 502 | como **ADC1_IN1**, **MX_ADC1_Init()** (que será explicado abaixo), entre outros. Se tivessemos usando outro ADC, exemplo "ADC3", usariamos 503 | a função ADC3_IN3, MX_ADC3_Init(), portanto, usem as funções adequadas para cada ADC que forem configurar. 504 | 505 | ![Cube select ADC pin function](media/cube_select_pin_function_adc.png) 506 | 507 | Ao selecionar um pino como ADC (nesse caso PA0), ele ficará laranja, indicando 508 | que ainda faltam configurações a serem realizadas, na coluna ao lado, na seção 509 | "Analog", é possível selecionar o periférico e dizer o que quer fazer com ele, 510 | nesse caso, escolhemos o ADC1_IN1 (canal 1 do ADC1), então é necessário mudar 511 | seu modo na parte "Mode" de "Disable" para "IN1 Single-ended". No pino PA1 tem o 512 | ADC1_IN2, que também será usado a seguir. 513 | 514 | ![Cube ADC configuration screen](media/cube_adc_config.png) 515 | 516 | Uma observação é que se não aparecer esta opção de mudar o modo do ADC1_IN1, significa 517 | que ele já está configurado como "IN1 Single-ended", assim, não precisa fazer a parte 518 | citada acima. 519 | 520 | ![Cube ADC configuration screen 2](media/cube_adc_config2.png) 521 | 522 | Após escolher um pino de ADC, aparecem algumas opções na parte "Configuration" 523 | abaixo. É necessário alterar algumas dessas opções para terminar de 524 | configurá-lo. 525 | 526 | Como dito, **essa tela pode variar dependendo do uC e do pino escolhido, porque 527 | alguns uCs tem mais funcionalidades**. Só é necessário mexer em algumas 528 | configurações. Na maioria dos casos, queremos que o ADC seja lido continuamente, 529 | então é necessário ligar o "Continuous Conversion Mode" e o "DMA Continuous 530 | Requests": 531 | 532 | ![Cube ADC configuring 1](media/cube_adc_configuring_1.png) 533 | 534 | Além disso, deve-se mudar o "Number of Conversion" para a quantidade de canais que 535 | deve ser lido, nesse caso, 2. Ao mudar isso, o "Scan Conversion Mode" será ativado 536 | automaticamente e aparecerá novos menus “Rank” de acordo como quantidade 537 | escolhida: 538 | 539 | ![Cube ADC configuring 2](media/cube_adc_configuring_2.png) 540 | 541 | É necessário abrir esses "Ranks" e colocar os canais lá, preferencialmente em 542 | ordem, e aumentar o "Sampling Time" para algo maior (não há um número definido): 543 | 544 | ![Cube ADC configuring 3](media/cube_adc_configuring_3.png) 545 | 546 | ![Cube ADC configuring 4](media/cube_adc_configuring_4.png) 547 | 548 | Além disso, é necessário ligar as interrupções do ADC, na aba NVIC: 549 | 550 | ![Cube ADC enable NVIC](media/cube_adc_nvic_enable.png) 551 | 552 | E adicionar o DMA na aba DMA, mudando o Mode para "Circular" e Data Width para 553 | "Word": 554 | 555 | ![Cube ADC add DMA](media/cube_adc_add_dma.png) 556 | 557 | Após essas configurações, podemos gerar o código. 558 | 559 | Para podermos iniciar o ADC, basta adicionar a função `MX_ADC1_Init()` 560 | na main ou em alguma outra função de inicialização, as funções relacionadas 561 | ao adc estão no `stm32f3xx_hal_adc.c`. 562 | 563 | Como estamos utilizando o DMA e a configuração de leitura contínua, é necessário criar 564 | um buffer para guardar essas leituras, então, em algum lugar do código, é 565 | necessário declarar um vetor com um tamanho múltiplo do número de canais (é 566 | necessário um número razoavelmente grande, para evitar que ele encha o buffer 567 | muito rápido), como por exemplo `uint32_t adc_buffer[512];` Com esse buffer 568 | criado, basta inicializar o ADC com DMA, e as leituras passarão a ser feitas 569 | automaticamente: 570 | 571 | ```c 572 | HAL_ADC_Start_DMA(&hadc1, adc_buffer, 512); 573 | ``` 574 | 575 | Isso deve ser adicionado depois do Init, e apenas uma vez, quando isso for 576 | feito, os canais serão lidos na ordem definida na configuração acima (os ranks) 577 | e o vetor será preenchido na mesma ordem, quando o vetor encher, o DMA acionará 578 | uma interrupção, que pode pode ser acessada por: 579 | 580 | ```c 581 | void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc); 582 | ``` 583 | 584 | Essa função deve ser definida em algum lugar do programa, como após a main ou em 585 | um arquivo relacionado a utilização do ADC, como sensores.c caso o ADC seja 586 | utilizado para leitura de sensores. Nela deve-se manipular os dados do buffer, 587 | por exemplo: 588 | 589 | ```c 590 | #define NUMBER_OF_SENSORS 2 591 | #define READINGS_PER_SENSOR 256 592 | 593 | // [...] 594 | 595 | void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { 596 | if (hadc->Instance == hadc1.Instance) { 597 | uint32_t readings[NUMBER_OF_SENSORS] = {0}; 598 | 599 | for (int i = 0; i < READINGS_PER_SENSOR; i++) { 600 | for (int j = 0; j < NUMBER_OF_SENSORS; j++) { 601 | readings[j] += adc_buffer[NUMBER_OF_SENSORS * i + j]; 602 | } 603 | } 604 | 605 | for (int i = 0; i < NUMBER_OF_SENSORS; i++) { 606 | readings[i] /= READINGS_PER_SENSOR; 607 | line_sensor[i] = readings[i]; 608 | } 609 | } 610 | } 611 | ``` 612 | 613 | Esse programa faz a média das 256 leituras de cada canal do adc e salva num 614 | vetor global chamado `line_sensor`. 615 | 616 | # UART e DMA 617 | 618 | Utilizamos UART em conjunto com DMA para fazer comunicação serial. Exemplos de 619 | uso na equipe são no Tracer e nos sumôs autônomos, que recebem dados pelo 620 | aplicativo por bluetooth. 621 | 622 | Começando pelo Cube. Primeiro, na parte esquerda de Pinout, na seção 623 | Connectivity, encontre o periférico USART1 e, na parte Mode da parte que abrir, 624 | escolha Mode Asynchronous. Ao fazer isso, os pinos PA10 e PA9 serão setados na 625 | parte da direita. 626 | 627 | ![Cube UART configuring 1](media/cube_uart_configuring_1.png) 628 | 629 | Na parte Configuration abaixo, aparecem algumas opções. Em Parameter Settings, 630 | só precisamos mudar a Baud Rate para 9600 Bits/s. 631 | 632 | ![Cube UART configuring 2](media/cube_uart_configuring_2.png) 633 | 634 | Em NVIC Settings, precisamos habilitar o USART1 global interrupt. 635 | 636 | ![Cube UART configuring 3](media/cube_uart_configuring_3.png) 637 | 638 | Em DMA Settings, devemos clicar em Add. Aparecerá um campo escrito “Select”. 639 | Então, clicamos lá e selecionamos USART1_RX. Embaixo, em DMA Request Settings, 640 | devemos escolher Mode Circular. Certifique-se que Data Width está como Byte e, 641 | em Increment Address, Memory esteja selecionado. 642 | 643 | ![Cube UART configuring 4](media/cube_uart_configuring_4.png) 644 | 645 | Com isso, podemos gerar o código. 646 | 647 | ## Recebendo dados 648 | 649 | Precisamos adicionar a função de init do USART e a função de init do DMA na main 650 | ou em alguma função de inicialização. 651 | 652 | ```c 653 | MX_DMA_Init(); 654 | MX_USART1_UART_Init(); 655 | ``` 656 | 657 | Agora dividiremos em dois casos: o caso em que o pacote de dados consiste de 658 | apenas 1 byte e o caso em que consiste de um número definido de bytes (maior que 659 | 1). 660 | 661 | ### Pacote de 1 byte 662 | 663 | Para o caso em que o pacote de dados recebido pela serial é sempre de 1 byte, 664 | basta adicionarmos na main ou em alguma função de inicialização: 665 | 666 | ```c 667 | uint8_t rx_data = 0; 668 | 669 | void serial_init(void) { 670 | MX_DMA_Init(); 671 | MX_USART1_UART_Init(); 672 | HAL_UART_Receive_DMA(&huart1, &rx_data, 1); 673 | } 674 | ``` 675 | 676 | A variável rx_data será atualizada sempre que receber um byte. 677 | 678 | ### Pacote de mais de 1 byte 679 | 680 | Para o caso em que o pacote de dados recebido pela serial é sempre de um tamanho 681 | fixo de bytes maior que 1, precisamos utilizar a função de callback para fazer o 682 | processamento do pacote. Por exemplo, vamos processar um pacote com informações 683 | sobre velocidade estruturado da seguinte forma: 684 | 685 | - O primeiro byte contém um valor que indica que é o começo do pacote, 686 | chamaremos de header. Esse valor será 255. 687 | - O segundo byte contém um valor que indica o sentido da velocidade da esquerda 688 | (0 para frente e 1 para trás). 689 | - O terceiro byte contém o valor da velocidade da esquerda (valor de 0 a 255). 690 | - O quarto byte contem um valor que indica o sentido da velocidade da direita, 691 | usando a mesma convenção do segundo byte. 692 | - O quinto byte contém o valor da velocidade da direita. 693 | - O sexto byte contém um valor que indica fim de um pacote. Chamaremos de tail. 694 | O valor será 254. 695 | 696 | A função de callback pode ser definida da seguinte forma: 697 | 698 | ```c 699 | #define PACKET_SIZE 6 700 | 701 | typedef enum rx_packet_index { 702 | RX_PACKET_HEADER_I = 0, 703 | RX_PACKET_LEFT_DIRECTION_I, 704 | RX_PACKET_LEFT_SPEED_I, 705 | RX_PACKET_RIGHT_DIRECTION_I, 706 | RX_PACKET_RIGHT_SPEED_I, 707 | RX_PACKET_TAIL_I, 708 | } rx_packet_index_t; 709 | 710 | typedef enum rx_packet_byte { 711 | RX_PACKET_HEADER = 0xFF, 712 | RX_PACKET_TAIL = 0xFE, 713 | } rx_packet_byte_t; 714 | 715 | typedef enum rx_packet_status { 716 | RX_STATUS_ERROR = 0, 717 | RX_STATUS_SUCCESS = 1, 718 | } rx_packet_status_t; 719 | 720 | uint8_t rx_data[PACKET_SIZE] = {0}; 721 | rx_packet_status_t packet_status = RX_STATUS_ERROR; 722 | 723 | int16_t speed_left = 0; 724 | int16_t speed_right = 0; 725 | 726 | void serial_init(void) { 727 | MX_DMA_Init(); 728 | MX_USART1_UART_Init(); 729 | HAL_UART_Receive_DMA(&huart1, rx_data, PACKET_SIZE); 730 | } 731 | 732 | void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart) { 733 | if (huart->Instance != USART1) { 734 | return; 735 | } 736 | 737 | if (rx_data[RX_PACKET_HEADER_I] != RX_PACKET_HEADER || 738 | rx_data[RX_PACKET_TAIL_I] != RX_PACKET_TAIL) { 739 | packet_status = RX_STATUS_ERROR; 740 | return; 741 | } 742 | 743 | speed_left = rx_data[RX_PACKET_LEFT_DIRECTION_I] == 0 744 | ? rx_data[RX_PACKET_LEFT_SPEED_I] 745 | : -rx_data[RX_PACKET_RIGHT_SPEED_I]; 746 | 747 | speed_right = rx_data[RX_PACKET_RIGHT_DIRECTION_I] == 0 748 | ? rx_data[RX_PACKET_RIGHT_SPEED_I] 749 | : -rx_data[RX_PACKET_LEFT_SPEED_I]; 750 | } 751 | ``` 752 | 753 | Note que primeiro checamos se header e tail estão corretos antes de atribuir os 754 | valores das velocidades. 755 | 756 | ### Transmitindo dados 757 | 758 | É possível transmitir dados por UART utilizando interrupts ou DMA, porém, para 759 | nossas aplicações na equipe, basta simplesmente transmitirmos no momento que for 760 | necessário. Para transmitir, basta utilizar a seguinte função (desde que o UART 761 | tenha sido inicializado com `MX_USART1_UART_Init`): 762 | 763 | ```c 764 | HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) 765 | ``` 766 | 767 | No primeiro argumento vai o endereço do handle do UART, no nosso caso, 768 | `&huart1`. No segundo argumento vai o vetor de bytes para ser transmitido. No 769 | terceiro argumento vai o tamanho desse vetor de dados. No quarto argumento vai o 770 | timeout, ou seja, o tempo que ficará tentando enviar os dados caso dê alguma 771 | falha na transmissão. A função retorna o estado, que pode ser `HAL_TIMEOUT`, 772 | `HAL_OK` ou `HAL_BUSY`. 773 | 774 | # Interrupções 775 | 776 | Interrupções são pequenas funções chamadas enquanto a rotina principal está em 777 | execução, sem serem referenciadas explicitamente no programa principal. Uma 778 | série de eventos é capaz de invocar uma interrupção (como *overflow* de um 779 | Timer, término de uma conversão ADC, mudança de tensão de um pino, recebimento 780 | de pacote UART ou I2C, etc. Uma lista completa pode ser encontrada no manual do 781 | STM32). 782 | 783 | ## NVIC 784 | 785 | A configuração dos diversos eventos que invocam interrupções é explicada nas 786 | devidas seções deste documento. É importante ressaltar, no entanto, que o 787 | **NVIC** (*nested vector interrupt controller*) da respectiva interrupção esteja 788 | habilitado. O NVIC corresponde a um conjunto de ponteiros apontando para as 789 | funções que devem ser chamadas em cada evento. Para que a sua função seja 790 | chamada na interrupção, lembre-se de habilitá-la. 791 | 792 | ## Interrupções externas 793 | 794 | Vamos nos debruçar sobre um evento específico que invoca interrupções, a mudança 795 | de estado de um pino, ou seja, mudança de high para low ou vice versa. 796 | 797 | As interrupções por mudança de estado são chamadas External Interrupts **EXTI** 798 | e são numeradas de 0 a 15 (EXTI0, EXTI1 …) . Todos os pinos dos STM32 são 799 | capazes de gerar uma interrupção. Para isso ser possível, uma mesma interrupção 800 | é invocada por mais de um pino. 801 | 802 | ![Interrupt registers](media/interrupt_registers.png) 803 | 804 | Dessa forma, se os pinos PA**0** e PB**1** estiverem sendo usados, serão 805 | chamadas EXT0 e EXT1, respectivamente. Caso os pinos PA**0** e PB**0** estiverem 806 | sendo usados, a EXT0 será chamada em ambos os casos e será necessário verificar, 807 | dentro da interrupção, qual pino sofreu uma mudança de estado. 808 | 809 | Para habilitar uma interrupção externa, selecione o pino e configure como 810 | GPIO_EXTIn, onde n é o número do pino correspondente. 811 | 812 | ![Cube select EXTI pin function](media/cube_select_pin_function_exti.png) 813 | 814 | No lado esquerdo, na seção "System Core", clique em "GPIO". 815 | 816 | Na parte "Configuration", habilite a entrada correspondente no NVIC. 817 | 818 | ![Cube GPIO set EXTI NVIC](media/cube_gpio_set_exti_nvic.png) 819 | 820 | Na aba GPIO, configure quando o pino irá chamar a interrupção, na borda de 821 | subida (*low* -> *high*), de descida (*high* -> *low*), ou em ambas. 822 | 823 | ![Cube GPIO select mode](media/cube_gpio_select_mode.png) 824 | 825 | Essa é a toda a configuração necessária, pode-se agora gerar o código do 826 | projeto. No arquivo .c de sua preferência, devemos declarar a função Callback 827 | para a interrupção, chamada de `HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)`. Para 828 | todo pino configurado, essa função será chamada quando houver uma mudança de 829 | estado. 830 | 831 | A declaração original dessa função se encontra no arquivo 832 | `stm32f-xx_hal_gpio.c`. 833 | 834 | O exemplo mais simples de uso de interrupção externa consiste em observar a 835 | mudança de estado de um botão ou uma chave e atualizar o estado de um LED de 836 | acordo com a alteração ocorrida. 837 | 838 | ```c 839 | void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { 840 | if (GPIO_Pin == Button_Pin) 841 | HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); 842 | } 843 | } 844 | ``` 845 | 846 | No exemplo acima, sempre que a função for invocada pela mudança de estado do 847 | botão o led trocará de estado. Para o caso de uma interrupção de borda de subida 848 | e descida, o led permanece aceso enquanto o botão é pressionado. Se a 849 | interrupção estivesse configurada somente para borda de descida ou somente para 850 | borda de subida, o led mudaria de estado a cada vez que o botão fosse 851 | pressionado. 852 | 853 | Isso também pode ser utilizado na leitura de alguns sensores digitais, como 854 | encoders e alguns modelos de sensores de distância, como é utilizado na equipe. 855 | Confira os códigos do Moai, do Tracer e do uMouse para mais detalhes. 856 | 857 | # Timers 858 | 859 | Nas configurações dos timers no Cube, podemos escolher a fonte do timer. Ou 860 | seja, qual sinal será utilizado para o timer. Normalmente, utilizamos o clock 861 | interno do microcontrolador. No nosso exemplo, é um sinal periódico de onda 862 | quadrada com frequência de 64 MHz. Vamos considerar esse sinal para a 863 | explicação. 864 | 865 | Podemos escolher, também, três parâmetros importantes: o prescaler, o counter 866 | mode e o counter period. 867 | 868 | O prescaler é o número que dividirá a frequência da fonte usada para o timer. No 869 | nosso caso, o clock interno. Isso não vai reduzir o clock interno, apenas 870 | considerará para o timer um sinal com frequência dividida. No Cube, o valor que 871 | dividirá a frequência do sinal será o número que for colocado no campo de 872 | prescaler somado com 1. Isso porque o prescaler não pode ser 0. Então, se 873 | colocarmos o prescaler como 0, a frequência será dividida por 1. Se colocarmos o 874 | prescaler como 63, a frequência será dividida por 64 e, no nosso caso, obteremos 875 | um sinal com frequência de 1 MHz e período de 1 𝜇s. O timer conta o número de 876 | bordas de subida do sinal após a aplicação do prescaler. 877 | 878 | O counter mode é simplesmente para escolhermos se queremos que o contador conte 879 | para cima ou para baixo. Ou seja, de 0 até algum valor ou de algum valor até 0. 880 | 881 | O counter period é esse “algum valor”. Ou seja, é até quanto o timer deve contar 882 | ou a partir de quanto se for no caso de contar para baixo. No nosso caso, usando 883 | um prescaler de 63, temos que o período será de 1 𝜇s. Então, a cada 1 𝜇s, o 884 | valor do timer aumenta (ou diminui) em 1. Se colocarmos o counter mode como up e 885 | o counter period como 1000, o timer contará de 0 a 1000, aumentando o valor em 1 886 | a cada 1 𝜇s. Ou seja, atingirá o valor 1000 depois de 1000 𝜇s. Ao atingir o 887 | valor 1000, na próxima borda de subida do sinal o valor volta para 0. Há um 888 | sinal que sinaliza quando ocorre esse *overflow* (ou underflow no caso de 889 | counter mode down). Podemos usar esse final para fazer um interrupt a cada fim 890 | de contagem, caso que faremos em seguida. 891 | 892 | No exemplo a seguir vou dizer *overflow* algumas vezes, mas poderia ser também 893 | usado o underflow com counter mode down. 894 | 895 | Digamos que você queira fazer um contador de microssegundos com resolução de 100 896 | 𝜇s. Um jeito de fazer isso de forma confiável é usar um interrupt quando ocorre 897 | o *overflow*. A ideia é configurar um timer que dá *overflow* a cada 100 𝜇s e 898 | uma interrupção que soma 100 a uma variável a cada *overflow*. Precisamos 899 | primeiro pensar em que valores de prescaler e counter period devemos usar para 900 | isso acontecer. Considerando nosso clock de 64 MHz, temos 64 bordas de subida do 901 | clock a cada 1 𝜇s. Logo, basta dividir a frequência por 64 (colocar no 63 902 | prescaler) e usar um counter period de 99. Assim, depois de 99 bordas de subida, 903 | o timer terá o valor 99 e, na próxima borda de subida, ocorrerá o *overflow*. Ou 904 | seja, teremos um *overflow* a cada 100 bordas de subida do clock, o que equivale 905 | a 100 𝜇s. 906 | 907 | Vamos para a configuração no Cube. 908 | 909 | Na parte da esquerda em Pinout, vamos escolher um timer. Escolherei o timer 2 910 | (TIM2). Ao clicar nele, uma seção aparece ao lado, contendo as partes "Mode" e 911 | "Configuration". Na parte "Mode", basta escolhermos a Clock Source como Internal 912 | Clock (não precisa setar um pino porque é totalmente interno). 913 | 914 | ![Cube Timer set clock source](media/cube_timer_set_internal_clock.png) 915 | 916 | Na parte "Configuration", vemos a configuração do timer. Como mencionado 917 | anteriormente, vamos deixar o Prescaler como 63, o Counter Mode como Up e o 918 | Counter Period como 99. 919 | 920 | ![Cube Timer configuration](media/cube_timer_config.png) 921 | 922 | Em NVIC Settings, vamos habilitar o interrupt. 923 | 924 | ![Cube Timer enable interrupt](media/cube_timer_interrupt_enable.png) 925 | 926 | Em Clock Configuration, tenha certeza de que a frequência está em 64 MHz. 927 | 928 | Com isso, terminamos a configuração no Cube, podemos gerar o código. 929 | 930 | Precisamos adicionar a função `MX_TIM2_Init()` a main ou a algum outra função de 931 | inicialização personalizada. As funções relacionadas ao timer podem ser 932 | encontradas em `Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_tim.c`. Podemos 933 | ver os protótipos das funções em 934 | `Drivers/STM32F3xx_HAL_Driver/Inc/stm32f3xx_hal_tim.h`. Precisamos inicializar o 935 | interrupt, adicionando a seguinte linha após a função `MX_TIM2_Init()` ser 936 | chamada. 937 | 938 | ```c 939 | HAL_TIM_Base_Start_IT(&htim2); 940 | ``` 941 | 942 | Agora, precisamos definir a função que é chamada quando ocorre o interrupt. A 943 | declaração da função é void `HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef 944 | *htim)`, e vamos definir da seguinte forma, em main.c ou em um arquivo 945 | relacionado ao uso desse timer, como `timer.c`: 946 | 947 | ```c 948 | void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { 949 | if (htim->Instance == TIM2) { 950 | time_elapsed += 100; 951 | } 952 | } 953 | ``` 954 | 955 | A variável time_elapsed foi declarada anteriormente de forma global com o tipo 956 | `uint64_t` e inicializada com 0. Ela foi declarada com esse tipo para não nos 957 | preocuparmos com *overflow*. Dependendo da aplicação, `uint32_t` é o suficiente. 958 | Para verificar se é o suficiente, calcule o valor máximo de uma variável de 32 959 | bits e pense que são microssegundos. Se a placa nunca permanecer ligada por essa 960 | quantidade de tempo, é o suficiente. 961 | 962 | Pronto. Agora se quiser verificar quanto tempo passou entre um evento e outro, 963 | basta checar o valor dessa variável no momento de um evento e no momento de 964 | outro evento e subtrair um do outro. 965 | 966 | **Observação**: se você quiser contar o tempo em milissegundos, não é necessário 967 | fazer isso. Com o código gerado pelo Cube, já existe uma função que retorna o 968 | tempo que passou desde quando a função `HAL_Init()` foi chamada, em ms. É a 969 | função `HAL_GetTick()`. 970 | 971 | Algumas macros interessantes de timer são: 972 | 973 | * `__HAL_TIM_SET_COUNTER(__HANDLE__, __COUNTER__)`: seta o valor do timer. No 974 | lugar de `__HANDLE__`, utiliza-se o endereço da handle do timer em questão. No 975 | nosso exemplo seria `&htim2`. Em `__COUNTER__`, o valor desejado. Por exemplo, 976 | se quiséssemos zerar o valor do timer, utilizaríamos 977 | `__HAL_TIM_SET_COUNTER(&htim2, 0)`. 978 | * `__HAL_TIM_GET_COUNTER(__HANDLE__)`: retorna o valor atual do timer. No lugar 979 | de `__HANDLE__`, utiliza-se o endereço da handle do timer em questão. No nosso 980 | exemplo seria `&htim2`. Por exemplo, se quiséssemos ler o valor do timer, 981 | utilizaríamos `__HAL_TIM_GET_COUNTER(&htim2)`. 982 | 983 | As aplicações de timer mais utilizadas na equipe são em PWM e PPM, que serão 984 | explicadas na próxima seção. 985 | 986 | # PWM 987 | 988 | Não cabe aqui explicar o que é uma PWM. Utilizamos saídas de PWM principalmente 989 | para gerar sinais para drivers de locomoção. Para gerar sinais de PWM, 990 | utilizamos pinos com timer com a função de PWM Generation. 991 | 992 | Começando pelo Cube, escolhemos um pino que tenha timer, como o PA8, e setamos 993 | como timer. No caso do PA8, é o canal 1 do timer 1 (TIM1_CH1). Ele fica amarelo 994 | porque faltam coisas a serem configuradas. 995 | 996 | ![Cube PWM set pin as timer](media/cube_pwm_set_pin_timer.png) 997 | 998 | Na parte ao lado esquerdo, em "Timers", encontramos o TIM1. Clicando, aparece a 999 | seção com as partes "Mode" e "Configuration". Em "Mode", setamos a fonte de 1000 | clock (Clock Source) como Internal Clock. Ou seja, vamos utilizar o clock 1001 | interno do microcontrolador para gerar o sinal de PWM. Se você configurou o 1002 | clock como no início do documento, a frequência do clock para esse exemplo é de 1003 | 64 MHz. Esse número varia de microcontrolador para microcontrolador e é 1004 | importante para as contas que serão feitas na configuração da PWM. Em Channel 1, 1005 | escolhemos a opção PWM Generation CH1. Com isso, o pino setado fica verde. 1006 | 1007 | ![Cube PWM set configurations 1](media/cube_pwm_set_config_1.png) 1008 | 1009 | Após ter feito isso, aparecem opções na parte "Configuration". Em Counter 1010 | Settings, vamos alterar o Prescaler e o Counter Period. 1011 | 1012 | ![Cube PWM set configurations 2](media/cube_pwm_set_config_2.png) 1013 | 1014 | Prescaler é o valor que dividirá a frequência da fonte de clock (no nosso caso, 1015 | o clock interno, com frequência de 64 MHz). Vale ressaltar que o valor que 1016 | realmente dividirá a frequência do clock será o valor que colocarmos como 1017 | prescaler + 1. Ou seja, se desejamos dividir a frequência por 64, devemos 1018 | colocar 63 como prescaler aqui. 1019 | 1020 | O Counter Period é a resolução da PWM. Ou seja, o valor que será o duty cycle de 1021 | 100%. Se o Counter Period for 1000, por exemplo, o “duty cycle”irá variar de 0 a 1022 | 1000. Então, se a PWM for para um motor, no programa, setar uma velocidade de 1023 | 1000 significa colocar o motor em 100% da velocidade, 500 seria 50% da 1024 | velocidade e assim por diante. 1025 | 1026 | A frequência da PWM gerada é calculada por: 1027 | 1028 | ![f=fclock / \[\(Prescaler + 1029 | 1)*CounterPeriod](https://latex.codecogs.com/png.latex?f%20%3D%20%5Cfrac%7Bf_%7Bclock%7D%7D%7B%28Prescaler%20+%201%29*CounterPeriod%7D) 1030 | 1031 | Para facilitar as contas, vamos colocar 63 no Prescaler e 1000 no Counter 1032 | Period. Como o clock é de 64 MHz, obtemos uma PWM com frequência de 1 kHz. 1033 | 1034 | ![Cube PWM set configurations 3](media/cube_pwm_set_config_3.png) 1035 | 1036 | Mais abaixo, em PWM Generation Channel 1, vamos mudar Fast Mode para Enable. Com 1037 | isso, podemos apertar OK e concluímos a configuração da geração de PWM no Cube. 1038 | Podemos gerar o código. 1039 | 1040 | ![Cube PWM set configurations 4](media/cube_pwm_set_config_4.png) 1041 | 1042 | Antes de gerar, vamos configurar outra PWM no canal 2 do timer 1 para termos 1043 | algo parecido com o que usamos nos robôs: uma PWM para o motor direito e outra 1044 | para o motor esquerdo. Para isso, basta voltar para o Pinout e, na parte do lado 1045 | esquerdo, em TIM1, escolher, em Channel 2, a opção PWM Generation CH2. Ao fazer 1046 | isso, o pino PA9 no lado direito é setado como TIM1_CH2 automaticamente e fica 1047 | verde. 1048 | 1049 | ![Cube PWM set configurations 5](media/cube_pwm_set_config_5.png) 1050 | 1051 | Como é o mesmo timer, basta ir nas configurações do TIM1 novamente na parte 1052 | "Configuration" e, em PWM Generation Channel 2, setar Fast Mode para Enable. 1053 | 1054 | ![Cube PWM set configurations 6](media/cube_pwm_set_config_6.png) 1055 | 1056 | Precisamos adicionar a função `MX_TIM1_Init()` a main ou a alguma outra função 1057 | de inicialização. Para que a saída de PWM seja gerada efetivamente, temos que 1058 | inicializar a geração de PWM nos canais dos timers escolhidos. Para isso, temos 1059 | que utilizar as funções: 1060 | 1061 | ```c 1062 | void HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); 1063 | void HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); 1064 | ``` 1065 | 1066 | Isso deve ser adicionado depois do Init. 1067 | 1068 | Para gerar PWM com o valor desejado, utilizamos a macro: 1069 | 1070 | ```c 1071 | __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) 1072 | ``` 1073 | 1074 | Em `__HANDLE__`, no nosso caso, colocamos `&htim1`, em `__CHANNEL__`, 1075 | `TIM_CHANNEL_1` ou `TIM_CHANNEL_2` e em `__COMPARE__` colocamos o valor que 1076 | queremos (no nosso caso, um valor de 0 a 1000). Assim, se colocarmos no loop 1077 | principal da main as seguintes linhas: 1078 | 1079 | ```c 1080 | __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 755); 1081 | __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 445); 1082 | ``` 1083 | 1084 | O programa ficará gerando uma PWM de duty cycle 75.5% no canal 1 (pino PA8) e 1085 | uma PWM de duty cycle 44.5% no canal 2 (pino PA9). Esse é o princípio básico. 1086 | Partindo disso, podemos desenvolver qualquer lógica para fazer os motores 1087 | girarem da forma que queremos. 1088 | 1089 | ## Geração de PPM 1090 | 1091 | Para geração de PPM, utilizamos o mesmo princípio, porque basta interpretarmos a 1092 | PPM como uma PWM de duty cycle limitado. Só precisamos pensar nos valores de 1093 | Prescaler e Counter Period para fazer o sinal correto. 1094 | 1095 | Para facilitar as contas e a forma de trabalhar com PPM, recomendo colocar o 1096 | Prescaler sempre como o valor da frequência de clock em MHz subtraído de 1 e o 1097 | Counter Period como 20000. No nosso caso, colocaremos o Prescaler como 63 e o 1098 | Counter Period como 20000. Utilizando a fórmula da frequência da PWM gerada com 1099 | esses valores e a frequência do clock (64 MHz), obtemos uma frequência de 50 Hz. 1100 | O período é, portanto, 1 / 50 = 0.02 s = 20 ms, que é o período de uma PPM. Como 1101 | o Counter Period é 20000, pensar nos valores que a PPM pode ter é simples: 1000 1102 | a 2000. Portanto basta limitar o valor que colocamos no terceiro parâmetro da 1103 | macro `__HAL_TIM_SET_COMPARE` a um intervalo de 1000 a 2000. 1104 | 1105 | ## Leitura de PPM 1106 | 1107 | Utilizamos a leitura de PPM principalmente para ler sinais de um receptor. No 1108 | caso de robôs sem arma, precisamos ler, geralmente, duas PPMs (elevator e 1109 | aileron). No caso de robôs com arma, precisamos ler, geralmente três PPMs 1110 | (elevator, aileron e throttle). No exemplo desse documento, faremos a leitura de 1111 | três sinais de PPM. 1112 | 1113 | Vamos começar configurando no Cube. Os pinos que usamos para ler PPM devem ser 1114 | de timer e desempenhar a função Input Capture. No nosso exemplo, vamos escolher 1115 | o timer 1, canais 1, 2 e 3. No lado esquerdo, em TIM1, escolhemos Clock Source 1116 | como Internal Clock e em Channel 1, Channel 2 e Channel 3 colocamos a opção 1117 | Input Capture direct mode. Com isso, os pinos PA8, PA9 e PA10 serão selecionados 1118 | no Pinout na parte direita e estarão com a cor verde. 1119 | 1120 | ![Cube PPM read set configurations 1](media/cube_ppm_read_set_config_1.png) 1121 | 1122 | Na parte "Configuration", apareceram algumas opções. 1123 | 1124 | Utilizando o mesmo princípio da parte de geração de PPM, vamos setar o Prescaler 1125 | como 63. O Counter Period precisa ser o valor máximo de 16 bits (65535). 1126 | 1127 | ![Cube PPM read set configurations 2](media/cube_ppm_read_set_config_2.png) 1128 | 1129 | Descendo um pouco, vemos as configurações de Input Capture Channel 1, Channel 2 1130 | e Channel 3. Iremos mudar a Polarity Selection. Esse parâmetro de configuração 1131 | escolhe quando ocorre o interrupt no pino setado como Input Capture. Para ler 1132 | PPM, queremos essencialmente saber o tempo que o pino esteve em HIGH. Logo, é 1133 | melhor que ocorra interrupt tanto quando o pino passa de LOW para HIGH como 1134 | quando passa de HIGH para LOW. Por isso, escolhemos Both Edges em Polarity 1135 | Selection. Dependendo do microcontrolador, pode ser que a opção Both Edges não 1136 | exista. Trataremos esse caso depois. 1137 | 1138 | ![Cube PPM read set configurations 3](media/cube_ppm_read_set_config_3.png) 1139 | 1140 | Na aba NVIC Settings, precisamos habilitar o interrupt. 1141 | 1142 | Em alguns timers, haverá somente a opção de global interrupt e, em outros, terão 1143 | outras opções. No primeiro caso, habilite o global interrupt. No segundo caso, 1144 | habilite a opção de capture compare interrupt. Por exemplo, no caso do timer 1 1145 | desse microcontrolador: 1146 | 1147 | ![Cube PPM read enable interrupt](media/cube_ppm_read_enable_interrupt.png) 1148 | 1149 | Após isso, podemos gerar o código. 1150 | 1151 | Precisamos adicionar a função `MX_TIM1_Init()` a main ou alguma outra função de 1152 | inicialização personalizada. 1153 | 1154 | Para que o timer comece a contar, devemos inicializá-lo no código. Para isso, 1155 | utilizamos: 1156 | 1157 | ```c 1158 | HAL_TIM_Base_Start(&htim1); 1159 | ``` 1160 | 1161 | Para que os interrupts aconteçam, devemos habilitá-los no código. Para isso, 1162 | utilizamos: 1163 | 1164 | ```c 1165 | HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); 1166 | HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2); 1167 | HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_3); 1168 | ``` 1169 | 1170 | Isso deve ser adicionado depois do Init. 1171 | 1172 | Agora, precisamos definir o que fazer quando ocorre a interrupção. Para isso, 1173 | definimos a função: 1174 | 1175 | ```c 1176 | void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim); 1177 | ``` 1178 | 1179 | Essa função pode ser definida em `main.c` ou em `tim.c`. Uma forma de definir 1180 | essa função é a seguinte: 1181 | 1182 | ```c 1183 | #define PPM_CHANNELS 3 1184 | 1185 | typedef enum _radio_channel { 1186 | CH1, /**< Radio Channel 1 */ 1187 | CH2, /**< Radio Channel 2 */ 1188 | CH3, /**< Radio Channel 3 */ 1189 | } radio_channel_t; 1190 | 1191 | static uint16_t ppm_receiver[PPM_CHANNELS] = {0}; 1192 | 1193 | void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim) { 1194 | static uint16_t _tim1_ch1[2] = {0, 0}; 1195 | static uint16_t _tim1_ch2[2] = {0, 0}; 1196 | static uint16_t _tim1_ch3[2] = {0, 0}; 1197 | 1198 | if (TIM1 == htim->Instance) { 1199 | if (HAL_TIM_ACTIVE_CHANNEL_1 == htim->Channel) { 1200 | if (ppm_radio_ch1_is_high()) { 1201 | _tim1_ch1[0] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); 1202 | } else { 1203 | _tim1_ch1[1] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); 1204 | ppm_receiver[CH1] = _tim1_ch1[1] - _tim1_ch1[0]; 1205 | } 1206 | } else if (HAL_TIM_ACTIVE_CHANNEL_2 == htim->Channel) { 1207 | if (ppm_radio_ch2_is_high()) { 1208 | _tim1_ch2[0] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); 1209 | } else { 1210 | _tim1_ch2[1] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); 1211 | ppm_receiver[CH2] = _tim1_ch2[1] - _tim1_ch2[0]; 1212 | } 1213 | } else if (HAL_TIM_ACTIVE_CHANNEL_3 == htim->Channel) { 1214 | if (ppm_radio_ch3_is_high()) { 1215 | _tim1_ch3[0] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3); 1216 | } else { 1217 | _tim1_ch3[1] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3); 1218 | ppm_receiver[CH3] = _tim1_ch3[1] - _tim1_ch3[0]; 1219 | } 1220 | } 1221 | } 1222 | } 1223 | ``` 1224 | 1225 | As funções `ppm_radio_chx_is_high()` leem o valor do pino como GPIO. 1226 | 1227 | Analisando o código, vemos que primeiro precisamos checar qual timer gerou o 1228 | interrupt. Depois, quais dos canais desse timer que sofreu uma borda de clock. 1229 | Temos que contar o tempo que o sinal ficou em alto. Então, como ocorre o 1230 | interrupt em bordas de descida como de subida do clock, temos que subtrair o 1231 | valor capturado do timer quando ocorreu a borda descida do valor capturado do 1232 | timer quando ocorreu a borda de subida. Para detectar se ocorreu uma borda de 1233 | subida ou de descida, utilizamos a função `ppm_radio_chx_is_high()`. Se for uma 1234 | borda de subida, salvamos o valor na primeira posição do vetor. Caso contrário, 1235 | na segunda posição. Subtraindo os valores do vetor, temos a leitura da PPM. 1236 | 1237 | Agora, veremos o caso em que não existe a opção Both Edges no timer. Nesse caso, 1238 | devemos utilizar uma interrupção externa de GPIO com um timer, explicado na 1239 | seção sobre interrupções. Sempre é possível usar uma interrupção externa de GPIO 1240 | em Both Edges. Então, o código seria zerar o timer na borda de descida e pegar o 1241 | valor do timer na borda de subida, com o timer configurado com prescaler igual 1242 | foi configurado aqui e Counter Period um valor maior que 2000. 1243 | 1244 | O motivo de utilizarmos o Counter Period como o valor máximo de 16 bits e a 1245 | variável como `uint16_t` é para que, quando ocorre o overflow do timer, a 1246 | subtração realizada no código continue correta. Por exemplo, se a borda de 1247 | subida ocorreu com o valor capturado do timer sendo 65500 e a borda de descida 1248 | ocorreu 2 ms depois, o valor na borda de descida seria 67500. Porém, como a 1249 | variável é de 16 bits, ocorre o overflow e o valor começa a ser 0 a partir do 1250 | que seria 65536. Então, o valor capturado do timer 2 ms depois seria 67500 - 1251 | 65536 = 1964. Ao fazer a subtração do valor na borda de descida pelo valor na 1252 | borda de subida, estaremos fazendo 1964 - 65500 = -63536. Como a variável é 1253 | unsigned de 16 bits, esse valor negativo será -63536 + 65536 = 2000, o valor 1254 | esperado. 1255 | 1256 | # Encoders 1257 | 1258 | O Modo Encoder dos timers de hardware do STM32 oferece uma maneira eficiente de interagir com encoders de quadratura (A/B) diretamente usando os periféricos de timer. Este modo lida automaticamente com a contagem de pulsos e a detecção de direção em hardware, reduzindo significativamente a carga da CPU e melhorando a precisão em comparação com a contagem manual usando interrupções externas GPIO (EXTI), especialmente em velocidades mais altas. 1259 | 1260 | Antes de começar a configurar o encoder, é importante entender alguns pontos: 1261 | 1262 | 1. **Compatibilidade do Timer:** Nem todos os timers em um microcontrolador STM32 suportam o Modo Encoder. Verifique o datasheet do seu MCU específico e use o STM32CubeMX para confirmar quais timers (ex: TIM1, TIM2, TIM3, TIM4, TIM8 no STM32F303xC) oferecem essa funcionalidade. 1263 | 2. **Requisitos de Canal:** O modo Encoder normalmente requer o uso do **Canal 1 (TI1)** e do **Canal 2 (TI2)** da *mesma* instância de timer. 1264 | 3. **Resolução do Timer (16 bits vs. 32 bits):** 1265 | * **Timers de 32 bits (Recomendado):** Oferecem uma faixa de contagem muito grande (0 a 4.294.967.295), minimizando o risco de overflow (estouro para cima) ou underflow (estouro para baixo) do contador durante intervalos de medição típicos. Use-os se estiverem disponíveis para encoders. 1266 | * **Timers de 16 bits:** Possuem uma faixa limitada (0 a 65535). Se altas velocidades ou longos intervalos de medição fizerem o contador dar a volta (wrap around) frequentemente, você pode perder contagens ou obter leituras incorretas. Embora usáveis, podem exigir lógica de software adicional para lidar com overflows/underflows, lendo periodicamente o contador e acumulando o valor em uma variável mais larga (ex: 32 bits ou 64 bits). 1267 | 1268 | Para configurar o encoder, siga os passos abaixo: 1269 | 1270 | 1. **Habilitar Timer:** Na aba "Pinout & Configuration", navegue até "Timers" e selecione o timer **compatível** desejado (ex: `TIM5`). 1271 | 2. **Selecionar Modo Encoder:** 1272 | * Nas configurações de "Mode" do timer, encontre o dropdown "Combined Channels". 1273 | * Selecione **`Encoder Mode`**. Isso configurará automaticamente os pinos TIMx_CH1 e TIMx_CH2. Certifique-se de que esses pinos estejam disponíveis e não entrem em conflito com outros periféricos. 1274 | ![Configuração do Modo Encoder no CubeMX](media/cube_encoder_mode_config.png) 1275 | 3. **Configurar Parâmetros:** Vá para a aba "Configuration" -> "Parameter Settings" do timer. Configurações chave incluem: 1276 | * **`Counter Mode`:** Geralmente deve ser `Up` (Crescente) por padrão. 1277 | * **`Counter Period (AutoReload Register - ARR)`:** Defina este para o **valor máximo** para a resolução do timer (`0xFFFF` para 16 bits, `0xFFFFFFFF` para 32 bits). Isso permite que o timer conte livremente sem resetar baseado no próprio valor da contagem. 1278 | * **`Encoder Mode`:** Selecione **`Encoder Mode TI1 and TI2`**. Isso configura o timer para contar nas bordas de *ambas* as entradas dos Canais 1 e 2, fornecendo a maior resolução (4 contagens por ciclo do encoder) e detectando a direção com base na relação de fase entre TI1 e TI2. 1279 | * **`Input Filter (IC1 Filter, IC2 Filter)`:** Ajuda a filtrar sinais ruidosos do encoder. Um valor entre 0 (sem filtro) e 15 pode ser definido. Valores mais altos fornecem mais filtragem, mas introduzem latência. Ajuste com base na qualidade do sinal e requisitos de velocidade. Normalmente não é necessário alterar. 1280 | * **`Polarity (IC1 Polarity, IC2 Polarity)`:** Geralmente `Rising Edge` (Borda de Subida). Determina qual borda do sinal dispara a contagem. Garanta que corresponda aos requisitos do seu encoder, se necessário. Os padrões costumam ser suficientes. 1281 | ![Configuração dos Parâmetros do Modo Encoder](media/cube_encoder_mode_settings.png) 1282 | 4. **Configurar Pinos GPIO:** 1283 | * Vá para a aba "GPIO Settings" do timer *ou* encontre os pinos TIMx_CH1 e TIMx_CH2 na visualização principal do Pinout ou na seção System Core -> GPIO. 1284 | * É frequentemente recomendado habilitar o resistor de **`Pull-up`** interno para cada pino de entrada do encoder, especialmente se as saídas do encoder forem open-drain ou para garantir um estado definido quando desconectado. 1285 | ![Configuração de Pull-up no GPIO](media/cube_encoder_mode_gpio_settings.png) 1286 | 5. **Gerar Código:** Gere o código do projeto. 1287 | 1288 | Para implementar o encoder no seu código, siga os passos abaixo: 1289 | 1290 | 1. **Inicialize o Timer:** Chame a função gerada pelo CubeMX. 1291 | ```c 1292 | MX_TIM5_Init(); // Chama a inicialização configurada no CubeMX 1293 | ``` 1294 | 1295 | 2. **Inicie o Modo Encoder:** Após a inicialização, você precisa "ligar" o modo encoder para que o timer comece a contar os pulsos. Isso é feito **apenas uma vez**. 1296 | 1297 | ```c 1298 | // Use o handle do timer configurado (ex: htim5 para TIM5) 1299 | // TIM_CHANNEL_ALL indica para usar os canais configurados (CH1 e CH2 no nosso caso) 1300 | HAL_TIM_Encoder_Start(&htim5, TIM_CHANNEL_ALL); 1301 | ``` 1302 | 1303 | 3. **Leia a Contagem:** Para ler o valor atual do encoder, basta acessar o registrador do contador do timer a qualquer momento. O valor representa a posição líquida (considerando incrementos e decrementos). 1304 | 1305 | ```c 1306 | // Variável para armazenar a contagem 1307 | int32_t encoder_count; 1308 | 1309 | // No seu loop principal ou onde precisar ler o valor: 1310 | encoder_count = (int32_t)__HAL_TIM_GET_COUNTER(&htim5); 1311 | 1312 | // Agora a variável 'encoder_count' tem o valor atual do contador do encoder. 1313 | // Você pode usá-la para controle, cálculo de velocidade (em outra parte do código), etc. 1314 | // Exemplo simples: Imprimir o valor 1315 | // printf("Contagem Encoder: %ld\r\n", encoder_count); 1316 | ``` 1317 | 1318 | # I²C 1319 | 1320 | O I²C é um protocolo de comunicação serial síncrono muito utilizado em sensores 1321 | e outros dispositivos. Sua transmissão se estabelece em dois barramentos (bus): 1322 | SDA (dados) e SCL (clock). A comunicação é síncrona pois os dados são 1323 | transmitidos pelo SDA de acordo com os pulsos de clock gerados pelo SCL. Existem 1324 | dois modos configuráveis para o I²C: Master e Slave. O modo mais utilizado pela 1325 | equipe é o modo Master, no qual o microcontrolador transmite dados de controle e 1326 | faz aquisição de leituras de um sensor de distância, por exemplo. 1327 | 1328 | Para configurar o I²C no STMCubeMX, vá para a aba ao lado esquerdo na aba Pinout 1329 | & Configuration e veja que há algumas opções que podem ser selecionadas na seção 1330 | "Connectivity" (I2C1, I2C2, …). Clique na desejada e, em "Mode" na parte que 1331 | abriu, selecione a opção I2C: 1332 | 1333 | ![Cube I2C set configurations 1](media/cube_i2c_set_config_1.png) 1334 | 1335 | Ao clicar na opção, observe que aparecem em dois pinos as descrições I2C1_SDA e 1336 | I2C1_SCL, que são os barramentos de data e clock, respectivamente. 1337 | 1338 | Na parte "Configuration" apareceram algumas opções. Com isso, é possível 1339 | configurar o I2C selecionado: 1340 | 1341 | ![Cube I2C set configurations 2](media/cube_i2c_set_config_2.png) 1342 | 1343 | Na tela acima é possível selecionar diversos parâmetros: 1344 | 1345 | * I2C speed mode: Determina a velocidade de transmissão de dados - Standard 1346 | mode(100 KHz), Fast Mode(400KHz), Fast Mode Plus(1000KHz). Em geral deixamos 1347 | em Standard mode. 1348 | * Rise time e Fall time: deixar o default 1349 | * Analog filter: Filtram ruídos nas linhas SDA e SCL do I²C. Deixar como 1350 | enabled. 1351 | 1352 | Na aba "NVIC Settings" é possível ativar as interrupções para I²C também, mas 1353 | isto não será utilizado neste tutorial. 1354 | 1355 | Após gerar o código, haverá um arquivo `i2c.c` e `i2c.h` que contém todas as 1356 | configurações especificadas anteriormente no STMCubeMX. É necessário adicionar a 1357 | função de inicialização `MX_I2C1_Init()` à main.c ou a alguma outra função de 1358 | inicialização customizada. 1359 | 1360 | As principais funções utilizadas para enviar e receber dados são: 1361 | 1362 | ```c 1363 | HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) 1364 | HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) 1365 | HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) 1366 | HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) 1367 | ``` 1368 | Estas funções estão definidas em `stm32fxxx_hal_i2c.c` 1369 | 1370 | * `HAL_I2C_Master_Transmit` 1371 | 1372 | Envia dados do master para o slave (do microcontrolador para o sensor, por 1373 | exemplo). 1374 | 1375 | * `HAL_I2C_Master_Receive` 1376 | 1377 | Lê os dados do slave. 1378 | 1379 | * `HAL_I2C_Mem_Write` 1380 | 1381 | Envia dados do master para um registrador (sub-address) específico de um 1382 | dispositivo 1383 | 1384 | * `HAL_I2C_Mem_Read` 1385 | 1386 | Lê dados de um registrador (sub-address) específico de um dispositivo. 1387 | 1388 | Os principais parâmetros são: 1389 | 1390 | * `*hi2c`: Ponteiro para o módulo I2C escolhido. No caso escolhido nos passos 1391 | acima é um `&hi2c1`. 1392 | * `DevAddress`: endereço do slave que você quer se comunicar(disponível em 1393 | datasheet do componente). 1394 | * `*pData`: ponteiro(vetor) de dados que será enviado ou recebido. 1395 | * `Size`: tamanho dos dados em bytes. 1396 | * `Timeout`: É um tempo limite que o master tentará comunicação com o slave. Se 1397 | passar do timeout e não houver resposta do slave, o master continua a rotina 1398 | principal. Isto evita que a linha I2C fique travada sem resposta. 1399 | * `MemAddress`: Número do sub-endereço do dispositivo para onde se deseja enviar 1400 | ou receber dados. 1401 | * `MemAddSize`: Tamanho do sub-endereço do dispositivo para onde se deseja 1402 | enviar ou receber dados. 1403 | 1404 | Um exemplo de aplicação do protocolo I²C na equipe é o sensor de distância 1405 | VL53L0X, utilizado nos sumôs e uMouse. O código a seguir está na biblioteca 1406 | vl53l0x no repositório do GitHub: 1407 | 1408 | ```c 1409 | static uint8_t address = ADDRESS_DEFAULT; 1410 | #define TIMEOUT 1000 1411 | 1412 | void writeReg(uint8_t reg, uint8_t val) { 1413 | uint8_t bytes[2] = { 1414 | reg, 1415 | val 1416 | }; 1417 | 1418 | while(HAL_I2C_Master_Transmit(&hi2c1, address, (uint8_t*)(&bytes), 2, TIMEOUT) != HAL_OK) 1419 | } 1420 | ``` 1421 | 1422 | Na função acima, o microcontrolador (master) faz comunicação pelo canal I2C1 com 1423 | o sensor de distância (slave) que possui endereço padrão `ADDRESS_DEFAULT` e 1424 | transmite dados de 2 bytes que são passados como parâmetro pelo usuário com um 1425 | Timeout de 1000. Observe que a função `HAL_I2C_Master_Transmit` retorna uma flag 1426 | `HAL_OK` para indicar se tudo ocorreu bem com a transmissão, se houver algum 1427 | erro, o código fica dentro do while. Há a possibilidade de fazer uma função de 1428 | tratamento de erros utilizando esta flag de forma a melhorar o funcionamento do 1429 | sensor. 1430 | 1431 | ```c 1432 | static uint8_t address = ADDRESS_DEFAULT; 1433 | #define TIMEOUT 1000 1434 | 1435 | uint8_t readReg(uint8_t reg) { 1436 | uint8_t val; 1437 | 1438 | if ((status = HAL_I2C_Mem_Read(&hi2c1, address, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, TIMEOUT)) != HAL_OK) { 1439 | return 0; 1440 | } 1441 | 1442 | return val; 1443 | } 1444 | ``` 1445 | 1446 | Nesta outra função, lemos o valor pelo canal I2C1 de um registrador de 8 bits 1447 | reg do sensor com endereço `ADDRESS_DEFAULT` e colocamos na variável val 1448 | utilizando um Timeout 1000. Observe que utilizamos a flag para a função retornar 1449 | 0 caso dê algo de errado na comunicação com o master. Esta função é utilizada 1450 | para retornar o valor lido pelo sensor. 1451 | 1452 | # STM Studio 1453 | 1454 | STM Studio é o programa de depuração da ST. Com ele é possível ler e escrever em variáveis do programa em tempo real, ou seja, enquanto está rodando na placa. 1455 | 1456 | Para sua utilização, os passos que você deve fazer são os seguintes: 1457 | 1458 | ## Leitura de variáveis 1459 | 1460 | Ao abrir pela primeira vez, verá uma tela como essa: 1461 | 1462 | ![STM Studio 1](media/stmstudio_1.png) 1463 | 1464 | Clique com o botão direito do Mouse sobre Display Variables Settings e clique em Import para importar as variáveis de interesse de um programa para o STM Studio. 1465 | 1466 | ![STM Studio 2](media/stmstudio_2.png) 1467 | 1468 | Em Executable Variables clique nas reticências e localize o arquivo que contém as variáveis. 1469 | 1470 | Tal arquivo é gerado na compilação de seu programa e pode assumir três formatos .elf ou .out ou .axf dependendo de onde compilou seu programa. 1471 | 1472 | Se foi compilado das maneiras descritas por esse documento, o arquivo terá extensão .elf e estará localizado na pasta build do seu projeto. 1473 | 1474 | ![STM Studio 3](media/stmstudio_3.png) 1475 | 1476 | Nessa tela, é possível ver todas as variáveis globais do seu programa e escolher as de interesse. 1477 | 1478 | Se selecionar a opção Expand table elements é possível escolher um item de determinado vetor, que não seria possível sem essa opção. Assim, no exemplo a seguir, pegarei as variáveis responsáveis por armazenar o valor de três sensores, em que, cada um é um item de um vetor. 1479 | 1480 | Após selecionar todas as variáveis que deseja, clique em Import e deverá ver que elas apareceram em Display Variables Settings. 1481 | 1482 | ![STM Studio 4](media/stmstudio_4.png) 1483 | 1484 | Com isso feito clique nas variáveis que deseja observar, clique com o botão direito, e no menu que aparecer, clique em Send to >> Var View 1. 1485 | 1486 | Com isso, sua variável poderá ser visualizada em tempo real em três modos distintos: em tabela, graficamente e em um gráfico de barras. 1487 | 1488 | O modo de visualização, pode ser selecionado no quadro no inferior esquerdo da tela em Viewers settings >> Display >> VarViewer1 as. 1489 | 1490 | ![STM Studio 5](media/stmstudio_5.png) 1491 | 1492 | Feito isso já será possível visualizar suas variáveis em tempo real. 1493 | 1494 | Para isso, basta ligar a placa no computador com o gravador e selecionar play. 1495 | 1496 | Certifique-se que a opção ST-link SWD esteja selecionada, caso esteja usando os gravadores ST-LINK que temos na gaiola. 1497 | 1498 | ![STM Studio 6](media/stmstudio_6.png) 1499 | 1500 | Abaixo, imagens de exemplo da visualização de variáveis na vista barras e de gráfico: 1501 | 1502 | ![STM Studio 7](media/stmstudio_7.png) 1503 | 1504 | ![STM Studio 8](media/stmstudio_8.png) 1505 | 1506 | Cada uma das vistas pode ser mais ou menos útil em determinada situação. 1507 | 1508 | Nessas vistas, atalhos que podem ser úteis são os de zoom. Use a roda do mouse para aumentar ou diminuir o zoom. 1509 | 1510 | Além disso é possível dar um auto zoom nos valores das suas variáveis clicando com o botão direito do mouse no gráfico e depois nessa opção. 1511 | 1512 | ## Escrita de variáveis 1513 | 1514 | Para se inscrever em determinada variável do programa, os passos são bem semelhantes aos de escrita. Para isso, basta ir na aba de Write variables, importar a variável como descrito anteriormente. Após isso, pode-se escrever o valor desejado, como se mostra abaixo no exemplo. 1515 | 1516 | ![STM Studio 1](media/stmstudio_9.png) 1517 | 1518 | ## Extras 1519 | 1520 | Até aqui já é possível fazer quase todos os casos de interesse. Aqui apenas algumas opções a mais e alguns possíveis problemas. 1521 | 1522 | * Para mexer em configurações mais avançadas como taxa de aquisição, pode-se selecionar o menu de Acquisition Settings (ícone do lado de play na versão atual) e verá algumas opções que pode mexer se desejar alguma taxa de aquisição específica: 1523 | 1524 | ![STM Studio 1](media/stmstudio_10.png) 1525 | 1526 | * Note que na imagem acima tem a opção de escolher a localização de uma Log File. Tal arquivo salva os dados da última vez que usou o STM Studio e pode ser útil. Para abrir o Log basta ir na pasta especificada. 1527 | 1528 | * As vezes, quando mexe-se em uma variável no programa, compila-se novamente e vai usar no STM Studio quando essa variável já estava aberta, é necessário dar um update na variável. Isso por que as vezes a variável fica com o endereço antigo e não pega as mudanças feitas. Para isso clicar com o botão direito e depois em update. 1529 | 1530 | ![STM Studio 1](media/stmstudio_11.png) 1531 | 1532 | * Lembre que para gravar um novo programa em sua placa é necessário parar a depuração do STM Studio. O botão de Play vira um botão de Stop enquanto o programa está rodando. 1533 | 1534 | * É possível visualizar suas variáveis de um último modo que é a visualização em um plano 2D. Para isso, no Menu Views selecione Point Viewer. No canto inferior esquerdo em Point Viewer coloque as variáveis que deseja que apareça nos eixos X e Y. 1535 | 1536 | ![STM Studio 1](media/stmstudio_12.png) 1537 | 1538 | * É possível criar expressões com suas variáveis e visualizar o valor de tais expressões. Para isso, clique com o botão direito do mouse em new como na imagem a seguir. 1539 | 1540 | ![STM Studio 1](media/stmstudio_13.png) 1541 | 1542 | * Com isso, crie sua função na página que se abrir. No exemplo colocarei, ranges[0] + ranges[1]: 1543 | 1544 | ![STM Studio 1](media/stmstudio_14.png) 1545 | 1546 | Basta digitar a expressão. Não é para clicar nos símbolos. Eles servem apenas de referência do que é possível fazer. 1547 | 1548 | # CMake e Makefile 1549 | 1550 | Compilar apenas um arquivo em c++ é bastante simples, geralmente basta um comando no terminal. Mas para projetos maiores, como os desenvolvidos na equipe, nos quais o código está distribuído em vários arquivos, e principalmente várias pastas, fica mais complicado. Para isso existem ferramentas como o Make. 1551 | ## Make/Makefile 1552 | O Make consegue compilar um arquivo principal (que tem a função main) e suas dependências com um só comando, e também gravar ele no microcontrolador. Para isso, ele precisa de um arquivo de texto Makefile, que contém instruções para o Make conseguir achar os arquivos e as dependências deles. Apesar de ser muito prático utilizar essa ferramenta, criar e manter o Makefile é trabalhoso e complicado, então para isso utilizamos também o CMake. 1553 | 1554 | ## CMake 1555 | Com o CMake conseguimos criar um Makefile a partir de outro arquivo de instruções (CMakeLists.txt). Embora pareça redundante criar instruções para criar outro arquivos de instruções, o CMakeLists é muito mais simples de ser criado e alterado. 1556 | 1557 | ## Instalando CMake no Linux 1558 | Para instalar, basta rodar no terminal o comando: 1559 | ```bash 1560 | sudo apt install cmake 1561 | ``` 1562 | 1563 | > [!IMPORTANT] 1564 | > Os tópicos seguintes são voltados para projetos que utilizam o nosso [template](https://github.com/ThundeRatz/STM32ProjectTemplate) 1565 | 1566 | ## Configuração inicial do CMake 1567 | 1568 | Ao iniciar um projeto com o template, algumas configurações devem ser feitas no CmakeLists principal do repositório, indicado na imagem: 1569 | 1570 | ![CMake Main File](media/cmake_main_file.png) 1571 | 1572 | Abrindo esse arquivo você encontrará algo como isso: 1573 | 1574 | ![CMake Variables](media/cmake_project_variables.png) 1575 | 1576 | Nos dois locais indicados você deve inserir o nome do seu projeto e a versão da eletrônica dele. Isso é importante porque o CMake vai procurar na pasta cube um arquivo ioc (aquele feito no CubeMX) chamado "nome"_"versão", para gerar os arquivos necessários. No exemplo da imagem, seria example_v0.ioc 1577 | 1578 | Note que se for reprojetar a placa do seu projeto, você terá um novo cube(ex: example_v1.ioc), e então terá que voltar ao CmakeLists e trocar o BOARD_VERSION para v1. 1579 | ## Compilando e executando o projeto 1580 | O principal comando do CMake é: 1581 | ```bash 1582 | cmake . 1583 | ``` 1584 | Esse comando prepara tudo que é necessário para compilar o código em seguida. Porém com isso ele cria muitos arquivos, e isso pode poluir seu diretório. 1585 | 1586 | Para evitar isso, nós usamos uma pasta chamada `build` onde o comando deve ser executado e que deve ser criada manualmente: 1587 | ```bash 1588 | mkdir build #cria a pasta `build` 1589 | cd build #entra na pasta `build` 1590 | cmake .. 1591 | ``` 1592 | 1593 | > Esta versão do comando (`cmake ..`) utiliza o CMakeLists da pasta anterior (principal do projeto), mas cria os arquivos na pasta build. 1594 | 1595 | > Note que só é necessário criar a pasta `build` se ela ainda não tiver sido criada. 1596 | 1597 | Depois disso podemos usar o Make, que tem vários comandos, listados [aqui](https://github.com/ThundeRatz/STM32ProjectTemplate/blob/develop/cmake/templates/helpme.in), sendo os principais: 1598 | 1599 | `make cube` : gera os arquivos do cube (igual o Generate Code do CubeMX) 1600 | 1601 | `make` : compila o arquivo main.cpp (da pasta src) 1602 | 1603 | `make flash` : compila e grava(passa as intruções para o microcontrolador) o arquivo main.cpp (que deve estar na pasta src) 1604 | 1605 | `make TEST_NAME` : compila o arquivo de teste com o nome indicado (que deve estar em uma subpasta de test/src. Por exemplo, test/src/proxy/TEST_NAME.cpp) 1606 | 1607 | `make flash_TEST_NAME` : compila e grava o arquivo de teste com o nome indicado (que deve estar em uma subpasta de test/src. Por exemplo, test/src/proxy/TEST_NAME.cpp) 1608 | 1609 | # Apêndices 1610 | 1611 | ## Colocando caminhos no PATH 1612 | 1613 | PATH é uma variável protegida do sistema (Windows, Linux e Mac) e contém uma 1614 | lista de diretórios. Quando um comando é executado, o sistema procura os arquivos 1615 | necessários nos diretórios listadas no PATH do usuário e do sistema. 1616 | 1617 | >[!NOTE] 1618 | >Para configurar o PATH no WSL de programas que estão instalados no Windows, 1619 | >é necessário acrescentar `/mnt/c/` no começo do caminho do diretório para 1620 | >identificar que o arquivo está instalado no Windows e não no ambiente Linux 1621 | >do WSL. 1622 | 1623 | Para alterar o PATH, o modo pode ser diferente dependendo da shell 1624 | utilizada no terminal. Aqui, mostraremos como é feito utilizando bash 1625 | e fish. O procedimento é similar em outras shells. 1626 | 1627 | > [!NOTE] 1628 | > Por padrão, no Ubuntu, a shell configurada é o bash, então se você não fez 1629 | nenhuma alteração, siga o tutorial do bash. 1630 | 1631 | Para manter a variável PATH com o mesmo valor em futuras sessões do terminal, 1632 | precisamos editar o arquivo de configuração da shell. 1633 | 1634 | - **Bash**: 1635 | 1636 | ```bash 1637 | code ~/.bashrc 1638 | ``` 1639 | 1640 | Adicione a seguinte linha no final do arquivo: 1641 | 1642 | ```bash 1643 | export PATH=$PATH:"/caminho/do/diretorio" 1644 | ``` 1645 | 1646 | Salve as alterações. 1647 | 1648 | É possível adicionar vários diretórios com um comando. Os diretórios na variável 1649 | PATH são separados por “:”. 1650 | 1651 | ```bash 1652 | export PATH=$PATH:"/caminho/do/diretorio/um:/caminho/do/diretorio/dois" 1653 | ``` 1654 | 1655 | Nos comandos acima, estamos adicionando os diretórios no fim da variável, então, eles 1656 | terão menor prioridade sobre outros diretórios se possuírem um executável com o mesmo 1657 | nome. Para adicionar no início, o comando seria: 1658 | 1659 | ```bash 1660 | export PATH="/diretorio/um:/diretorio/dois:$PATH" 1661 | ``` 1662 | 1663 | Mas recomenda-se adicionar no fim. 1664 | 1665 | Para aplicar as alterações feche e abra o terminal ou digite: 1666 | 1667 | ```bash 1668 | source ~/.bashrc 1669 | ``` 1670 | 1671 | Exemplo de configuração do **PATH do CubeProgrammer**: 1672 | 1673 | ```bash 1674 | export PATH=$PATH:"/home/eu/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin" 1675 | ``` 1676 | 1677 | - **Fish**: 1678 | 1679 | ```bash 1680 | nano ~/.config/fish/config.fish 1681 | ``` 1682 | 1683 | Adicione a seguinte linha: 1684 | 1685 | ```bash 1686 | set -gx PATH $PATH /diretorio/um /diretorio/dois 1687 | ``` 1688 | 1689 | No comando acima, estamos adicionando os diretórios no fim da variável, então, 1690 | eles terão menor prioridade sobre outros diretórios se possuírem um executável 1691 | com o mesmo nome. Para adicionar no início, o comando seria: 1692 | 1693 | ```bash 1694 | set -gx PATH /diretorio/um /diretorio/dois $PATH 1695 | ``` 1696 | 1697 | Mas recomenda-se adicionar no fim. 1698 | 1699 | Salve o arquivo (Ctrl + X e depois Y). 1700 | 1701 | Para aplicar as alterações feche e abra o terminal ou digite: 1702 | 1703 | ```bash 1704 | source ~/.config/fish/config.fish 1705 | ``` 1706 | 1707 | ## Criando variáveis no PATH 1708 | 1709 | Além de colocar caminhos para diretórios no PATH, é possível dar nome a 1710 | eles, possibilitando fazer chamadas do diretório desejado usando somente o nome da 1711 | variável que você definiu para o caminho até esse diretório. 1712 | 1713 | >[!NOTE] 1714 | >Para criar variáveis no PATH do WSL de programas que estão instalados no Windows, 1715 | >é necessário acrescentar `/mnt/c/` no começo do caminho do diretório para 1716 | >identificar que o arquivo está instalado no Windows e não no ambiente Linux 1717 | >do WSL. 1718 | 1719 | O procedimento é parecido com colocar apenas caminhos no PATH. 1720 | 1721 | - **Bash**: 1722 | 1723 | Abra o .bashrc 1724 | 1725 | ```bash 1726 | code ~/.bashrc 1727 | ``` 1728 | 1729 | Adicione a seguinte linha no final: 1730 | 1731 | ```bash 1732 | export NOME_DA_VARIAVEL="/caminho/do/diretorio" 1733 | ``` 1734 | 1735 | Salve o arquivo. 1736 | 1737 | Para aplicar as alterações feche e abra o terminal ou digite: 1738 | 1739 | ```bash 1740 | source ~/.bashrc 1741 | ``` 1742 | 1743 | Exemplo de declaração do **CUBE_PATH**: 1744 | 1745 | ```bash 1746 | export CUBE_PATH="/home/eu/STM32CubeMX" 1747 | ``` 1748 | 1749 | - **Fish**: 1750 | 1751 | ```bash 1752 | nano ~/.config/fish/config.fish 1753 | ``` 1754 | 1755 | Crie a variável: 1756 | 1757 | ```bash 1758 | set -gx NOME_DA_VARIAVEL "caminho/do/diretorio" 1759 | ``` 1760 | 1761 | Coloque no PATH: 1762 | 1763 | ```bash 1764 | set -gx PATH $PATH $NOME_DA_VARIAVEL 1765 | ``` 1766 | 1767 | Salve o arquivo (Ctrl + X e depois Y). 1768 | 1769 | Para aplicar as alterações feche e abra o terminal ou digite: 1770 | 1771 | ```bash 1772 | source ~/.config/fish/config.fish 1773 | ``` 1774 | 1775 | ## Habilitando a virtualização na BIOS 1776 | Para que seja possível a instalação do WSL, primeiro é necessário que a virtualização da BIOS/UEFI esteja habilitada. A virtualização permite que o seu dispositivo Windows emule um sistema operativo diferente, como Android ou Linux. 1777 | Para saber este status na sua máquina, abra o gerenciador de tarefas, vá para desempenho e olhe naquelas informações ali em baixo: 1778 | 1779 | ![virtualização ON](https://github.com/user-attachments/assets/4109eded-9a59-48f6-bf46-1b09f19a6374) 1780 | 1781 | Se já está habilitado, prossiga para o passo 1 da [instalação do WSL](#windows-subsystem-for-linux). 1782 | Caso contrário, acesse este [guia](https://support.microsoft.com/pt-br/windows/ativar-a-virtualiza%C3%A7%C3%A3o-no-windows-c5578302-6e43-4b4b-a449-8ced115f58e1) da Microsoft pelo seu celular e siga o passo a passo no seu computador. 1783 | 1784 | >[!Warning] 1785 | >Será necessário fazer alterações na sua UEFI/BIOS. Caso não esteja acostumado com isso, peça ajuda aos veteranos. 1786 | 1787 | Depois de seguir o guia da Microsoft, verifique novamente se a virtualização está habilitado como mostra a foto anterior. Se deu tudo certo, prossiga para a [instalação do WSL](#windows-subsystem-for-linux). 1788 | 1789 | >Voltar para o [índice](#Índice) 1790 | --------------------------------------------------------------------------------