phillip_bourdon234

Electric_Skateboard_Main_Remote.c

Mar 1st, 2021 (edited)
144
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 34.99 KB | None | 0 0
  1. //===================================================================================================================
  2. //                                                                                  CODE FOR THE REMOTE
  3. //===================================================================================================================
  4.  
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include "stm32f10x.h"
  9. #include "system_stm32f10x.h"
  10. #include "GPIO_Driver.h"
  11. #include "NRF_Driver.h"
  12. #include "delay.h"
  13. #include "LCD_Driver.h"
  14. #include "RTC_Driver.h"
  15. #include "EEPROM_Driver.h"
  16.  
  17. #define RX_PIPE 1
  18. #define ADDR_HIGH 0xAABBCCDD
  19. #define ADDR_LOW 0xEE
  20. #define EN_D_PL_W 1
  21. #define EN_AUTO_ACK 1
  22. #define PL_W 3
  23. #define CHANNEL 0x7B
  24.  
  25. #define EEPROM_ADDRESS 0xA0
  26.  
  27. #define PASSWORD_ADDRESS_HI             0x00
  28. #define PASSWORD_ADDRESS_LO             0x01
  29.  
  30.  
  31. //Each part has two qualities: a name, and how many minutes that part has spent riding.
  32. #define PART_NAME_ADDRESS_HI            0x01
  33. #define PART_NAME_ADDRESS_LO            0x00
  34.  
  35. #define PART_TIME_ADDRESS_HI            0x01
  36. #define PART_TIME_ADDRESS_LO            0x0B
  37.                
  38. //The LED_ANIMATION_ADDR_HI and LO addresses store the LED animation value that was
  39. //picked in the handleLCD_neopixel_scene() function so that the user doesn't
  40. //have to set the LED animation he wants every time he turns on the remote.
  41. #define LED_ANIMATION_ADDR_HI           0x04
  42. #define LED_ANIMATION_ADDR_LO           0x00
  43.  
  44.  
  45. void init_ADC(void);
  46. void EXTI0_IRQHandler(void);
  47. void EXTI1_IRQHandler(void);
  48. uint16_t map(uint16_t var, uint16_t var_low, uint16_t var_high, uint16_t low_thresh, uint16_t high_thresh);
  49.  
  50. void handleLCD_lock_scene(void);
  51. void handleLCD_menu_scene(void);
  52. void handleLCD_ride_scene(void);
  53. void handleLCD_set_time_scene(void);
  54. void handleLCD_set_password_scene(void);
  55. void handleLCD_add_part_scene(uint8_t part_number);
  56. void handleLCD_display_parts_scene(void);
  57. void handleLCD_neopixel_scene(void);
  58.  
  59. void load_eeprom(void);
  60. void load_parts_in_use(void);
  61.  
  62. volatile static uint8_t toggle_button_val = 0;
  63. volatile static uint8_t brake_button_val = 0;
  64.  
  65. uint16_t static adc_data[2] = {0, 0}; //adc[0] represents the voltage divider values
  66.                                                                             //adc[1] represents the analog slider values
  67.  
  68. //the parts_in_use[] global array is used in the following functions: handleLCD_ride_scene(),
  69. //handleLCD_add_part_scene(uint8_t part_number), and load_parts_in_use(). The purpose of this gloabal
  70. //array is to keep track of which parts are in use in order to save time reading and writing to
  71. //the unused part addresses in the EEPROM. The array contains either a 0 or a 1, depending on if
  72. //the part (the index) is in use.
  73. uint8_t static parts_in_use[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  74.                                                                                                                                            
  75.    
  76. int main()
  77. {      
  78.     uint8_t static hourglass[8] =
  79.     {
  80.         0x1F,   //0b00011111,
  81.         0x11,   //0b00010001,
  82.         0x0A,   //0b00001010,
  83.         0x04,   //0b00000100,
  84.         0x0A,   //0b00001010,
  85.         0x11,   //0b00010001,
  86.         0x1F,   //0b00011111,
  87.         0x00    //0b00000000
  88.     };
  89.  
  90.     uint8_t static battery[8] =
  91.     {
  92.         0x04,   //0b00000100,
  93.         0x1F,   //0b00011111,
  94.         0x11,   //0b00010001,
  95.         0x11,   //0b00010001,
  96.         0x1F,   //0b00011111,
  97.         0x1F,   //0b00011111,
  98.         0x1F,   //0b00011111,
  99.         0x1F,   //0b00011111
  100.     };
  101.  
  102.     uint8_t static locked_lock[8] =
  103.     {
  104.         0x00,   //0b00000000,
  105.         0x0E,   //0b00001110,
  106.         0x11,   //0b00010001,
  107.         0x11,   //0b00010001,
  108.         0x1F,   //0b00011111,
  109.         0x1B,   //0b00011011,
  110.         0x1B,   //0b00011011,
  111.         0x1F,   //0b00011111
  112.     };
  113.  
  114.     uint8_t static unlocked_lock[8] =
  115.     {
  116.         0x0E,   //0b00001110,
  117.         0x11,   //0b00010001,
  118.         0x01,   //0b00000001,
  119.         0x01,   //0b00000001,
  120.         0x1F,   //0b00011111,
  121.         0x1B,   //0b00011011,
  122.         0x1B,   //0b00011011,
  123.         0x1F,   //0b00011111
  124.     };
  125.    
  126.    
  127.     //==========================================
  128.     GPIO_TYPE toggle_button;
  129.     toggle_button.port = PORTA;
  130.     toggle_button.pin = 0;
  131.     toggle_button.mode = INPUT;
  132.     toggle_button.speed = SPEED_50MHZ;
  133.     //==========================================
  134.     config_gpio_interrupt(toggle_button.port, toggle_button.pin, RISING_EDGE);
  135.     enable_gpio_interrupt(toggle_button.pin, EXTI0_IRQn);
  136.    
  137.    
  138.     //==========================================
  139.     GPIO_TYPE brake_button;
  140.     brake_button.port = PORTA;
  141.     brake_button.pin = 1;
  142.     brake_button.mode = INPUT;
  143.     brake_button.speed = SPEED_50MHZ;
  144.     //==========================================
  145.     config_gpio_interrupt(brake_button.port, brake_button.pin, RISING_FALLING_EDGE);
  146.     enable_gpio_interrupt(brake_button.pin, EXTI1_IRQn);
  147.    
  148.    
  149.     //==========================================
  150.     GPIO_TYPE potentiometer;
  151.     potentiometer.port = PORTA;
  152.     potentiometer.pin = 2;
  153.     potentiometer.mode = INPUT;
  154.     potentiometer.mode_type = INPUT_ANALOG;
  155.     potentiometer.speed = SPEED_50MHZ;
  156.     //==========================================
  157.  
  158.     init_delay();
  159.     init_ADC();
  160.     eeprom_init();
  161.     lcd_init(PORTA, 8, PORTA, 9, PORTA, 10, PORTA, 11, PORTB, 7, PORTB, 8, PORTB, 9);
  162.     rtc_init();
  163.    
  164.     nrf_init_pins(PORTA, 12, PORTB, 15);
  165.     nrf_init_transmiter(ADDR_HIGH, ADDR_LOW, CHANNEL, EN_AUTO_ACK, EN_D_PL_W);
  166.     nrf_cmd_act_as_TX();
  167.  
  168.     lcd_create_character(1, hourglass);
  169.     lcd_create_character(2, battery);
  170.     lcd_create_character(3, locked_lock);
  171.     lcd_create_character(4, unlocked_lock);
  172.  
  173.     //load_eeprom();     //this function should only be called when the user wants to initiate or reset the data related to the parts
  174.     load_parts_in_use();
  175.        
  176.     handleLCD_lock_scene();
  177.  
  178.     while(1)
  179.     {
  180.             handleLCD_menu_scene();
  181.     }
  182. }
  183.  
  184.  
  185.  
  186. //The EXTI0_IRQHandler interupt service routine changes the value of toggle_button_val
  187. //from 1 to 0 or from 0 to 1 when a rising edge is detected on the toggle_button
  188. void EXTI0_IRQHandler()
  189. {
  190.     EXTI->PR |= (1<<0); //clears interrupt
  191.    
  192.     if(toggle_button_val == 0)
  193.         toggle_button_val = 1;
  194.     else
  195.         toggle_button_val = 0;
  196.  
  197. }
  198.  
  199. //The EXTI1_IRQHandler interupt service routine changes the value of toggle_brake_button_val
  200. //from 1 to 0 or from 0 to 1 when a rising or falling edge is detected on the brake_button
  201. void EXTI1_IRQHandler()
  202. {
  203.     EXTI->PR |= (1<<1); //clears interrupt
  204.    
  205.     if(brake_button_val == 0)
  206.         brake_button_val = 1;
  207.     else
  208.         brake_button_val = 0;
  209. }
  210.  
  211. //The map function takes a variable(var) and it's lowest and highest possible
  212. //values(var_low and var_high) and returns a new value that correctly maps to
  213. //the users defined lowest and highest values (low_thresh and high_thresh).
  214. uint16_t map(uint16_t var, uint16_t var_low, uint16_t var_high, uint16_t low_thresh, uint16_t high_thresh)
  215. {
  216.     return (var -  var_low)*(high_thresh - low_thresh)/(var_high - var_low) + low_thresh;
  217. }
  218.  
  219.  
  220. //The init_ADC function writes to the registers relating to the STM32's internal
  221. //analog to digital converter.
  222. void init_ADC(void)
  223. {
  224.    
  225.     RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; //sets the prescaler on the ADC clock to 6
  226.     RCC->AHBENR |= RCC_AHBENR_DMA1EN; //enables the clock for the DMA
  227.     RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_AFIOEN; //enable clock for ADC1 and Alternate IO clock
  228.    
  229.     ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_CONT; //enables DMA for the ADC and enables continuous mode
  230.     ADC1->CR1 |= ADC_CR1_SCAN; //enables SCAN mode (DMA only);
  231.    
  232.     DMA1_Channel1->CPAR |= (uint32_t)(&(ADC1->DR)); //tells the DMA to get data from the ADC1_DR reg
  233.     DMA1_Channel1->CMAR |= (uint32_t)adc_data; //tells the DMA to send the data to ADC_samples
  234.     DMA1_Channel1->CNDTR |= 2; //tells the DMA how much data to send (size of the ADC_samples)
  235.     DMA1_Channel1->CCR |= DMA_CCR1_CIRC | DMA_CCR1_MINC; //sets the DMA to circular mode and implements memory incremation
  236.     DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0; //tells the ADC that the peripheral has a size of uint16_t
  237.     DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0; //tells the ADC that ADC_samples has a size of uint16_t
  238.     DMA1_Channel1->CCR |= DMA_CCR1_EN; //enables DMA
  239.    
  240.     ADC1->SMPR2 |= ADC_SMPR2_SMP2_0 | ADC_SMPR2_SMP2_1 | ADC_SMPR2_SMP2_2 | //sets channel 2 sampling rate
  241.                                  ADC_SMPR2_SMP8_0 | ADC_SMPR2_SMP8_1 | ADC_SMPR2_SMP8_2; //sets channel 8 sampling rate
  242.     ADC1->SQR1 |= ADC_SQR1_L_0; //tells the ADC to do 2 conversions
  243.     ADC1->SQR3 |= ADC_SQR3_SQ1_1; //sets the first conversion in the sequence equal to channel 2
  244.     ADC1->SQR3 |= ADC_SQR3_SQ2_3; //sets the second conversion in the sequence equal to channel 8
  245.    
  246.    
  247.     ADC1->CR2 |= ADC_CR2_ADON; //wakes up the ADC
  248.     delayMs(1); //satisy the t(stab) for the ADC when it's waking up
  249.     ADC1->CR2 |= ADC_CR2_ADON; //turns on the ADC
  250.     delayMs(1); //satisy the t(stab)
  251.    
  252.     ADC1->CR2 |= ADC_CR2_CAL; //starts a calibration on the ADC
  253.     while(ADC1->CR2 == ADC_CR2_CAL); //wait until the CAL bit = 0 (this means the ADC made a sucessful converstion)
  254. }
  255.  
  256.  
  257. //The handleLCD_lock_scene function is called before main and in the handleLCD_set_password_scene
  258. //function. It's purpose is to halt the user from entering any other scene until the correct password
  259. //has been inputted. It's other purpose is to prevent the user from being able to change the password
  260. //in the handleLCD_set_password_scene function without firstly giving the current password.
  261. void handleLCD_lock_scene(void)
  262. {
  263.     uint8_t  actual_password[4];
  264.    
  265.     //These 4 lines below load the actual_password[]  array with the password
  266.     //that is currently in the EEPROM at addresses PASSWORD_ADDRESS_HI and PASSWORD_ADDRESS_LO.
  267.     //Also, the reason why PASSWORD_ADDRESS_LO is being incremented by 1 each time the actual_password
  268.     //index increases by 1 is because each digit of the password is stored in a seperate address.
  269.     //For example, if the password is '1742', then '1' is stored in PASSWORD_ADDRESS_LO,
  270.     //'7' is stored in PASSWORD_ADDRESS_LO+1 etc. The reason for this is because the addresses
  271.     //can only hold numbers less than 2^8 (256) and I wanted the password to be 4 digits long.
  272.     //Holding the individual digits in an array also helps with the logic and flow of this function.
  273.     actual_password[0] = eeprom_read(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO);
  274.     actual_password[1] = eeprom_read(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO+1);
  275.     actual_password[2] = eeprom_read(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO+2);
  276.     actual_password[3] = eeprom_read(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO+3);
  277.    
  278.     //The attempted_password[] array will hold the temporary attempted password and at the end of the
  279.     //4th digit, will compare each element with the actual_password[] array.
  280.     uint16_t attempted_password[4] = {0, 0, 0, 0};
  281.     uint8_t isPassword = 0;
  282.    
  283.     //Unfortunatly, I did not create a function in the LCD_Driver library to print char arrays, so
  284.     //you must print the characters individualy, which I know is lame. But im lazy.
  285.     lcd_set_cursor(0,0);
  286.     lcd_write_char('P');
  287.     lcd_write_char('A');
  288.     lcd_write_char('S');
  289.     lcd_write_char('S');
  290.     lcd_write_char('W');
  291.     lcd_write_char('O');
  292.     lcd_write_char('R');
  293.     lcd_write_char('D');
  294.   lcd_write_char(':');
  295.     lcd_write_char('*');
  296.     lcd_write_char('*');
  297.     lcd_write_char('*');
  298.     lcd_write_char('*');
  299.    
  300.     lcd_set_cursor(0,14);
  301.     lcd_write_char(3); //display the locked_lock graphic that was created in main
  302.    
  303.    
  304.     //The do while loop will repeat until the isPassword variable equals 4. This variable increases when
  305.     //a digit in the attempted_password[4] array equals actual_password[4] array (this is handled in lines
  306.     //322-327).
  307.     do
  308.     {
  309.         //this for loop ends when the user succesfully inputs 4 digits.
  310.         for(uint8_t k = 0; k < 4; k++)
  311.         {
  312.             while(toggle_button_val != 1)
  313.             {
  314.                 lcd_set_cursor(0,9+k);
  315.                 attempted_password[k] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 10));
  316.                 lcd_write_number(attempted_password[k]);
  317.             }
  318.             lcd_set_cursor(0,9+k);
  319.             lcd_write_char('*');
  320.             toggle_button_val = 0; 
  321.         }
  322.            
  323.         for(int p = 0; p < 4; p++)
  324.         {
  325.             if(attempted_password[p] == actual_password[p])
  326.             {
  327.                 isPassword++;
  328.             }
  329.         }
  330.  
  331.         //if the password was not correct, everything in this if stated will be executed.
  332.         if(isPassword != 4)
  333.         {
  334.             isPassword = 0;
  335.             //this for loops produces a locking animation
  336.             for(int j = 0; j < 6; j++)
  337.             {
  338.                 lcd_set_cursor(0,14);
  339.                 if(j == 1 || j == 3 || j == 5)
  340.                     lcd_write_char(3); //locked character
  341.                 else
  342.                     lcd_write_char(' ');
  343.                 delayMs(200);
  344.             }
  345.             lcd_set_cursor(0,9);
  346.             lcd_write_char('*');
  347.             lcd_write_char('*');
  348.             lcd_write_char('*');
  349.             lcd_write_char('*');
  350.         }
  351.     }while(isPassword != 4);   
  352.    
  353.     lcd_set_cursor(0,14);
  354.     lcd_write_char(4); //unlocked character
  355.     delayMs(1000);
  356.    
  357.     lcd_clear(0, 0, 16);
  358.    
  359. }
  360.  
  361.  
  362. //The handleLCD_menu_scene function displays all of the scenes that the
  363. //user can choose from and when the toggle_button is pressed, executes the users chosen
  364. //scene. These scenes are the handleLCD_ride_scene(), handleLCD_set_time_scene(),
  365. //handleLCD_set_password_scene(), handleLCD_display_parts_scene(), and handleLCD_neopixel_scene().
  366. void handleLCD_menu_scene(void)
  367. {
  368.     uint8_t menu_selection;
  369.    
  370.     do
  371.     {
  372.          //The menu_selection varible has a range of [0,5) and is changed by adc_data[1], which
  373.          //is the data that comes from the analog slider.
  374.          menu_selection = (uint8_t)map(adc_data[1], 0, 4096, 0, 5);
  375.        
  376.         //NOTE: Make this a switch case statement instead of 4 if statements.
  377.         if(menu_selection == 0)
  378.         {
  379.             lcd_set_cursor(0,0);
  380.             lcd_write_char('R');
  381.             lcd_write_char('I');
  382.             lcd_write_char('D');
  383.             lcd_write_char('E');
  384.             lcd_write_char(' ');
  385.             lcd_write_char(' ');
  386.             lcd_write_char(' ');
  387.             lcd_write_char(' ');
  388.             lcd_write_char(' ');
  389.             lcd_write_char(' ');
  390.             lcd_write_char(' ');
  391.             lcd_write_char(' ');
  392.             lcd_write_char(' ');
  393.             lcd_write_char(' ');
  394.             lcd_write_char(' ');
  395.             lcd_write_char(' ');
  396.         }
  397.         if(menu_selection == 1)
  398.         {
  399.             lcd_set_cursor(0,0);
  400.             lcd_write_char('S');
  401.             lcd_write_char('E');
  402.             lcd_write_char('T');
  403.             lcd_write_char(' ');
  404.             lcd_write_char('T');
  405.             lcd_write_char('I');
  406.             lcd_write_char('M');
  407.             lcd_write_char('E');
  408.             lcd_write_char(' ');
  409.             lcd_write_char(' ');
  410.             lcd_write_char(' ');
  411.             lcd_write_char(' ');
  412.             lcd_write_char(' ');
  413.             lcd_write_char(' ');
  414.             lcd_write_char(' ');
  415.         }
  416.         if(menu_selection == 2)
  417.         {
  418.             lcd_set_cursor(0,0);
  419.             lcd_write_char('S');
  420.             lcd_write_char('E');
  421.             lcd_write_char('T');
  422.             lcd_write_char(' ');
  423.             lcd_write_char('P');
  424.             lcd_write_char('A');
  425.             lcd_write_char('S');
  426.             lcd_write_char('S');       
  427.             lcd_write_char('W');
  428.             lcd_write_char('O');
  429.             lcd_write_char('R');
  430.             lcd_write_char('D');   
  431.             lcd_write_char(' ');
  432.             lcd_write_char(' ');
  433.             lcd_write_char(' ');
  434.             lcd_write_char(' ');           
  435.         }
  436.         if(menu_selection == 3)
  437.         {
  438.             lcd_set_cursor(0,0);
  439.             lcd_write_char('D');
  440.             lcd_write_char('I');
  441.             lcd_write_char('S');
  442.             lcd_write_char('P');
  443.             lcd_write_char('L');
  444.             lcd_write_char('A');
  445.             lcd_write_char('Y');
  446.             lcd_write_char(' ');       
  447.             lcd_write_char('P');
  448.             lcd_write_char('A');
  449.             lcd_write_char('R');
  450.             lcd_write_char('T');   
  451.             lcd_write_char('S');
  452.             lcd_write_char(' ');
  453.             lcd_write_char(' ');
  454.             lcd_write_char(' ');           
  455.         }
  456.         if(menu_selection == 4)
  457.         {
  458.             lcd_set_cursor(0,0);
  459.             lcd_write_char('S');
  460.             lcd_write_char('E');
  461.             lcd_write_char('L');
  462.             lcd_write_char('E');
  463.             lcd_write_char('C');
  464.             lcd_write_char('T');
  465.             lcd_write_char(' ');
  466.             lcd_write_char('L');       
  467.             lcd_write_char('E');
  468.             lcd_write_char('D');
  469.             lcd_write_char(' ');
  470.             lcd_write_char(' ');   
  471.             lcd_write_char(' ');
  472.             lcd_write_char(' ');       
  473.         }
  474.     }while(toggle_button_val != 1);
  475.    
  476.     toggle_button_val = 0;
  477.    
  478.     switch(menu_selection)
  479.     {
  480.         case 0:
  481.             handleLCD_ride_scene();
  482.         break;
  483.            
  484.         case 1:
  485.             handleLCD_set_time_scene();
  486.         break;
  487.            
  488.         case 2:
  489.             handleLCD_set_password_scene();
  490.         break;
  491.        
  492.         case 3:
  493.             handleLCD_display_parts_scene();
  494.         break;
  495.        
  496.         case 4:
  497.             handleLCD_neopixel_scene();
  498.         break;
  499.     }
  500. }
  501.  
  502.  
  503. //The handleLCD_set_password_scene function allows the user to change the
  504. //password after inputing the correct current password. The new password
  505. //is loaded into EEPROM so the system remebers after a power off reset.
  506. void handleLCD_set_password_scene()
  507. {
  508.     handleLCD_lock_scene(); //Make the user correctly input the current password
  509.    
  510.     uint8_t password[4] = {0, 0, 0, 0};
  511.    
  512.     lcd_set_cursor(0,0);
  513.     lcd_write_char('N');
  514.     lcd_write_char('E');
  515.     lcd_write_char('W');
  516.     lcd_write_char(' ');
  517.     lcd_write_char('P');
  518.     lcd_write_char('W');
  519.     lcd_write_char('O');
  520.     lcd_write_char('R');
  521.     lcd_write_char('D');
  522.     lcd_write_char('>');
  523.     lcd_write_char('_');
  524.     lcd_write_char('_');
  525.     lcd_write_char('_');
  526.     lcd_write_char('_');
  527.    
  528.     for(uint8_t i = 0; i < 4; i++)
  529.     {
  530.         while(toggle_button_val != 1)
  531.         {
  532.             lcd_set_cursor(0,10+i);
  533.             password[i] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 10));
  534.             lcd_write_number(password[i]);
  535.         }
  536.         toggle_button_val = 0;
  537.        
  538.     }
  539.    
  540.     eeprom_write(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO, password[0]);
  541.     eeprom_write(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO+1, password[1]);
  542.     eeprom_write(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO+2, password[2]);
  543.     eeprom_write(EEPROM_ADDRESS, PASSWORD_ADDRESS_HI, PASSWORD_ADDRESS_LO+3, password[3]);
  544.    
  545.     lcd_clear(0, 0, 16);
  546.        
  547. }
  548.  
  549.  
  550. //The handleLCD_set_time_scene function allows the user to change the time that is displayed
  551. //in the handleLCD_ride_scene function.
  552. void handleLCD_set_time_scene()
  553. {
  554.     uint8_t rtc_time[4] = {0, 0, 0, 0};
  555.     uint8_t hours = 0;
  556.     uint8_t minutes = 0;
  557.    
  558.     lcd_set_cursor(0,0);
  559.     lcd_write_char('N');
  560.     lcd_write_char('E');
  561.     lcd_write_char('W');
  562.     lcd_write_char(' ');
  563.     lcd_write_char('T');
  564.     lcd_write_char('I');
  565.     lcd_write_char('M');
  566.     lcd_write_char('E');
  567.     lcd_write_char('>');
  568.    
  569.     //The reason I did not contain all of these while loops in a foor loop
  570.     //is because the input changes depending on what the user chooses. For
  571.     //example, the range of the first digit is 3, while the range of the second digit is 10,
  572.     //and the range of the third digit is 6. I could make a for loop and incorparate a lot of
  573.     //if statements, but I just decided to handle one digit at a time and have seperate while
  574.     //loops for each.
  575.    
  576.     while(toggle_button_val != 1) //wait for the toggle button to be pressed
  577.     {
  578.         lcd_set_cursor(0,9);
  579.         rtc_time[3] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 3));
  580.         lcd_write_number(rtc_time[3]);
  581.         lcd_write_char('*');
  582.         lcd_write_char(':');
  583.         lcd_write_char('*');
  584.         lcd_write_char('*');
  585.     }
  586.    
  587.     toggle_button_val = 0; //reset toggle button value
  588.    
  589.     while(toggle_button_val != 1) //wait for the toggle button to be pressed
  590.     {
  591.         lcd_set_cursor(0,10);
  592.         rtc_time[2] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 10));
  593.        
  594.         if(rtc_time[3] == 2) //this is a case where the first digit is 2. If it is, then the third digit
  595.                                // cannot exceed 3 (military time -> 00:00 - 23:59)
  596.         {
  597.             if(rtc_time[2] > 3)
  598.             {
  599.                 rtc_time[2] = 3;
  600.             }
  601.         }
  602.        
  603.         lcd_write_number(rtc_time[2]);
  604.         lcd_write_char(':');
  605.         lcd_write_char('*');
  606.         lcd_write_char('*');
  607.     }
  608.    
  609.     toggle_button_val = 0; //reset toggle button value
  610.        
  611.     while(toggle_button_val != 1) //wait for the toggle button to be pressed
  612.     {
  613.         lcd_set_cursor(0,12);
  614.         rtc_time[1] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 6));
  615.         lcd_write_number(rtc_time[1]);
  616.         lcd_write_char('*');
  617.     }
  618.        
  619.     toggle_button_val = 0; //reset toggle button value
  620.    
  621.     while(toggle_button_val != 1) //wait for the toggle button to be pressed
  622.     {
  623.         lcd_set_cursor(0,13);
  624.         rtc_time[0] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 10));
  625.         lcd_write_number(rtc_time[0]);
  626.     }
  627.    
  628.     toggle_button_val = 0; //reset toggle button value
  629.    
  630.     //turns the indivual digits into numbers that can be inputed in rtc_set_time
  631.     hours = (rtc_time[3] * 10) + rtc_time[2];  
  632.     minutes = (rtc_time[1] * 10) + rtc_time[0];
  633.    
  634.     rtc_set_time(hours, minutes, 0);
  635.    
  636.     lcd_clear(0, 0, 16);
  637.    
  638. }
  639.  
  640.  
  641. //The handleLCD_ride_scene function allows the user to send input from the analog slider
  642. //and the brake button to the skateboard. While in this function, the user can also see the
  643. //time of day and the current battery percentage of the remote. Accessing this function also
  644. //sends the last loaded LED animation in EEPROM to the skateboard. When the user exists this function,
  645. //then the time (in minutes) that passed during the time in this scene get added to the parts in use
  646. //(more info of this can be found in the functions handleLCD_display_parts_scene and handleLCD_add_parts_scene
  647. void handleLCD_ride_scene(void)
  648. {
  649.     uint8_t batteryLevel = 0;
  650.     uint8_t rtc_data[3];
  651.     uint8_t nrf_data[4];
  652.     uint8_t compare_minute = 0;
  653.     uint16_t minute_counter = 0;    //This variable keeps track of the minutes that pass during the time spent in this function
  654.    
  655.     lcd_set_cursor(1,0);
  656.     lcd_write_char(2); //print the hourglass graphic made in main
  657.     lcd_write_char('>');
  658.    
  659.     lcd_set_cursor(0,0);
  660.     lcd_write_char(1); //print the battery graphic made in main
  661.     lcd_write_char('>');
  662.    
  663.     lcd_set_cursor(1, 4);
  664.     lcd_write_char('%');   
  665.    
  666.     reset_millis(); //These functions are described in the delay.h header file
  667.    
  668.     nrf_data[2] = eeprom_read(EEPROM_ADDRESS, LED_ANIMATION_ADDR_HI, LED_ANIMATION_ADDR_LO);
  669.    
  670.     do
  671.     {
  672.        
  673.         //TX MODE
  674.         nrf_data[0] += 1;   //This value is dumby data that is constantly changing in order for the
  675.                                                 //NRF24L01 module on the skateboard side to constantly set the
  676.                                                 //RX_DR interrupt. I do this because of a feature on the skateboard
  677.                                                 //that detects when the RX_DR hasn't been set for a certain amount
  678.                             //of time, which indicates that the remote lost connection and slows
  679.                             //down the skateboard
  680.         nrf_data[1] =   (uint8_t)(map(adc_data[1], 0, 4096, 0, 255));
  681.         nrf_data[3] = brake_button_val;
  682.         batteryLevel = (uint8_t)(map(adc_data[0], 2600, 3800, 0, 100));
  683.        
  684.         if(nrf_data[0] > 250)
  685.             nrf_data[0] = 0;
  686.        
  687.         if(nrf_send(nrf_data, 32))
  688.         {
  689.                 //I had an LED toggling in here for debugging purposes initially
  690.         }
  691.  
  692.         //the play_millis function is described in the delay.h header, but basically
  693.         //this if statement executes every (5000 * 1000)Us, which is 5 seconds. I
  694.         //implemented this so that the data that controls the skateboard is sent at a much faster rate
  695.         //than what is inside this if statement, which is basically just updating the LCD screen with the time
  696.         //and the battery percentage of the remote. I know now that an RTOS would be much more appropriate
  697.         //for this type of behavior, so I will be working on that for version 2.0
  698.         if(play_millis(5000*1000) == 0)
  699.         {  
  700.             compare_minute = rtc_get_minutes();
  701.            
  702.             if(rtc_data[1] != compare_minute) //the rtc_data[1] is changed a few lines down,
  703.                     minute_counter++;                           //so what this if statement does is it checks to see if the
  704.                                                                                 //"current" minute (compare_minute) is different to the value
  705.                                                                                 //in rtc_data[1]. If it is, then it knows that the minute has
  706.                                                                                 //just increased, therefore I increment the minute_counter.
  707.                                                                                 //It would be better if the RTC module had data like this that
  708.                                                                                 //I could access, but unfortunately I had to implement this myself.
  709.                
  710.            
  711.             lcd_set_cursor(1, 2);
  712.  
  713.             if(batteryLevel < 10)
  714.                 lcd_write_char('0');
  715.             lcd_write_number(batteryLevel);
  716.            
  717.             rtc_data[0] =   rtc_get_seconds();
  718.             rtc_data[1] = rtc_get_minutes();
  719.             rtc_data[2] = rtc_get_hours();
  720.        
  721.             lcd_set_cursor(0, 2);
  722.            
  723.             if(rtc_data[2] < 10)
  724.                 lcd_write_char('0');
  725.            
  726.             lcd_write_number(rtc_data[2]);
  727.             lcd_set_cursor(0, 4);
  728.             lcd_write_char(':');
  729.            
  730.             if(rtc_data[1] < 10)
  731.                 lcd_write_char('0');
  732.            
  733.             lcd_write_number(rtc_data[1]);
  734.             lcd_set_cursor(0,7);
  735.         }      
  736.        
  737.     }while(toggle_button_val != 1);
  738.    
  739.     toggle_button_val = 0;
  740.    
  741.     minute_counter--; //this eliminates the first compare (rtc_data[1] != compare_minute) because the first
  742.                                         //compare will almost always have a different value, resulting in an incrementaion of
  743.                                         //minute_counter. However, this does not indicate a minute has past, therefor
  744.                                         //I subtract one from the minute_counter variable.
  745.    
  746.    
  747.     uint16_t part_time_spent_new = 0;
  748.     uint16_t part_time_spent_current = 0;
  749.    
  750.     //This for loop saves the minutes to the parts that are only in use
  751.     for(uint8_t k = 0; k < 16; k++)
  752.     {
  753.         if(parts_in_use[k] == 1) //check to see if the part is in use
  754.         {
  755.            
  756.             //because the EEPROM address are 1 byte each and I want the number of minutes to be as big
  757.             //as 9999, then I have to store the hi and low bits of the minutes in seperate registers.
  758.             //If I want to access the entire number of minutes, then I have to take the hi part of the number,
  759.             //shift it left by 8, then do a |= on the low part of the number.
  760.             part_time_spent_current = eeprom_read(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*k)+(PART_TIME_ADDRESS_LO)));
  761.             part_time_spent_current = (uint16_t)(part_time_spent_current<<8);
  762.             part_time_spent_current |= eeprom_read(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*k)+(PART_TIME_ADDRESS_LO+1)));
  763.                    
  764.             part_time_spent_new = part_time_spent_current + minute_counter;
  765.        
  766.             eeprom_write(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*k)+(PART_TIME_ADDRESS_LO)), (part_time_spent_new>>8)); //stores the high value
  767.             eeprom_write(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*k)+(PART_TIME_ADDRESS_LO+1)), (part_time_spent_new&0x00FF));//stores the low value
  768.            
  769.         }
  770.     }
  771.    
  772.     lcd_clear(0, 0, 16);
  773.     lcd_clear(1, 0, 16);
  774. }
  775.  
  776.  
  777. //The handleLCD_display_parts_scene function displays 16 different part slots that the user
  778. //can choose from. Each slot can have a part name and the time (in minutes) that that part has
  779. //been ridden for. If the toggle_button is pressed while on the selected part, then the
  780. //handleLCD_add_parts_scene function will be called. After the add_parts_scene function, then the
  781. //user is taken back to handleLCD_display_parts_scene and the new part and new time are read from
  782. //EEPROM and displayed on the LCD screen. The user can exit the function when the toggle_button
  783. //is pressed when the LCD displays "Exit".
  784. void handleLCD_display_parts_scene()
  785. {
  786.     uint16_t part_time_spent = 0;
  787.     uint8_t display_parts_menu_selection;
  788.     uint8_t part_name_from_eeprom;
  789.     uint8_t empty_part_detection = 0;
  790.    
  791.     lcd_clear(0, 0, 16);
  792.    
  793.     do
  794.     {
  795.         display_parts_menu_selection = (uint8_t)map(adc_data[1], 0, 4096, 0, 17);
  796.            
  797.         if(display_parts_menu_selection == 16) //display the exit scene
  798.         {
  799.             lcd_clear(1,0,16);
  800.             lcd_set_cursor(0,4);
  801.             lcd_write_char(' ');   
  802.             lcd_write_char(' ');   
  803.             lcd_write_char('E');
  804.             lcd_write_char('X');
  805.             lcd_write_char('I');
  806.             lcd_write_char('T');
  807.             lcd_write_char(' ');   
  808.             lcd_write_char(' ');   
  809.         }
  810.         else
  811.         {
  812.             lcd_set_cursor(0,4);
  813.             lcd_write_char('P');
  814.             lcd_write_char('A');
  815.             lcd_write_char('R');
  816.             lcd_write_char('T');
  817.             lcd_write_char(' ');
  818.            
  819.             if(display_parts_menu_selection < 9)
  820.                 lcd_write_char('0');
  821.             lcd_write_number(display_parts_menu_selection+1);
  822.            
  823.             lcd_set_cursor(1,0);
  824.            
  825.             //This for loops displays the specified part from the user (it changes depending on the
  826.             //analog slider value which is stored in the variable display_parts_menu_selection)
  827.             for(uint8_t r = 0; r < 11; r++)
  828.             {
  829.                 //the part_name_from_eeprom equals the correct ASCII value stored in EEPROM
  830.                 part_name_from_eeprom = eeprom_read(EEPROM_ADDRESS, PART_NAME_ADDRESS_HI, ((16*display_parts_menu_selection)+(PART_NAME_ADDRESS_LO+r)));
  831.                
  832.                 //if the part_name_from_eeprom equals 32, then the character equals a space ' ' and if
  833.                 //all of the ASCII values in the part are spaces, then there is no part in that slot.
  834.                 if(part_name_from_eeprom == 32)
  835.                 {
  836.                     empty_part_detection++;
  837.                 }
  838.                 lcd_write_char(part_name_from_eeprom);
  839.             }
  840.             if(empty_part_detection != 11)
  841.             {
  842.                 //get the hi and lo bits from the part time address and put them into one 16 bit variable
  843.                 part_time_spent = eeprom_read(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*display_parts_menu_selection)+(PART_TIME_ADDRESS_LO)));
  844.                 part_time_spent = (uint16_t)(part_time_spent<<8);
  845.                 part_time_spent |= eeprom_read(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*display_parts_menu_selection)+(PART_TIME_ADDRESS_LO+1)));
  846.            
  847.                 lcd_set_cursor(1,12);
  848.                 if(part_time_spent < 10)
  849.                 {
  850.                     lcd_write_char(' ');
  851.                     lcd_write_char(' ');
  852.                     lcd_write_char(' ');
  853.                     lcd_write_number(part_time_spent);
  854.                 }
  855.                 else if(part_time_spent < 100)
  856.                 {
  857.                     lcd_write_char(' ');
  858.                     lcd_write_char(' ');
  859.                     lcd_write_number(part_time_spent);
  860.                 }
  861.                 else if(part_time_spent < 1000)
  862.                 {
  863.                     lcd_write_char(' ');
  864.                     lcd_write_number(part_time_spent);
  865.                 }
  866.                 else
  867.                 {
  868.                     lcd_write_number(part_time_spent);
  869.                 }
  870.             }
  871.             else //if there is no part, than the LCD should not display any time
  872.             {
  873.                 lcd_set_cursor(1,12);
  874.                 lcd_write_char(' ');
  875.                 lcd_write_char(' ');
  876.                 lcd_write_char(' ');
  877.                 lcd_write_char(' ');
  878.             }
  879.             empty_part_detection = 0;
  880.         }
  881.            
  882.     }while(toggle_button_val != 1);
  883.    
  884.     toggle_button_val = 0;     
  885.  
  886.     if(display_parts_menu_selection != 16) //the handleLCD_add_part_scene will only be called when the user
  887.                                                                                  //presses the toggle button while NOT on the exit scene
  888.     {  
  889.         handleLCD_add_part_scene(display_parts_menu_selection);
  890.     }
  891.        
  892. }
  893.  
  894. //The handleLCD_add_part_scene lets the user choose the name
  895. //of their part and the time it has already been in use (most of the time it will be 0)
  896. void handleLCD_add_part_scene(uint8_t part_number)
  897. {
  898.     uint8_t part_name[11];
  899.     uint8_t num_array[4]; //used for the time spent display
  900.     uint16_t time_spent = 0; //value used to put into eeprom
  901.     uint8_t empty_part_detection = 0;
  902.    
  903.     lcd_clear(1,0, 16);
  904.     lcd_set_cursor(0,0);
  905.     lcd_write_char('N');
  906.     lcd_write_char('A');
  907.     lcd_write_char('M');
  908.     lcd_write_char('E');
  909.     lcd_write_char(':');
  910.     lcd_write_char('_');
  911.     lcd_write_char('_');
  912.     lcd_write_char('_');
  913.     lcd_write_char('_');
  914.     lcd_write_char('_');
  915.     lcd_write_char('_');
  916.     lcd_write_char('_');
  917.     lcd_write_char('_');
  918.     lcd_write_char('_');
  919.     lcd_write_char('_');
  920.     lcd_write_char('_');
  921.    
  922.    
  923.     //This foor loops cycles through the naming convention of the part (a total of 11 characters)
  924.     //and stores the ASCII value in the part_name[] array.
  925.     for(uint8_t n = 0; n < 11; n++)
  926.     {
  927.         while(toggle_button_val != 1)
  928.         {
  929.             lcd_set_cursor(0,n+5);
  930.             part_name[n] = (uint8_t)(map(adc_data[1], 0, 4096, 64, 91)); //64-91 is the ascii values for the alphabet in caps
  931.             if(part_name[n] == 64)
  932.             {
  933.                 part_name[n] = 32; //change the '@' to a ' '
  934.             }
  935.             lcd_write_char(part_name[n]);
  936.         }
  937.        
  938.         if(part_name[n] == 32) //if the part_name[n] is an empty space (32) then increment empty_part_detection
  939.         {                                            
  940.             empty_part_detection++;
  941.         }
  942.         toggle_button_val = 0;     
  943.        
  944.     }
  945.    
  946.    
  947.     if(empty_part_detection != 11) //if the part does not equal all empty spaces, then add a time
  948.                                                                  //time to that specific part slot. If not, then this code is skipped
  949.     {
  950.         parts_in_use[part_number] = 1; //loads a 1 in the parts_in_use[part_number] to indicate that
  951.                                                                      //the slot number of "part_number" is in use.
  952.        
  953.         lcd_clear(1,0, 16);
  954.         lcd_set_cursor(0,0);
  955.         lcd_write_char('T');
  956.         lcd_write_char('I');
  957.         lcd_write_char('M');
  958.         lcd_write_char('E');
  959.         lcd_write_char(' ');
  960.         lcd_write_char('S');
  961.         lcd_write_char('P');
  962.         lcd_write_char('E');
  963.         lcd_write_char('N');
  964.         lcd_write_char('T');
  965.         lcd_write_char(':');
  966.         lcd_write_char('_');
  967.         lcd_write_char('_');
  968.         lcd_write_char('_');
  969.         lcd_write_char('_');
  970.         lcd_write_char(' ');
  971.    
  972.         for(uint8_t t = 0; t < 4; t++)
  973.         {
  974.             while(toggle_button_val != 1)
  975.             {
  976.                 lcd_set_cursor(0,t+11);
  977.                 num_array[t] = (uint8_t)(map(adc_data[1], 0, 4096, 0, 10));
  978.                 lcd_write_number(num_array[t]);
  979.             }
  980.             toggle_button_val = 0;     
  981.         }
  982.     }
  983.  
  984.     empty_part_detection = 0; //resets the empty_part_detection counter
  985.    
  986.     lcd_clear(0, 0, 16);
  987.    
  988.    
  989.     //This for loop loads the EEPROM with the entire name of the specified part in the specified part slot.
  990.     for(uint8_t w = 0; w < 11; w++)
  991.     {
  992.         eeprom_write(EEPROM_ADDRESS, PART_NAME_ADDRESS_HI,((16*part_number)+(PART_NAME_ADDRESS_LO+w)), part_name[w]);
  993.     }
  994.    
  995.     time_spent = (num_array[0] * 1000) + (num_array[1] * 100) + (num_array[2] * 10) + (num_array[3]);
  996.    
  997.     //write the time that was spent on that part into the specified part slot in EEPROM.
  998.     eeprom_write(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*part_number)+(PART_TIME_ADDRESS_LO+1)), (time_spent&0x00FF));
  999.     eeprom_write(EEPROM_ADDRESS, PART_TIME_ADDRESS_HI, ((16*part_number)+(PART_TIME_ADDRESS_LO)), ((time_spent>>8)));
  1000.    
  1001.     handleLCD_display_parts_scene();
  1002.    
  1003. }
  1004.  
  1005. //The load_eeprom function just loads the EEPROM with ascii values that correlate to empty spaces ' '
  1006. //to indicate a fresh slate of part slots with no names or no times.
  1007. void load_eeprom()
  1008. {
  1009.     for(uint8_t i = 0; i < 16; i++)
  1010.     {
  1011.         for(uint8_t r = 0; r < 11; r++)
  1012.         {
  1013.             eeprom_write(EEPROM_ADDRESS, PART_NAME_ADDRESS_HI, ((16*i)+(PART_NAME_ADDRESS_LO+r)), 32);
  1014.         }
  1015.     }
  1016. }
  1017.  
  1018.  
  1019. //this function is called before main and loads the parts_in_use[] array with either a 1 or a 0,
  1020. //depending on if the part (read from EEPROM) has all empty spaces (this would indicate there is
  1021. //no part in that slot and parts_in_use[i] would equal 0. If the EEPROM has a valid ASCII character
  1022. //in the part slot, than this indicates a part name, which indicates the part is in use, which means the
  1023. //parts_in_use[i] will equal 1.
  1024. void load_parts_in_use()
  1025. {
  1026.     uint8_t check = 0;
  1027.     uint8_t empty_part_detection = 0;
  1028.    
  1029.     //This for loop iterates 16 times, which indicates the size of how many part slots there are
  1030.     for(uint8_t i = 0; i < 16; i++)
  1031.     {
  1032.         //this for loop iterates 11 times, which indicates the size of how big the name of the part can be
  1033.         for(uint8_t w = 0; w < 11; w++)
  1034.         {
  1035.             check = eeprom_read(EEPROM_ADDRESS, PART_NAME_ADDRESS_HI,((16*i)+(PART_NAME_ADDRESS_LO+w)));
  1036.             if(check == 32)
  1037.                 empty_part_detection++;
  1038.         }
  1039.         if(empty_part_detection != 11)
  1040.             parts_in_use[i] = 1;
  1041.     }
  1042. }
  1043.  
  1044.  
  1045. //The handleLCD_neopixel_scene function iterates over 11 different
  1046. //LED animations for the neopixel strip and sends this data to the
  1047. //skateboard so it can light up accordingly.
  1048. void handleLCD_neopixel_scene()
  1049. {
  1050.     uint8_t nrf_data[4] = {0, 0, 0, 0}; //even though I am only care about sending nrf_data[2]
  1051.                                                                             //which is the value of the selected LED animation,
  1052.                                                                           //I still have to send a packet of 4 because of the way
  1053.                                                                             //The skateboard handles the data.
  1054.     lcd_clear(0,0, 16);
  1055.     lcd_set_cursor(0,0);
  1056.     lcd_write_char('L');
  1057.     lcd_write_char('E');
  1058.     lcd_write_char('D');
  1059.     lcd_write_char(' ');
  1060.     lcd_write_char('A');
  1061.     lcd_write_char('N');
  1062.     lcd_write_char('I');
  1063.     lcd_write_char('M');
  1064.     lcd_write_char('A');
  1065.     lcd_write_char('T');
  1066.     lcd_write_char('I');
  1067.     lcd_write_char('O');
  1068.     lcd_write_char('N');
  1069.     lcd_write_char(':');
  1070.    
  1071.     do
  1072.     {
  1073.         //TX MODE
  1074.         nrf_data[2] =   (uint8_t)(map(adc_data[1], 0, 4096, 0, 11));
  1075.  
  1076.         lcd_set_cursor(0,14);
  1077.         if((nrf_data[2]+1) < 10)
  1078.         {
  1079.             lcd_write_char('0');
  1080.         }
  1081.         lcd_write_number(nrf_data[2]+1);
  1082.        
  1083.         if(nrf_send(nrf_data, 32))
  1084.         {
  1085.             //I had an LED toggling in here for debugging purposes initially
  1086.         }
  1087.  
  1088.        
  1089.     }while(toggle_button_val != 1);
  1090.    
  1091.     //Load the EEPROM with the LED animation so that the user does not have to
  1092.     //go to this scene every time he rides in order to get his desired animation.
  1093.     eeprom_write(EEPROM_ADDRESS, LED_ANIMATION_ADDR_HI, LED_ANIMATION_ADDR_LO, nrf_data[2]);
  1094.    
  1095.     toggle_button_val = 0;
  1096.    
  1097. }
  1098.  
  1099.  
Add Comment
Please, Sign In to add comment