{"id":15145314,"url":"https://github.com/mytechnotalent/stm32f4_lora_uart_driver","last_synced_at":"2026-03-11T11:37:54.815Z","repository":{"id":230461832,"uuid":"779444821","full_name":"mytechnotalent/STM32F4_LoRa_UART_Driver","owner":"mytechnotalent","description":"An STM32F4, RYLR998 LoRa UART driver written entirely in Assembler.","archived":false,"fork":false,"pushed_at":"2024-03-31T15:55:06.000Z","size":16471,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-13T01:22:47.197Z","etag":null,"topics":["arm","armv7","assembly","assembly-language","assembly-language-programming","driver","embedded","embedded-devices","embedded-systems","iot","lora","mcu","radio","radio-hacking","reverse-engineering","rf","rylr998","uart"],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mytechnotalent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-29T21:22:16.000Z","updated_at":"2024-04-01T06:50:32.000Z","dependencies_parsed_at":"2024-12-20T06:36:46.876Z","dependency_job_id":null,"html_url":"https://github.com/mytechnotalent/STM32F4_LoRa_UART_Driver","commit_stats":{"total_commits":20,"total_committers":1,"mean_commits":20.0,"dds":0.0,"last_synced_commit":"54e1f687ce43fbcc136e39f6dcda2d4ee17def27"},"previous_names":["mytechnotalent/stm32f4_lora_uart_driver"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_LoRa_UART_Driver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_LoRa_UART_Driver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_LoRa_UART_Driver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_LoRa_UART_Driver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mytechnotalent","download_url":"https://codeload.github.com/mytechnotalent/STM32F4_LoRa_UART_Driver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535844,"owners_count":20954633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arm","armv7","assembly","assembly-language","assembly-language-programming","driver","embedded","embedded-devices","embedded-systems","iot","lora","mcu","radio","radio-hacking","reverse-engineering","rf","rylr998","uart"],"created_at":"2024-09-26T11:23:11.090Z","updated_at":"2026-03-11T11:37:54.807Z","avatar_url":"https://github.com/mytechnotalent.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://github.com/mytechnotalent/STM32F4_LoRa_UART_Driver/blob/main/STM32F4_LoRa_UART_Driver.png?raw=true\"\u003e\n\n## FREE Reverse Engineering Self-Study Course [HERE](https://github.com/mytechnotalent/Reverse-Engineering-Tutorial)\n### VIDEO PROMO [HERE](https://www.youtube.com/watch?v=aD7X9sXirF8)\n\n\u003cbr\u003e\n\n# STM32F4 LoRa UART Driver\nAn STM32F4, RYLR998 LoRa UART driver written entirely in Assembler.\n\n\u003cbr\u003e\n\n# Code\n```assembler\n/**\n * FILE: main.s\n *\n * DESCRIPTION:\n * This file contains the assembly code for a STM32F401 LoRa UART driver utilizing the STM32F401CC6 \n * microcontroller.\n *\n * AUTHOR: Kevin Thomas\n * CREATION DATE: March 15, 2024\n * UPDATE DATE: March 31, 2024\n *\n * ASSEMBLE AND LINK w/ SYMBOLS:\n * 1. arm-none-eabi-as -g main.s -o main.o\n * 2. arm-none-eabi-ld main.o -o main.elf -T STM32F401CCUX_FLASH.ld\n * 3. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c \"program main.elf verify reset exit\"\n * ASSEMBLE AND LINK w/o SYMBOLS:\n * 1. arm-none-eabi-as -g main.s -o main.o\n * 2. arm-none-eabi-ld main.o -o main.elf -T STM32F401CCUX_FLASH.ld\n * 3. arm-none-eabi-objcopy -O binary --strip-all main.elf main.bin\n * 3. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c \"program main.bin 0x08000000 verify reset exit\"\n * DEBUG w/ SYMBOLS:\n * 1. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg\n * 2. arm-none-eabi-gdb main.elf\n * 3. target remote :3333\n * 4. monitor reset halt\n * 5. l\n * DEBUG w/o SYMBOLS:\n * 1. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg\n * 2. arm-none-eabi-gdb main.bin\n * 3. target remote :3333\n * 4. monitor reset halt\n * 5. x/8i $pc\n */\n\n\n.syntax unified\n.cpu cortex-m4\n.fpu softvfp\n.thumb\n\n\n/**\n * The start address for the .data section defined in linker script.\n */\n.word _sdata\n\n/**\n * The end address for the .data section defined in linker script.\n */\n.word _edata\n\n/**\n * The start address for the initialization values of the .data section defined in linker script.\n */\n.word _sidata\n\n/**\n * The start address for the .bss section defined in linker script.\n */\n.word _sbss\n\n/**\n * The end address for the .bss section defined in linker script.\n */\n.word _ebss\n\n\n/**\n * Provide weak aliases for each Exception handler to the Default_Handler. As they are weak aliases, any function\n * with the same name will override this definition.\n */\n.macro weak name\n  .global \\name\n  .weak \\name\n  .thumb_set \\name, Default_Handler\n  .word \\name\n.endm\n\n\n/**\n * Initialize the .isr_vector section. The .isr_vector section contains vector table.\n */\n.section .isr_vector, \"a\"\n\n/**\n * The STM32F401CCUx vector table. Note that the proper constructs must be placed on this to ensure that it ends up\n * at physical address 0x00000000.\n */\n.global isr_vector\n.type isr_vector, %object\nisr_vector:\n  .word _estack\n  .word Reset_Handler\n   weak NMI_Handler\n   weak HardFault_Handler\n   weak MemManage_Handler\n   weak BusFault_Handler\n   weak UsageFault_Handler\n  .word 0\n  .word 0\n  .word 0\n  .word 0\n   weak SVC_Handler\n   weak DebugMon_Handler\n  .word 0\n   weak PendSV_Handler\n   weak SysTick_Handler\n  .word 0\n   weak EXTI16_PVD_IRQHandler                              // EXTI Line 16 interrupt PVD through EXTI line detection \n   weak TAMP_STAMP_IRQHandler                              // Tamper and TimeStamp interrupts through the EXTI line\n   weak EXTI22_RTC_WKUP_IRQHandler                         // EXTI Line 22 interrupt RTC Wakeup interrupt, EXTI line\n   weak FLASH_IRQHandler                                   // FLASH global interrupt\n   weak RCC_IRQHandler                                     // RCC global interrupt\n   weak EXTI0_IRQHandler                                   // EXTI Line0 interrupt\n   weak EXTI1_IRQHandler                                   // EXTI Line1 interrupt\n   weak EXTI2_IRQHandler                                   // EXTI Line2 interrupt\n   weak EXTI3_IRQHandler                                   // EXTI Line3 interrupt\n   weak EXTI4_IRQHandler                                   // EXTI Line4 interrupt\n   weak DMA1_Stream0_IRQHandler                            // DMA1 Stream0 global interrupt\n   weak DMA1_Stream1_IRQHandler                            // DMA1 Stream1 global interrupt\n   weak DMA1_Stream2_IRQHandler                            // DMA1 Stream2 global interrupt\n   weak DMA1_Stream3_IRQHandler                            // DMA1 Stream3 global interrupt\n   weak DMA1_Stream4_IRQHandler                            // DMA1 Stream4 global interrupt\n   weak DMA1_Stream5_IRQHandler                            // DMA1 Stream5 global interrupt\n   weak DMA1_Stream6_IRQHandler                            // DMA1 Stream6 global interrupt\n   weak ADC_IRQHandler                                     // ADC1 global interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak EXTI9_5_IRQHandler                                 // EXTI Line[9:5] interrupts\n   weak TIM1_BRK_TIM9_IRQHandle                            // TIM1 Break interrupt and TIM9 global interrupt\n   weak TIM1_UP_TIM10_IRQHandler                           // TIM1 Update interrupt and TIM10 global interrupt\n   weak TIM1_TRG_COM_TIM11_IRQHandler                      // TIM1 T/C interrupts, TIM11 global interrupt\n   weak TIM1_CC_IRQHandler                                 // TIM1 Capture Compare interrupt\n   weak TIM2_IRQHandler                                    // TIM2 global interrupt\n   weak TIM3_IRQHandler                                    // TIM3 global interrupt\n   weak TIM4_IRQHandler                                    // TIM4 global interrupt\n   weak I2C1_EV_IRQHandler                                 // I2C1 event interrupt\n   weak I2C1_ER_IRQHandler                                 // I2C1 error interrupt\n   weak I2C2_EV_IRQHandler                                 // I2C2 event interrupt\n   weak I2C2_ER_IRQHandler                                 // I2C2 error interrupt\n   weak SPI1_IRQHandler                                    // SPI1 global interrupt\n   weak SPI2_IRQHandler                                    // SPI2 global interrupt\n   weak USART1_IRQHandler                                  // USART1 global interrupt\n   weak USART2_IRQHandler                                  // USART2 global interrupt\n  .word 0                                                  // reserved\n   weak EXTI15_10_IRQHandler                               // EXTI Line[15:10] interrupts\n   weak EXTI17_RTC_Alarm_IRQHandler                        // EXTI Line 17 interrupt / RTC Alarms (A and B) EXTI\n   weak EXTI18_OTG_FS_WKUP_IRQHandler                      // EXTI Line 18 interrupt / USBUSB OTG FS Wakeup EXTI\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak DMA1_Stream7_IRQHandler                            // DMA1 Stream7 global interrupt\n  .word 0                                                  // reserved\n   weak SDIO_IRQHandler                                    // SDIO global interrupt\n   weak TIM5_IRQHandler                                    // TIM5 global interrupt\n   weak SPI3_IRQHandler                                    // SPI3 global interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak DMA2_Stream0_IRQHandler                            // DMA2 Stream0 global interrupt\n   weak DMA2_Stream1_IRQHandler                            // DMA2 Stream1 global interrupt\n   weak DMA2_Stream2_IRQHandler                            // DMA2 Stream2 global interrupt\n   weak DMA2_Stream3_IRQHandler                            // DMA2 Stream3 global interrupt\n   weak DMA2_Stream4_IRQHandler                            // DMA2 Stream4 global interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak OTG_FS_IRQHandler                                  // USB On The Go FS global interrupt\n   weak DMA2_Stream5_IRQHandler                            // DMA2 Stream5 global interrupt\n   weak DMA2_Stream6_IRQHandler                            // DMA2 Stream6 global interrupt\n   weak DMA2_Stream7_IRQHandler                            // DMA2 Stream7 global interrupt\n   weak USART6_IRQHandler                                  // USART6 global interrupt\n   weak I2C3_EV_IRQHandler                                 // I2C3 event interrupt\n   weak I2C3_ER_IRQHandler                                 // I2C3 error interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak SPI4_IRQHandler                                    // SPI4 global interrupt\n\n/**\n * @brief  This code is called when processor starts execution.\n *\n *         This is the code that gets called when the processor first starts execution following a reset event. We \n *         first define and init the bss section and then define and init the data section, after which the \n *         application supplied main routine is called.\n *\n * @param  None\n * @retval None\n */\n.type Reset_Handler, %function\n.global Reset_Handler\nReset_Handler:\n  LDR   R4, =_estack                                       // load address at end of the stack into R0\n  MOV   SP, R4                                             // move address at end of stack into SP\n  LDR   R4, =_sdata                                        // copy the data segment initializers from flash to SRAM\n  LDR   R5, =_edata                                        // copy the data segment initializers from flash to SRAM\n  LDR   R6, =_sidata                                       // copy the data segment initializers from flash to SRAM\n  MOVS  R7, #0                                             // copy the data segment initializers from flash to SRAM\n  B     .Reset_Handler_Loop_Copy_Data_Init                 // branch\n.Reset_Handler_Copy_Data_Init:\n  LDR   R8, [R6, R7]                                       // copy the data segment initializers into registers\n  STR   R8, [R4, R7]                                       // copy the data segment initializers into registers\n  ADDS  R7, R7, #4                                         // copy the data segment initializers into registers\n.Reset_Handler_Loop_Copy_Data_Init:\n  ADDS  R8, R4, R7                                         // initialize the data segment\n  CMP   R8, R5                                             // initialize the data segment\n  BCC   .Reset_Handler_Copy_Data_Init                      // branch if carry is clear\n  LDR   R6, =_sbss                                         // copy the bss segment initializers from flash to SRAM\n  LDR   R8, =_ebss                                         // copy the bss segment initializers from flash to SRAM\n  MOVS  R7, #0                                             // copy the bss segment initializers from flash to SRAM\n  B     .Reset_Handler_Loop_Fill_Zero_BSS                  // branch\n.Reset_Handler_Fill_Zero_BSS:\n  STR   R7, [R6]                                           // zero fill the bss segment\n  ADDS  R6, R6, #4                                         // zero fill the bss segment\n.Reset_Handler_Loop_Fill_Zero_BSS:\n  CMP   R6, R8                                             // zero fill the bss segment\n  BCC   .Reset_Handler_Fill_Zero_BSS                       // branch if carry is clear\n  BL    main                                               // call function\n\n/**\n * @brief  This code is called when the processor receives and unexpected interrupt.\n *\n *         This is the code that gets called when the processor receives an unexpected interrupt. This simply enters \n *         an infinite loop, preserving the system state for examination by a debugger.\n *\n * @param  None\n * @retval None\n */\n.type Default_Handler, %function\n.global Default_Handler\nDefault_Handler:\n  BKPT                                                     // set processor into debug state\n  B.N   Default_Handler                                    // call function, force thumb state\n\n/**\n * Initialize the .text section. The .text section contains executable code.\n */\n.section .text\n\n/**\n * @brief  Entry point for initialization and setup of specific functions and main routine.\n *\n *         This function is the entry point for initializing and setting up specific functions. It calls other \n *         functions to enable certain features and then enters a loop for further execution.\n *\n * @param  None\n * @retval None\n */\n.type main, %function\n.global main\nmain:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  BL    GPIOA_Enable                                       // call function\n  BL    GPIOB_Enable                                       // call function\n  BL    GPIOC_Enable                                       // call function\n  BL    I2C1_Enable                                        // call function\n  BL    GPIOA_PA2_Alt_Function_Mode_Enable                 // call function\n  BL    GPIOA_PA3_Alt_Function_Mode_Enable                 // call function\n  BL    GPIOB_PB8_Alt_Function_Mode_Enable                 // call function\n  BL    GPIOB_PB8_Open_Drain_Enable                        // call function\n  BL    GPIOB_PB9_Alt_Function_Mode_Enable                 // call function\n  BL    GPIOB_PB9_Open_Drain_Enable                        // call function\n  BL    GPIOB_PB0_Input_Enable                             // call function\n  BL    GPIOB_PB1_Input_Enable                             // call function\n  BL    GPIOB_PB2_Input_Enable                             // call function\n  BL    GPIOB_PB3_Input_Enable                             // call function\n  BL    GPIOB_PB4_Input_Enable                             // call function\n  BL    GPIOB_PB5_Input_Enable                             // call function\n  BL    I2C1_Init                                          // call function\n  BL    SSD1306_Init                                       // call function\n  BL    SSD1306_Turn_On_Display                            // call function\n  BL    USART2_Enable                                      // call function\n  BL    USART2_Transmit_Enable                             // call function\n  BL    USART2_Receive_Enable                              // call function\n  //BL    LoRa_Setup                                       // call function\n  BL    Loop                                               // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables the GPIOA peripheral by setting the corresponding RCC_AHB1ENR bit.\n *\n * @details This function enables the GPIOA peripheral by setting the corresponding RCC_AHB1ENR bit. It loads the \n *          address of the RCC_AHB1ENR register, retrieves the current value of the register, sets the GPIOAEN bit, \n *          and stores the updated value back into the register.\n *\n * @param   None\n * @retval  None\n */\nGPIOA_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023830                                    // load address of RCC_AHB1ENR register\n  LDR   R5, [R4]                                           // load value inside RCC_AHB1ENR register\n  ORR   R5, #(1\u003c\u003c0)                                        // set the GPIOAEN bit\n  STR   R5, [R4]                                           // store value into RCC_AHB1ENR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables the GPIOB peripheral by setting the corresponding RCC_AHB1ENR bit.\n *\n * @details This function enables the GPIOB peripheral by setting the corresponding RCC_AHB1ENR bit. It loads the \n *          address of the RCC_AHB1ENR register, retrieves the current value of the register, sets the GPIOBEN bit, \n *          and stores the updated value back into the register.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023830                                    // load address of RCC_AHB1ENR register\n  LDR   R5, [R4]                                           // load value inside RCC_AHB1ENR register\n  ORR   R5, #(1\u003c\u003c1)                                        // set the GPIOBEN bit\n  STR   R5, [R4]                                           // store value into RCC_AHB1ENR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables the GPIOC peripheral by setting the corresponding RCC_AHB1ENR bit.\n *\n * @details This function enables the GPIOC peripheral by setting the corresponding RCC_AHB1ENR bit.  It loads the \n *          address of the RCC_AHB1ENR register, retrieves the current value of the register, sets the GPIOCEN bit, \n *          and stores the updated value back into the register.\n *\n * @param   None\n * @retval  None\n */\nGPIOC_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023830                                    // load address of RCC_AHB1ENR register\n  LDR   R5, [R4]                                           // load value inside RCC_AHB1ENR register\n  ORR   R5, #(1\u003c\u003c2)                                        // set the GPIOCEN bit\n  STR   R5, [R4]                                           // store value into RCC_AHB1ENR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOA pin 2 to operate in alternate function mode.\n *\n * @details This function configures GPIOA pin 2 to operate in alternate function mode. It modifies the GPIOA_MODER \n *          and GPIOA_AFRL registers to set the necessary bits for alternate function mode on pin 2. The MODER2 bit \n *          is set to select alternate function mode, and the AFRL2 bits are set to specify the desired alternate \n *          function.\n *\n * @param   None\n * @retval  None\n */\nGPIOA_PA2_Alt_Function_Mode_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020000                                    // load address of GPIOA_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOA_MODER register\n  ORR   R5, #(1\u003c\u003c5)                                        // set the MODER2 bit\n  BIC   R5, #(1\u003c\u003c4)                                        // clear the MODER2 bit\n  STR   R5, [R4]                                           // store value into GPIOA_MODER register\n  LDR   R4, =0x40020020                                    // load address of GPIOA_AFRL register\n  LDR   R5, [R4]                                           // load value inside GPIOA_AFRL register\n  BIC   R5, #(1\u003c\u003c11)                                       // clear the AFRL2 bit\n  ORR   R5, #(1\u003c\u003c10)                                       // set the AFRL2 bit\n  ORR   R5, #(1\u003c\u003c9)                                        // set the AFRL2 bit\n  ORR   R5, #(1\u003c\u003c8)                                        // set the AFRL2 bit\n  STR   R5, [R4]                                           // store value into GPIOA_AFRL register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12 , LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOA pin 3 to operate in alternate function mode.\n *\n * @details This function configures GPIOA pin 3 to operate in alternate function mode. It modifies the GPIOA_MODER \n *          and GPIOA_AFRL registers to set the necessary bits for alternate function mode on pin 3. The MODER3 bit \n *          is set to select alternate function mode, and the AFRL3 bits are set to specify the desired alternate\n *          function.\n *\n * @param   None\n * @retval  None\n */\nGPIOA_PA3_Alt_Function_Mode_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020000                                    // load address of GPIOA_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOA_MODER register\n  ORR   R5, #(1\u003c\u003c7)                                        // set the MODER3 bit\n  BIC   R5, #(1\u003c\u003c6)                                        // clear the MODER3 bit\n  STR   R5, [R4]                                           // store value into GPIOA_MODER register\n  LDR   R4, =0x40020020                                    // load address of GPIOA_AFRL register\n  LDR   R5, [R4]                                           // load value inside GPIOA_AFRL register\n  BIC   R5, #(1\u003c\u003c15)                                       // clear the AFRL3 bit\n  ORR   R5, #(1\u003c\u003c14)                                       // set the AFRL3 bit\n  ORR   R5, #(1\u003c\u003c13)                                       // set the AFRL3 bit\n  ORR   R5, #(1\u003c\u003c12)                                       // set the AFRL3 bit\n  STR   R5, [R4]                                           // store value into GPIOA_AFRL register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables Alternative Function Mode on GPIOB Pin 8.\n *\n * @details This assembly function enables the Alternative Function Mode on GPIOB Pin 8 by configuring the \n *          corresponding bits in the GPIOB_MODER and GPIOB_AFRH registers. It sets the pin to Alternative Function \n *          Mode and configures the specific alternative function for Pin 8.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB8_Alt_Function_Mode_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  ORR   R5, #(1\u003c\u003c17)                                       // set the MODER8 bit\n  BIC   R5, #(1\u003c\u003c16)                                       // clear the MODER8 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x40020424                                    // load address of GPIOB_AFRH register\n  LDR   R5, [R4]                                           // load value inside GPIOB_AFRH register\n  BIC   R5, #(1\u003c\u003c3)                                        // clear the AFRH8 bit\n  ORR   R5, #(1\u003c\u003c2)                                        // set the AFRH8 bit\n  BIC   R5, #(1\u003c\u003c1)                                        // clear the AFRH8 bit\n  BIC   R5, #(1\u003c\u003c0)                                        // clear the AFRH8 bit\n  STR   R5, [R4]                                           // store value into GPIOB_AFRH register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables Open Drain Mode on GPIOB Pin 8.\n *\n * @details This assembly function enables Open Drain Mode on GPIOB Pin 8 by setting the corresponding bit in the \n *          GPIOB_OTYPER register.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB8_Open_Drain_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020404                                    // load address of GPIOB_OTYPER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_OTYPER register\n  ORR   R5, #(1\u003c\u003c8)                                        // set the OT8 bit\n  STR   R5, [R4]                                           // store value into GPIOB_OTYPER register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables Alternative Function Mode on GPIOB Pin 9.\n *\n * @details This assembly function enables the Alternative Function Mode on GPIOB Pin 9 by configuring the \n *          corresponding bits in the GPIOB_MODER and GPIOB_AFRH registers. It sets the pin to Alternative Function \n *          Mode and configures the specific alternative function for Pin 9.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB9_Alt_Function_Mode_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  ORR   R5, #(1\u003c\u003c19)                                       // set the MODER9 bit\n  BIC   R5, #(1\u003c\u003c18)                                       // clear the MODER9 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x40020424                                    // load address of GPIOB_AFRH register\n  LDR   R5, [R4]                                           // load value inside GPIOB_AFRH register\n  BIC   R5, #(1\u003c\u003c7)                                        // clear the AFRH9 bit\n  ORR   R5, #(1\u003c\u003c6)                                        // set the AFRH9 bit\n  BIC   R5, #(1\u003c\u003c5)                                        // clear the AFRH9 bit\n  BIC   R5, #(1\u003c\u003c4)                                        // clear the AFRH9 bit\n  STR   R5, [R4]                                           // store value into GPIOB_AFRH register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables Open Drain Mode on GPIOB Pin 9.\n *\n * @details This assembly function enables Open Drain Mode on GPIOB Pin 9 by setting the corresponding bit in the \n *          GPIOB_OTYPER register.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB9_Open_Drain_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020404                                    // load address of GPIOB_OTYPER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_OTYPER register\n  ORR   R5, #(1\u003c\u003c9)                                        // set the OT9 bit\n  STR   R5, [R4]                                           // store value into GPIOB_OTYPER register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOB pin 0 to operate in input mode.\n *\n * @details This function configures GPIOB pin 0 to operate in input mode. It modifies the GPIOB_MODER register to \n *          set the necessary bits for input mode on pin 0. The MODER0 bit is set to input mode.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB0_Input_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  BIC   R5, #(1\u003c\u003c1)                                        // clear the MODER0 bit\n  BIC   R5, #(1\u003c\u003c0)                                        // clear the MODER0 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x4002040C                                    // load address of GPIOB_PUPDR register\n  LDR   R5, [R4]                                           // load value inside GPIOB_PUPDR register\n  BIC   R5, #(1\u003c\u003c1)                                        // clear the PUPDR0 bit\n  ORR   R5, #(1\u003c\u003c0)                                        // set the PUPDR0 bit\n  STR   R5, [R4]                                           // store value into GPIOB_PUPDR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOB pin 1 to operate in input mode.\n *\n * @details This function configures GPIOB pin 1 to operate in input mode. It modifies the GPIOB_MODER register to \n *          set the necessary bits for input mode on pin 1. The MODER1 bit is set to input mode.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB1_Input_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  BIC   R5, #(1\u003c\u003c3)                                        // clear the MODER1 bit\n  BIC   R5, #(1\u003c\u003c2)                                        // clear the MODER1 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x4002040C                                    // load address of GPIOB_PUPDR register\n  LDR   R5, [R4]                                           // load value inside GPIOB_PUPDR register\n  BIC   R5, #(1\u003c\u003c3)                                        // clear the PUPDR1 bit\n  ORR   R5, #(1\u003c\u003c2)                                        // set the PUPDR1 bit\n  STR   R5, [R4]                                           // store value into GPIOB_PUPDR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOB pin 2 to operate in input mode.\n *\n * @details This function configures GPIOB pin 2 to operate in input mode. It modifies the GPIOB_MODER register to \n *          set the necessary bits for input mode on pin 2. The MODER2 bit is set to input mode.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB2_Input_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  BIC   R5, #(1\u003c\u003c5)                                        // clear the MODER2 bit\n  BIC   R5, #(1\u003c\u003c4)                                        // clear the MODER2 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x4002040C                                    // load address of GPIOB_PUPDR register\n  LDR   R5, [R4]                                           // load value inside GPIOB_PUPDR register\n  BIC   R5, #(1\u003c\u003c5)                                        // clear the PUPDR2 bit\n  ORR   R5, #(1\u003c\u003c4)                                        // set the PUPDR2 bit\n  STR   R5, [R4]                                           // store value into GPIOB_PUPDR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOB pin 3 to operate in input mode.\n *\n * @details This function configures GPIOB pin 3 to operate in input mode. It modifies the GPIOB_MODER register to \n *          set the necessary bits for input mode on pin 3. The MODER3 bit is set to input mode.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB3_Input_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  BIC   R5, #(1\u003c\u003c7)                                        // clear the MODER3 bit\n  BIC   R5, #(1\u003c\u003c6)                                        // clear the MODER3 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x4002040C                                    // load address of GPIOB_PUPDR register\n  LDR   R5, [R4]                                           // load value inside GPIOB_PUPDR register\n  BIC   R5, #(1\u003c\u003c7)                                        // clear the PUPDR3 bit\n  ORR   R5, #(1\u003c\u003c6)                                        // set the PUPDR3 bit\n  STR   R5, [R4]                                           // store value into GPIOB_PUPDR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOB pin 4 to operate in input mode.\n *\n * @details This function configures GPIOB pin 4 to operate in input mode. It modifies the GPIOB_MODER register to \n *          set the necessary bits for input mode on pin 4. The MODER4 bit is set to input mode.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB4_Input_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  BIC   R5, #(1\u003c\u003c9)                                        // clear the MODER4 bit\n  BIC   R5, #(1\u003c\u003c8)                                        // clear the MODER4 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x4002040C                                    // load address of GPIOB_PUPDR register\n  LDR   R5, [R4]                                           // load value inside GPIOB_PUPDR register\n  BIC   R5, #(1\u003c\u003c9)                                        // clear the PUPDR4 bit\n  ORR   R5, #(1\u003c\u003c8)                                        // set the PUPDR4 bit\n  STR   R5, [R4]                                           // store value into GPIOB_PUPDR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Configures GPIOB pin 5 to operate in input mode.\n *\n * @details This function configures GPIOB pin 5 to operate in input mode. It modifies the GPIOB_MODER register to \n *          set the necessary bits for input mode on pin 5. The MODER5 bit is set to input mode.\n *\n * @param   None\n * @retval  None\n */\nGPIOB_PB5_Input_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020400                                    // load address of GPIOB_MODER register\n  LDR   R5, [R4]                                           // load value inside GPIOB_MODER register\n  BIC   R5, #(1\u003c\u003c11)                                       // clear the MODER5 bit\n  BIC   R5, #(1\u003c\u003c10)                                       // clear the MODER5 bit\n  STR   R5, [R4]                                           // store value into GPIOB_MODER register\n  LDR   R4, =0x4002040C                                    // load address of GPIOB_PUPDR register\n  LDR   R5, [R4]                                           // load value inside GPIOB_PUPDR register\n  BIC   R5, #(1\u003c\u003c11)                                       // clear the PUPDR5 bit\n  ORR   R5, #(1\u003c\u003c10)                                       // set the PUPDR5 bit\n  STR   R5, [R4]                                           // store value into GPIOB_PUPDR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables USART2 peripheral and configures its settings for communication using an interrupt.\n *\n * @details This function enables the USART2 peripheral by setting the corresponding RCC_APB1ENR bit.  It also \n *          configures the USART2 settings, including the baud rate and control register settings.  The USART2_BRR \n *          register is set to achieve a baud rate of 9600, and the USART2_CR1 register is modified to enable USART2\n *          (UE bit) and receive interrupt (RXNEIE bit) and enable transmission (TE bit) and enable receive (RE bit).\n *\n * @param   None\n * @retval  None\n */\nUSART2_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023840                                    // load address of RCC_APB1ENR register\n  LDR   R5, [R4]                                           // load value inside RCC_APB1ENR register\n  ORR   R5, #(1\u003c\u003c17)                                       // set the USART2EN bit\n  STR   R5, [R4]                                           // store value into RCC_AHB1ENR register\n  LDR   R4, =0x40004408                                    // load address of USART2_BRR register \n  LDR   R5, [R4]                                           // load value inside USART2_BRR register\n  MOV   R5, #0x683                                         // set register to 9600 baud\n  STR   R5, [R4]                                           // store value into USART2_BRR register\n  LDR   R4, =0x4000440C                                    // load address of USART2_CR1 register\n  LDR   R5, [R4]                                           // load value inside USART2_CR1 register\n  ORR   R5, #(1\u003c\u003c13)                                       // set the UE bit\n  STR   R5, [R4]                                           // store value into USART2_CR1 register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Sends a single character over USART2.\n *\n * @details This function sends a single character over USART2 by writing it to the USART2_DR register.  It first \n *          checks if the transmit buffer is empty (TXE bit) in the USART2_SR register.  If the buffer is not empty, \n *          it waits until it becomes empty before writing the character to USART_DR.\n *\n * @param   R0: The character to be sent over USART2.\n * @retval  None\n */\nUSART2_Transmit_Character:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R4, R0                                             // copy first arg into R4\n  LDR   R5, =0x40004400                                    // load address of USART2_SR register\n.USART2_Transmit_Character_Loop:\n  LDR   R6, [R5]                                           // load value inside USART2_SR register\n  AND   R6, #(1\u003c\u003c7)                                        // read TXE bit\n  CMP   R6, #0x00                                          // test TX FIFO is not full\n  BEQ   .USART2_Transmit_Character_Loop                    // branch if equal\n  LDR   R5, =0x40004404                                    // load value inside USART2_DR register\n  STR   R4, [R5]                                           // store value into USART2_DR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Receives a character over USART2.\n *\n * @details This function receives a character over USART2 by reading the USART2_DR register. The received character\n *          is then returned.\n *\n * @param   None\n * @retval  R0: The received character over USART2.\n */\nUSART2_Receive_Character:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40004400                                    // load address of USART2_SR register\n.USART2_Receive_Character_Loop:\n  BL    GPIOB_PB5_Read_Value                               // call function\n  CMP   R0, #0x20                                          // check if button is pressed\n  BNE   .USART2_Receive_Character_Exit                     // branch if not equal\n  LDR   R5, [R4]                                           // load value inside USART_SR register\n  AND   R5, #(1\u003c\u003c5)                                        // read the RXNE bit, if RXNE is 0, data is not recv\n  CMP   R5, #0                                             // cmp RXNE bit to 0 \n  BEQ   .USART2_Receive_Character_Loop                     // branch if equal\n  LDR   R4, =0x40004404                                    // load value inside USART2_DR register\n  LDRB  R0, [R4]                                           // load byte inside USART2_DR register\n.USART2_Receive_Character_Exit:\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Clear USART2 buffer.\n *\n *         This function clears the receive buffer of USART2 by reading the data register (USART2_DR)\n *         and discarding its content.\n *\n * @param  None\n * @retval None\n */\nUSART2_Clear_Buffer:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40004400                                    // load address of USART2_SR register\n  LDR   R5, [R4]                                           // load value inside USART_SR register\n  LDR   R4, =0x40004404                                    // load value inside USART2_DR register\n  LDR   R5, [R4]                                           // load byte inside USART2_DR register          \n  MOV   R5, #0                                             // move zero into R5\n  STR   R5, [R4]                                           // store value into USART2_DR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables I2C1 Peripheral.\n *\n * @details This assembly function enables the I2C1 peripheral by setting the corresponding bit in the RCC_APB1ENR\n *          register. It loads the address of the RCC_APB1ENR register, retrieves the current value of the register,\n *          sets the I2C1EN bit, and stores the updated value back into the register.\n *\n * @param   None\n * @retval  None\n */\nI2C1_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023840                                    // load address of RCC_APB1ENR register\n  LDR   R5, [R4]                                           // load value inside RCC_APB1ENR register\n  ORR   R5, #(1\u003c\u003c21)                                       // set the I2C1EN bit\n  STR   R5, [R4]                                           // store value into RCC_APB1ENR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Initializes and enables the I2C1 peripheral.\n *\n * @details This assembly function initializes and enables the I2C1 peripheral by configuring the relevant bits in\n *          the I2C1_CR1, I2C1_CR2, I2C1_CCR, and I2C1_TRISE registers. It performs a software reset, sets the \n *          frequency and duty cycle, configures the clock control register, sets the rise time, and finally, enables\n *          the I2C1 peripheral.\n *\n * @param   None\n * @retval  None\n */\nI2C1_Init:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40005400                                    // load address of I2C1_CR1 register\n  LDR   R5, [R4]                                           // load value inside I2C1_CR1 register\n  ORR   R5, #(1\u003c\u003c15)                                       // set the SWRST bit\n  STR   R5, [R4]                                           // store value into I2C1_CR1 register\n  BIC   R5, #(1\u003c\u003c15)                                       // clear the SWRST bit\n  STR   R5, [R4]                                           // store value into I2C1_CR1 register\n  LDR   R4, =0x40005404                                    // load address of I2C1_CR2 register\n  LDR   R5, [R4]                                           // load value inside I2C1_CR2 register\n  ORR   R5, #(1\u003c\u003c5)                                        // set the FREQ bit\n  ORR   R5, #(1\u003c\u003c4)                                        // set the FREQ bit\n  BIC   R5, #(1\u003c\u003c3)                                        // clear the FREQ bit\n  BIC   R5, #(1\u003c\u003c2)                                        // clear the FREQ bit\n  ORR   R5, #(1\u003c\u003c1)                                        // set the FREQ bit\n  BIC   R5, #(1\u003c\u003c0)                                        // clear the FREQ bit\n  STR   R5, [R4]                                           // store value into I2C1_CR2 register\n  LDR   R4, =0x4000541C                                    // load address of I2C1_CCR register\n  LDR   R5, [R4]                                           // load value inside I2C1_CCR register\n  ORR   R5, #(1\u003c\u003c15)                                       // set the F/S bit\n  ORR   R5, #(1\u003c\u003c14)                                       // set the DUTY bit\n  ORR   R5, #(1\u003c\u003c1)                                        // set the CCR bit\n  STR   R5, [R4]                                           // store value into I2C1_CCR register\n  LDR   R4, =0x40005420                                    // load address of I2C1_TRISE register\n  LDR   R5, [R4]                                           // load value inside I2C1_TRISE register\n  BIC   R5, #(1\u003c\u003c5)                                        // clear the TRISE bit\n  ORR   R5, #(1\u003c\u003c4)                                        // set the TRISE bit\n  BIC   R5, #(1\u003c\u003c3)                                        // clear the TRISE bit\n  ORR   R5, #(1\u003c\u003c2)                                        // set the TRISE bit\n  BIC   R5, #(1\u003c\u003c1)                                        // clear the TRISE bit\n  BIC   R5, #(1\u003c\u003c0)                                        // clear the TRISE bit\n  STR   R5, [R4]                                           // store value into I2C1_TRISE register\n  LDR   R4, =0x40005400                                    // load address of I2C1_CR1 register\n  LDR   R5, [R4]                                           // load value inside I2C1_CR1 register\n  ORR   R5, #(1\u003c\u003c0)                                        // set the PE bit\n  STR   R5, [R4]                                           // store value into I2C1_CR1 register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Initializes the SSD1306 OLED Display.\n *\n * @details This assembly function initializes the SSD1306 OLED display by configuring the I2C communication \n *          parameters and sending the necessary commands to set up the display parameters.\n *\n * @param   None\n * @retval  None\n */\nSSD1306_Init:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    Thirty_Microsecond_Delay                           // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x20                                          // set memory addressing mode, page addressing mode\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xB0                                          // set page start address for page addressing mode (0-7 pages)\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xA1                                          // set segment re-map, col addr 127 mapped to SEG0\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xC8                                          // set COM output scan direction, remapped\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x00                                          // set lower col start addr for page addr mode\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x10                                          // set higher col start addr for page addr mode\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xD5                                          // set display clock\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xF0                                          // divide ratio/oscillator freq\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x12                                          // set COM pins hardware config\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode \n  MOV   R2, #0xDB                                          // set VCOMH deselect level\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x20                                          // 0.77 VCC\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x8D                                          // charge pump setting\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0x14                                          // enable charge pump\n  BL    I2C_Write_Byte                                     // call function\n  BL    SSD1306_Clear_Screen                               // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Turns on the SSD1306 OLED Display.\n *\n * @details This assembly function turns on the SSD1306 OLED display by sending the necessary command through I2C \n *          communication to set the display panel to an active state.\n *\n * @param   None\n * @retval  None\n */\nSSD1306_Turn_On_Display:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xAF                                          // set display on\n  BL    I2C_Write_Byte                                     // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Turns off the SSD1306 OLED Display.\n *\n * @details This assembly function turns off the SSD1306 OLED display by sending the necessary command through I2C\n *          communication to set the display panel to an inactive state.\n *\n * @param   None\n * @retval  None\n */\nSSD1306_Turn_Off_Display:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, #0xAE                                          // set display off\n  BL    I2C_Write_Byte                                     // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Sets the cursor position on the SSD1306 OLED Display.\n *\n * @details This assembly function sets the cursor position on the SSD1306 OLED display by sending the necessary \n *          commands through I2C communication. It specifies the lower and higher column start addresses along with\n *          the page start address to define the cursor position.\n *\n * @param   R0: Lower column start address.\n * @param   R1: Higher column start address.\n * @param   R2: Page start address.\n *\n * @retval  None\n */\nSSD1306_Set_Cursor:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R4, R0                                             // copy first arg into R4\n  MOV   R5, R1                                             // copy second arg into R5\n  MOV   R6, R2                                             // copy third arg into R6 \n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, R4                                             // lower col start addr\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // command mode\n  MOV   R2, R5                                             // higher col start addr\n  BL    I2C_Write_Byte                                     // call function\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x00                                          // memory addr\n  MOV   R2, R6                                             // page start addr\n  BL    I2C_Write_Byte                                     // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Clears the screen of the SSD1306 OLED Display.\n *\n * @details This assembly function clears the entire screen of the SSD1306 OLED display by sending the necessary \n *          commands and data through I2C communication. It utilizes the SSD1306_Set_Cursor function to position the \n *          cursor at the beginning of the display and then writes data to fill the screen with zeros.\n *\n * @param   None\n * @retval  None\n */\nSSD1306_Clear_Screen:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x10                                          // higher col start addr\n  MOV   R2, #0xB0                                          // page start addr\n  BL    SSD1306_Set_Cursor                                 // call function\n  MOV   R3, #0x00                                          // set counter\n.SSD1306_Clear_Screen_Loop:\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x40                                          // data mode\n  MOV   R2, #0                                             // data\n  BL    I2C_Write_Byte                                     // call function\n  ADD   R3, #0x1                                           // increment counter\n  CMP   R3, #0x480                                         // cmp if 0x480\n  BNE   .SSD1306_Clear_Screen_Loop                         // branch not equal\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Displays a letter (character array) on the SSD1306 OLED Display.\n *\n * @details This assembly function displays a letter (character array) on the SSD1306 OLED display by utilizing the \n *          SSD1306_Set_Cursor function to position the cursor and then writing the letter data to the display memory\n *          through I2C communication. The function also calls the SSD1306_Turn_On_Display function to ensure the \n *          display is active.\n *\n * @param   R0: Lower column start address.\n * @param   R1: Higher column start address.\n * @param   R2: Page start address.\n * @param   R3: Address of the character array to be displayed.\n *\n * @retval  None\n */\nSSD1306_Display_Letter:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R4, R0                                             // copy first arg into R4\n  MOV   R5, R1                                             // copy second arg into R5\n  MOV   R6, R2                                             // copy third arg into R6\n  MOV   R7, R3                                             // copy fourth arg into R7\n  BL    SSD1306_Set_Cursor                                 // call function\n  MOV   R8, #0                                             // set counter\n.SSD1306_Display_Letter_Loop:\n  MOV   R0, #0x3C                                          // SSD1306 I2C addr\n  MOV   R1, #0x40                                          // data mode\n  LDRB  R2, [R7, R8]                                       // load byte at addr in R7 and inc by counter\n  BL    I2C_Write_Byte                                     // call function\n  ADDS  R8, #1                                             // increment counter\n  CMP   R8, #6                                             // compare if end of array\n  BNE   .SSD1306_Display_Letter_Loop                       // branch if not equal\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Writes a byte to the I2C device.\n *\n * @details This assembly function writes a byte to the I2C device. It waits for the device to be ready and sends the\n *          data byte using I2C communication.\n *\n * @param   R0: I2C device address.\n * @param   R1: I2C data mode (0x00 for command, 0x40 for data).\n * @param   R2: Byte of data to be sent.\n *\n * @retval  None\n */\nI2C_Write_Byte:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R4, R0                                             // copy first arg into R4\n  MOV   R5, R1                                             // copy second arg into R5\n  MOV   R6, R2                                             // copy third arg into R6\n.I2C_Wait_Not_Busy:\n  LDR   R7, =0x40005418                                    // load address of I2C1_SR2 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR2 register\n  TST   R8, #(1\u003c\u003c1)                                        // read the BUSY bit, if 0, then BNE\n  BNE   .I2C_Wait_Not_Busy                                 // branch if not equal\n  LDR   R7, =0x40005400                                    // load address of I2C1_CR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_CR1 register\n  ORR   R8, #(1\u003c\u003c8)                                        // set the START bit\n  ORR   R8, #(1\u003c\u003c0)                                        // set the PE bit\n  STR   R8, [R7]                                           // store value into I2C1_CR1 register\n.I2C_Wait_Start:\n  LDR   R7, =0x40005414                                    // load address of I2C1_SR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR1 register\n  TST   R8, #(1\u003c\u003c0)                                        // read the SB bit, if 1, then BEQ\n  BEQ   .I2C_Wait_Start                                    // branch if equal\n  LDR   R7, =0x40005410                                    // load address of I2C1_DR register\n  LSL   R4, #1                                             // left shift to make room for the rw bit\n  STR   R4, [R7]                                           // store value into I2C1_DR register\n.I2C_Wait_Addr_Flag:\n  LDR   R7, =0x40005414                                    // load address of I2C1_SR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR1 register\n  LDR   R7, =0x40005414                                    // load address of I2C1_SR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR1 register\n  TST   R8, #(1\u003c\u003c1)                                        // read the ADDR bit, if 1, then BEQ\n  BEQ   .I2C_Wait_Addr_Flag                                // branch if equal\n  LDR   R7, =0x40005418                                    // load address of I2C1_SR2 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR2 register\n  STR   R8, [R7]                                           // store value into I2C1_SR2 register\n.I2C_Wait_Data_Empty_Send_Mem_Addr:\n  LDR   R7, =0x40005414                                    // load address of I2C1_SR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR1 register\n  TST   R8, #(1\u003c\u003c7)                                        // read the TxE bit, if 0, then BEQ\n  BEQ   .I2C_Wait_Data_Empty_Send_Mem_Addr                 // branch if equal\n  LDR   R7, =0x40005410                                    // load address of I2C1_DR register\n  STR   R5, [R7]                                           // store value into I2C1_DR register\n.I2C_Wait_Data_Empty_Send_Data:\n  LDR   R7, =0x40005414                                    // load address of I2C1_SR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR1 register\n  TST   R8, #(1\u003c\u003c7)                                        // read the TxE bit, if 1, then BNE\n  BEQ   .I2C_Wait_Data_Empty_Send_Data                     // branch if equal\n.I2C_Send_Data:\n  LDR   R7, =0x40005410                                    // load address of I2C1_DR register\n  STR   R6, [R7]                                           // store value into I2C1_DR register\n.I2C_Wait_Data_Transfer_Finished:\n  LDR   R7, =0x40005414                                    // load address of I2C1_SR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_SR1 register\n  TST   R8, #(1\u003c\u003c2)                                        // read the BTF bit, if 0, then BEQ\n  BEQ   .I2C_Wait_Data_Transfer_Finished                   // branch if equal\n  LDR   R7, =0x40005400                                    // load address of I2C1_CR1 register\n  LDR   R8, [R7]                                           // load value inside I2C1_CR1 register\n  ORR   R8, #(1\u003c\u003c9)                                        // set the STOP bit\n  ORR   R8, #(1\u003c\u003c0)                                        // set the PE bit\n  STR   R8, [R7]                                           // store value into I2C1_CR1 register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Read the value of pin PB0.\n *\n *         This function reads the value of pin PB0 (GPIO pin 0 of GPIO port B).\n *\n * @param  None\n * @retval uint32_t Value of pin PB0 (0 or 1)\n */\nGPIOB_PB0_Read_Value:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020410                                    // load address of GPIOB_IDR register\n  LDR   R0, [R4]                                           // load value inside GPIOB_IDR register\n  AND   R0, #(1\u003c\u003c0)                                        // read the IDR0 bit\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Read the value of pin PB1.\n *\n *         This function reads the value of pin PB1 (GPIO pin 1 of GPIO port B).\n *\n * @param  None\n * @retval uint32_t Value of pin PB1 (0 or 1)\n */\nGPIOB_PB1_Read_Value:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020410                                    // load address of GPIOB_IDR register\n  LDR   R0, [R4]                                           // load value inside GPIOB_IDR register\n  AND   R0, #(1\u003c\u003c1)                                        // read the IDR1 bit\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Read the value of pin PB2.\n *\n *         This function reads the value of pin PB2 (GPIO pin 2 of GPIO port B).\n *\n * @param  None\n * @retval uint32_t Value of pin PB2 (0 or 1)\n */\nGPIOB_PB2_Read_Value:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020410                                    // load address of GPIOB_IDR register\n  LDR   R0, [R4]                                           // load value inside GPIOB_IDR register\n  AND   R0, #(1\u003c\u003c2)                                        // read the IDR2 bit\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Read the value of pin PB3.\n *\n *         This function reads the value of pin PB3 (GPIO pin 3 of GPIO port B).\n *\n * @param  None\n * @retval uint32_t Value of pin PB3 (0 or 1)\n */\nGPIOB_PB3_Read_Value:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020410                                    // load address of GPIOB_IDR register\n  LDR   R0, [R4]                                           // load value inside GPIOB_IDR register\n  AND   R0, #(1\u003c\u003c3)                                        // read the IDR3 bit\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Read the value of pin PB4.\n *\n *         This function reads the value of pin PB4 (GPIO pin 4 of GPIO port B).\n *\n * @param  None\n * @retval uint32_t Value of pin PB4 (0 or 1)\n */\nGPIOB_PB4_Read_Value:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020410                                    // load address of GPIOB_IDR register\n  LDR   R0, [R4]                                           // load value inside GPIOB_IDR register\n  AND   R0, #(1\u003c\u003c4)                                        // read the IDR4 bit\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Read the value of pin PB5.\n *\n *         This function reads the value of pin PB5 (GPIO pin 5 of GPIO port B).\n *\n * @param  None\n * @retval uint32_t Value of pin PB5 (0 or 1)\n */\nGPIOB_PB5_Read_Value:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40020410                                    // load address of GPIOB_IDR register\n  LDR   R0, [R4]                                           // load value inside GPIOB_IDR register\n  AND   R0, #(1\u003c\u003c5)                                        // read the IDR5 bit\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Enable USART2 transmission.\n *\n *         This function enables transmission on USART2 by setting the TE bit in the USART2_CR1 register\n *         and clearing the RE bit to disable reception.\n *\n * @param  None\n * @retval None\n */\nUSART2_Transmit_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x4000440C                                    // load address of USART2_CR1 register\n  LDR   R5, [R4]                                           // load value inside USART2_CR1 register\n  ORR   R5, #(1\u003c\u003c3)                                        // set the TE bit\n  STR   R5, [R4]                                           // store value into USART2_CR1 register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Enable USART2 reception.\n *\n *         This function enables reception on USART2 by setting the RE bit in the USART2_CR1 register\n *         and clearing the TE bit to disable transmission.\n *\n * @param  None\n * @retval None\n */\nUSART2_Receive_Enable:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x4000440C                                    // load address of USART2_CR1 register\n  LDR   R5, [R4]                                           // load value inside USART2_CR1 register\n  ORR   R5, #(1\u003c\u003c2)                                        // set the RE bit\n  STR   R5, [R4]                                           // store value into USART2_CR1 register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Delay for approximately 30 microseconds.\n *\n *         This function creates a delay of approximately 30 microseconds.\n *\n * @param  None\n * @retval None\n */\nThirty_Microsecond_Delay:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack \n  MOV   R4, #7                                             // number of loops\n.Thirty_Microsecond_Delay_Outer_Loop:\n  MOV   R5, #0xFFFF                                        // set initial delay count\n.Thirty_Microsecond_Delay_Inner_Loop:\n  SUB   R5, #1                                             // decrement delay count\n  CMP   R5, #0                                             // check if delay count reached zero\n  BNE   .Thirty_Microsecond_Delay_Inner_Loop               // continue loop if delay count not reached zero\n  SUB   R4, #1                                             // decrement loop counter\n  CMP   R4, #0                                             // check if delay count reached zero\n  BNE   .Thirty_Microsecond_Delay_Outer_Loop               // continue outer loop if more loops to go\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Menu selection loop.\n *\n *         This function continuously checks for button inputs to perform menu actions.\n *         If GPIOB_PB0 is pressed, it generates a message.\n *         If GPIOB_PB1 is pressed, it receives, parses, and displays a message.\n *         After each action, the function clears the screen and returns to the menu loop.\n *\n * @param  None\n * @retval None\n */\nMenu:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x11                                          // higher col start addr\n  MOV   R2, #0xB0                                          // page start addr\n  LDR   R3, =LETTER_M                                      // load the address of array M\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x12                                          // higher col start addr\n  MOV   R2, #0xB0                                          // page start addr\n  LDR   R3, =LETTER_E                                      // load the address of array E\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x13                                          // higher col start addr\n  MOV   R2, #0xB0                                          // page start addr\n  LDR   R3, =LETTER_N                                      // load the address of array N\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x14                                          // higher col start addr\n  MOV   R2, #0xB0                                          // page start addr\n  LDR   R3, =LETTER_U                                      // load the address of array U\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x11                                          // higher col start addr\n  MOV   R2, #0xB3                                          // page start addr\n  LDR   R3, =LETTER_T                                      // load the address of array T\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x12                                          // higher col start addr\n  MOV   R2, #0xB3                                          // page start addr\n  LDR   R3, =LETTER_X                                      // load the address of array X\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x11                                          // higher col start addr\n  MOV   R2, #0xB5                                          // page start addr\n  LDR   R3, =LETTER_R                                      // load the address of array R\n  BL    SSD1306_Display_Letter                             // call function\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, #0x12                                          // higher col start addr\n  MOV   R2, #0xB5                                          // page start addr\n  LDR   R3, =LETTER_X                                      // load the address of array X\n  BL    SSD1306_Display_Letter                             // call function\n.Menu_Select:\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    GPIOB_PB0_Read_Value                               // call function\n  CMP   R0, #0x1                                           // check if button is pressed\n  BNE   .Menu_Select_GPIOB_PB0_Button_Pressed              // branch if not equal\n  BL    GPIOB_PB1_Read_Value                               // call function\n  CMP   R0, #0x2                                           // check if button is pressed\n  BNE   .Menu_Select_GPIOB_PB1_Button_Pressed              // branch if not equal\n  B     .Menu_Select                                       // call function\n.Menu_Select_GPIOB_PB0_Button_Pressed:\n  BL    SSD1306_Clear_Screen                               // call function\n  BL    Generate_Message                                   // call function\n  B     .Menu_Exit                                         // branch\n.Menu_Select_GPIOB_PB1_Button_Pressed:\n  BL    SSD1306_Clear_Screen                               // call function\n  BL    Receive_Message                                    // call function\n  BL    Parse_Message                                      // call function\n  BL    Display_Message                                    // call function\n  B     .Menu_Exit                                         // branch\n.Menu_Exit:\n  BL    SSD1306_Clear_Screen                               // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Generate a message based on button inputs.\n *\n *         This function generates a message based on button inputs and sends it through USART2.\n *\n * @param  None\n * @retval None\n */\nGenerate_Message:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  BL    USART2_Clear_Buffer                                // call function\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    Thirty_Microsecond_Delay                           // call function\n  MOV   R4, #0                                             // set array index to zero\n  MOV   R5, #0x11                                          // higher col start addr\n  LDR   R6, =LETTERS_ARRAY                                 // load the address of array\n  MOV   R7, #0                                             // set array pointer\n  LDR   R8, =message_transmit_array                        // load addr of message array\n  MOV   R9, #0                                             // set current char\n  MOV   R10, #0x11                                         // set left of screen\n  MOV   R11, #0x15                                         // set right of screen\n  MOV   R12, #0xB0                                         // page start addr\n.Generate_Message_Loop:\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    GPIOB_PB0_Read_Value                               // call function\n  CMP   R0, #0x1                                           // check if button is pressed\n  BNE   .Generate_Message_GPIOB_PB0_Button_Pressed         // branch if not equal\n  BL    GPIOB_PB1_Read_Value                               // call function\n  CMP   R0, #0x2                                           // check if button is pressed\n  BNE   .Generate_Message_GPIOB_PB1_Button_Pressed         // branch if not equal\n  BL    GPIOB_PB2_Read_Value                               // call function\n  CMP   R0, #0x4                                           // check if button is pressed\n  BNE   .Generate_Message_GPIOB_PB2_Button_Pressed         // branch if not equal\n  BL    GPIOB_PB3_Read_Value                               // call function\n  CMP   R0, #0x8                                           // check if button is pressed\n  BNE   .Generate_Message_GPIOB_PB3_Button_Pressed         // branch if not equal\n  BL    GPIOB_PB4_Read_Value                               // call function\n  CMP   R0, #0x10                                          // check if button is pressed\n  BNE   .Generate_Message_GPIOB_PB4_Button_Pressed         // branch if not equal\n  BL    GPIOB_PB5_Read_Value                               // call function\n  CMP   R0, #0x20                                          // check if button is pressed\n  BNE   .Generate_Message_GPIOB_PB5_Button_Pressed         // branch if not equal\n  B     .Generate_Message_Loop                             // branch\n.Generate_Message_GPIOB_PB0_Button_Pressed:\n  BL    Thirty_Microsecond_Delay                           // call function\n  CMP   R5, R11                                            // check if we are at right of screen\n  BEQ   .Generate_Message_Exit                             // branch if equal\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, R5                                             // higher col start addr\n  MOV   R2, R12                                            // page start addr\n  LDR   R3, [R6, R4, LSL #2]                               // load the char based on index in R6\n  LDRB  R9, [R3, #6]                                       // move current char byte at addr into R9\n  BL    SSD1306_Display_Letter                             // call function\n  ADD   R4, #1                                             // increment index\n  CMP   R4, #26                                            // check if end of array is reached\n  BNE   .Generate_Message_Loop                             // if not, continue loop\n  MOV   R4, #0                                             // reset index to 0 when end of array is reached\n  B     .Generate_Message_Loop                             // continue loop\n.Generate_Message_GPIOB_PB1_Button_Pressed:\n  BL    Thirty_Microsecond_Delay                           // call function\n  CMP   R5, R11                                            // check if we are at right of screen\n  BEQ   .Generate_Message_Exit                             // branch if equal\n  MOV   R0, #0x00                                          // lower col start addr\n  MOV   R1, R5                                             // higher col start addr\n  MOV   R2, R12                                            // page start addr\n  LDR   R3, [R6, R4, LSL #2]                               // load the char based on index in R6\n  LDRB  R9, [R3, #6]                                       // move current char byte at addr into R9 \n  BL    SSD1306_Display_Letter                             // call function\n  SUB   R4, #1                                             // increment index\n  CMP   R4, #-1                                            // check if end of array is reached\n  BNE   .Generate_Message_Loop                             // if not, continue loop\n  MOV   R4, #25                                            // reset index to array end\n  B     .Generate_Message_Loop                             // continue loop\n.Generate_Message_GPIOB_PB2_Button_Pressed:\n  BL    Thirty_Microsecond_Delay                           // call function\n  CMP   R5, R11                                            // check if we are at right of screen\n  BEQ   .Generate_Message_Loop                             // branch if equal\n  STR   R9, [R8, R7]                                       // store char in the array at index in R4\n  ADD   R5, #1                                             // increment page addr\n  ADD   R7, #1                                             // increment array pointer\n  B     .Generate_Message_Loop                             // continue loop\n.Generate_Message_GPIOB_PB3_Button_Pressed:\n  BL    Thirty_Microsecond_Delay                           // call function\n  CMP   R5, R10                                            // check if we are at left of screen\n  BEQ   .Generate_Message_Loop                             // branch if equal\n  STR   R9, [R8, R7]                                       // store char in the array at index in R4\n  SUB   R5, #1                                             // increment page addr\n  ADD   R7, #1                                             // increment array pointer\n  B     .Generate_Message_Loop                             // continue loop\n.Generate_Message_GPIOB_PB4_Button_Pressed:\n  BL    Thirty_Microsecond_Delay                           // call function\n  MOV   R5, #0                                             // init index for the array\n  MOV   R6, #4                                             // array length\n.Generate_Message_Send_Char_Loop:\n  CMP   R5, R6                                             // compare current index with array length\n  BEQ   .Generate_Message_Exit                             // branch if equal\n  LDRB  R7, [R8, R5]                                       // load addr from the array at index R5 \n  MOV   R0, #0x41                                          // 'A'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x54                                          // 'T'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x2B                                          // '+'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x53                                          // 'S'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x45                                          // 'E'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x4E                                          // 'N'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x44                                          // 'D'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x3D                                          // '='\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x30                                          // '0'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x2C                                          // ','\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x31                                          // '1'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0x2C                                          // ','\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, R7                                             // current char at array position in R7\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0xD                                           // '\\r'\n  BL    USART2_Transmit_Character                          // call function\n  MOV   R0, #0xA                                           // '\\n'\n  BL    USART2_Transmit_Character                          // call function\n  ADD   R5, #1                                             // increment index\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    Thirty_Microsecond_Delay                           // call function\n  BL    Thirty_Microsecond_Delay                           // call function\n  B     .Generate_Message_Send_Char_Loop                   // branch \n.Generate_Message_GPIOB_PB5_Button_Pressed:\n  BL    Thirty_Microsecond_Delay                           // call function\n  B     .Generate_Message_Exit                             // branch\n.Generate_Message_Exit:\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Receive a message.\n *\n *         This function receives a message character by character through USART2 and stores it in the \n *         message full receive array.\n *\n * @param  None\n * @retval None\n */\nReceive_Message:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack \n  LDR   R4, =message_full_receive_array                    // load addr of message full receive array\n  MOV   R5, #-1                                            // set counter                                                                             \n.Receive_Message_Loop:\n  CMP   R5, #75                                            // compare if at end of our recv buffer\n  BEQ   .Receive_Message_Exit                              // branch if equal\n  ADD   R5, #1                                             // increment counter     \n  BL    USART2_Receive_Character                           // call function\n  STRB  R0, [R4, R5]                                       // store char into message_full_receive_array, offset R5\n  B     .Receive_Message_Loop                              // branch\n.Receive_Message_Exit:  \n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Parse a received message.\n *\n *         This function parses a received message stored in the message_full_receive_array\n *         and extracts specific characters to store in the message parsed receive array.\n *\n * @param  None\n * @retval None\n */\nParse_Message:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =message_full_receive_array                    // load addr of message full receive array\n  LDR   R5, =message_parsed_receive_array                  // load addr of message parsed receive array\n  MOV   R7, #2                                             // set counter\n.Parse_Message_Check_Third_Byte:\n  LDRB  R6, [R4, R7]                                       // load first byte into R6\n  CMP   R6, #0x43                                          // check to make sure it is 'C'\n  BEQ   .Parse_Message_Loop                                // branch if equal\n  ADD   R7, #1                                             // increment counter\n  B     .Parse_Message_Check_Third_Byte                    // branch\n.Parse_Message_Loop:\n  ADD   R8, R7, #7                                         // increment counter to get first correct character\n  LDRB  R6, [R4, R8]                                       // load offset of message full receive array\n  STRB  R6, [R5, #0]                                       // store byte message_parsed_receive_array\n  ADD   R8, R7, #26                                        // increment counter to get second correct character\n  LDRB  R6, [R4, R8]                                       // load offset 28 of message full receive array\n  STRB  R6, [R5, #1]                                       // store byte message_parsed_receive_array\n  ADD   R8, R7, #45                                        // increment counter to get second correct character\n  LDRB  R6, [R4, R8]                                       // load offset of message full receive array\n  STRB  R6, [R5, #2]                                       // store byte message_parsed_receive_array\n  ADD   R8, R7, #64                                        // increment counter to get second correct character\n  LDRB  R6, [R4, R8]                                       // load offset of message full receive array\n  STRB  R6, [R5, #3]                                       // store byte message_parsed_receive_array\n.Parse_Message_Exit:\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller \n\n/**\n * @brief  Display a received message.\n *\n *         This function displays a received message on an SSD1306 OLED display. It iterates through the characters \n *         of the message and calls a function to display each character on the OLED screen.\n *\n * @param  None\n * @retval None\n */\nDisplay_Message:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =message_parsed_receive_array                  // load addr of message parsed receive array\n  MOV   R5, #0x14                                          // set counter\n  MOV   R6, #-1                                            // set counter\n  MOV   R7, #0xB1                                          // page start addr\n  MOV   R8, #0x19                                          // set right of screen\n.Display_Message_Loop:\n  CMP   R6, #3                                             // compare if at end of our message parsed recv buffer\n  BEQ   .Display_Message_Until_Exit                        // branch if equal\n  ADD   R6, #1                                             // increment counter\n  LDRB  R0, [R4, R6]                                       // load byte in message parsed receive array offset R6\n  CMP   R0, 0x41                                           // compare 'A'\n  BEQ   .Display_Message_Letter_A                          // branch if equal\n  CMP   R0, 0x42                                           // compare 'B'\n  BEQ   .Display_Message_Letter_B                          // branch if equal\n  CMP   R0, 0x43                                           // compare 'C'\n  BEQ   .Display_Message_Letter_C                          // branch if equal\n  CMP   R0, 0x44                                           // compare 'D'\n  BEQ   .Display_Message_Letter_D                          // branch if equal\n  CMP   R0, 0x45                                           // compare 'E'\n  BEQ   .Display_Message_Letter_E                          // branch if equal\n  CMP   R0, 0x46                                           // compare 'F'\n  BEQ   .Display_Message_Letter_F                          // branch if equal\n  CMP   R0, 0x47                                           // compare 'G'\n  BEQ   .Display_Message_Letter_G                          // branch if equal\n  CMP   R0, 0x48                                           // compare 'H'\n  BEQ   .Display_Message_Letter_H                          // branch if equal\n  CMP   R0, 0x49                                           // compare 'I'\n  BEQ   .Display_Message_Letter_I                          // branch if equal\n  CMP   R0, 0x4A                                           // compare 'J'\n  BEQ   .Display_Message_Letter_J                          // branch if equal\n  CMP   R0, 0x4B                                           // compare 'K'\n  BEQ   .Display_Message_Letter_K                          // branch if equal\n  CMP   R0, 0x4C                                           // compare 'L'\n  BEQ   .Display_Message_Letter_L                          // branch if equal\n  CMP   R0, 0x4D                                           // compare 'M'\n  BEQ   .Display_Message_Letter_M                          // branch if equal\n  CMP   R0, 0x4E                                           // compare 'N'\n  BEQ   .Display_Message_Letter_N                          // branch if equal\n  CMP   R0, 0x4F                                           // compare 'O'\n  BEQ   .Display_Message_Letter_O                          // branch if equal\n  CMP   R0, 0x50                                           // compare 'P'\n  BEQ   .Display_Message_Letter_P                          // branch if equal\n  CMP   R0, 0x51                                           // compare 'Q'\n  BEQ   .Display_Message_Letter_Q                          // branch if equal\n  CMP   R0, 0x52                                           // compare 'R'\n  BEQ   .Display_Message_Letter_R                          // branch if equal\n  CMP   R0, 0x53                                           // compare 'S'\n  BEQ   .Display_Message_Letter_S                          // branch if equal\n  CMP   R0, 0x54                                           // compare 'T'\n  BEQ   .Display_Message_Letter_T                          // branch if equal\n  CMP   R0, 0x55                                           // compare 'U'\n  BEQ   .Display_Message_Letter_U                          // branch if equal\n  CMP   R0, 0x56                                           // compare 'V'\n  BEQ   .Display_Message_Letter_V                          // branch if equal\n  CMP   R0, 0x57                                           // compare 'W'\n  BEQ   .Display_Message_Letter_W                          // branch if equal\n  CMP   R0, 0x58                                           // compare 'X'\n  BEQ   .Display_Message_Letter_X                          // branch if equal\n  CMP   R0, 0x59                                           // compare 'Y'\n  BEQ   .Display_Message_Letter_Y                          // branch if equal\n  CMP   R0, 0x5A                                           // compare 'Z'\n  BEQ   .Display_Message_Letter_Z                          // branch if equal\n  CMP   R0, 0x00                                           // compare null\n  BEQ   .Display_Message_LETTER_NULL                       ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmytechnotalent%2Fstm32f4_lora_uart_driver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmytechnotalent%2Fstm32f4_lora_uart_driver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmytechnotalent%2Fstm32f4_lora_uart_driver/lists"}