📜 ⬆️ ⬇️

STM32, C ++ and FreeRTOS. Development from scratch. Part 3 (LCD and Screens)

Introduction


In the two previous parts of STM32, C ++ and FreeRTOS. Development from scratch. Part 1 and STM32, C ++ and FreeRTOS. Development from scratch. Part 2 I have already implemented the requirements of SR0, SR7, SR4 and SR6. Again, you need to remember what the general requirements are.
SR0: The device should measure three parameters (have three variables): microprocessor temperature , voltage VDDA , voltage from a variable resistor
SR1: The device should display the value of these variables on the indicator .
SR2: The unit of measurement for the microprocessor temperature is degrees Celsius, for the other parameters it is volts.
SR3: When you press button 1 , the display should show a screen with the next variable to be measured,
SR4: When you press button 1, LED 1 must change its state
SR5: When you press button 2 , the display should change the display mode of variables from constantly showing the variable to consecutive (changing screens every 1.5 seconds) when you next press from consecutive to constant,
SR6: When you press button 2, LED 2 should change its state.
SR7: LED 3 should blink once every 1 second.

This means that the most “tasty” requirements related to the display of all measured information on the indicator have remained: SR1, SR2, SR3, SR5. Well, let's start.

Development: indicator driver


I'll start with the indicator driver. I remember (all the same notorious project of 8 years ago), I already wrote a conclusion to the indicator and it didn’t cause any particular problems for me, but it was a simple microcontroller, and then a whole “processor” with a bunch of settings. It is good that there are many detailed articles on how to properly configure the indicator driver and others like it. For example, the publication of HallEffect Working with the LCD indicator on the STM32L-Discovery debug board , plus I dig into the source code of the demo project for the Olimex board, and of course I read the documentation in order to immediately understand all the "tricks" of modern microcontrollers. And it gave its fruits - I got acquainted with such a wonderful thing as Bit Banding, it is very accessible here: What is Bit Banding on the example of stm32 .

Having lifted technical skills, I sat down for the next drawing, to describe the class of the driver cLcdDriver. First, let's see how the indicator looks like:
')
image .

As you can see in it, 2 lines are small (top) and BIG bottom and a bunch of other segments that I need. I also decided to use only the bottom BIG line. It will be enough to display the value of variables and units of measurement. Well, we decided on where and what to display, and after drawing we got a class:

image

Now it's time to implement. To begin with, it was necessary to configure a bunch of ports, namely 47 :) for an alternative LCD function. But I am stubborn, I also had to switch the clocking of the LCD to the source from an external generator, because it didn’t work from the internal one when I debugged through it once. It was all shoved into __low_level_init ().

indicator setting in __low_level_init
/  LCD    ,   - //    RCC->CSR |= RCC_CSR_RTCRST; RCC->CSR &= ~RCC_CSR_RTCRST; RCC->CSR |= RCC_CSR_LSEON; while(!(RCC->CSR&RCC_CSR_LSERDY)) { } RCC->CSR |= RCC_CSR_RTCSEL_LSE; //   // PA.08  LCD COM0 //.08    .  174. CD00240194.pdf GPIOA->MODER |= GPIO_MODER_MODER8_1; //PA.08  LCD 0,  .189 CD00240194.pdf GPIOA->AFR[1]|= GPIO_AF_LCD; // PA.09  LCD COM1 //.09    .  174,189 CD00240194.pdf GPIOA->MODER |= GPIO_MODER_MODER9_1; GPIOA->AFR[1] |= GPIO_AF_LCD << PIN9_SHIFT; // PA.10  LCD COM2 //.10    .  174,189 CD00240194.pdf GPIOA->MODER |= GPIO_MODER_MODER10_1; GPIOA->AFR[1] |= GPIO_AF_LCD << PIN10_SHIFT; // PB.09  LCD COM3 //B.09    .  174,189 CD00240194.pdf GPIOB->MODER |= GPIO_MODER_MODER9_1; GPIOB->AFR[1] |= GPIO_AF_LCD << PIN9_SHIFT; // PA.01  LCD SEG0 //.01    .  174,189 CD00240194.pdf GPIOA->MODER |= GPIO_MODER_MODER1_1; GPIOA->AFR[0] |= GPIO_AF_LCD << PIN1_SHIFT; // PA.02  LCD SEG1 //.02    .  174189 CD00240194.pdf GPIOA->MODER |= GPIO_MODER_MODER2_1; GPIOA->AFR[0] |= GPIO_AF_LCD << PIN2_SHIFT; ... // PD.02  LCD SEG43 //D.02    .  174,189 CD00240194.pdf GPIOD->MODER |= GPIO_MODER_MODER2_1; GPIOD->AFR[0] |= GPIO_AF_LCD << PIN2_SHIFT; // LCD: DUTY = 1/4, BIAS=1/4, MUX_SEG = disable //VSEL = 0 (  ) //PRESCALLER= 1/2, DIVIDER = ck_ps/20, BLINK =0, BLINKF=0, CC=VLCD4, DEAD = 0 //PON = 3, UDDIE = 0,SOFIE = 0, HD = 0, .  377-378 CD00240194.pdf //DUTY 1/4 //   1/2,   20   378. CD00240194.pdf LCD->FCR = LCD_FCR_PS_1 | (LCD_FCR_DIV_0 | LCD_FCR_DIV_2) | (LCD_FCR_PON_0 | LCD_FCR_PON_1) | LCD_FCR_CC_2 | LCD_FCR_CC_1; //     FCR while (!(LCD->SR & LCD_SR_FCRSR )) { } LCD->CR |= (LCD_CR_DUTY_1 | LCD_CR_DUTY_0); //  LCD->CR |= LCD_CR_LCDEN; 


Go ahead, the indicator driver built into the microcontroller has its own RAM, you need to write to it, and then display it on the indicator at once. Here came to the rescue, and Bit Banding came, which allowed us to create an pTableSegs array of 166 elements (by the number of indicator segments) of the pointer to the addresses of tU32 cells - which are bits in RAM registers. Setting 1 or 0 to such a tU32 cell automatically sets or resets the bit in the RAM register to which it is mapped. The addresses of these cells are calculated using a sophisticated formula that I took from the documentation and the demo project.

 /  BitBanding .  49. CD00240194.pdf #define SEG_MASK(seg) (seg & (32-1)) #define SEG_EL(seg,com) (volatile tU32 *)(PERIPH_BASE + 0x2000000 + ((0x2400 + 0x14 + ((com*2) + ((seg<32)?0:1))*4)*32) + SEG_MASK(seg)*4)) //    bitBandinga    volatile tU32* cLcdDriver::pTableSegs[] = { SEG_EL(39,3), // + 0 SEG_EL(39,0), // - 1 SEG_EL(37,3), // 1A 2 SEG_EL(37,2), // 1B 3 SEG_EL(37,1), // 1C 4 SEG_EL(37,0), // 1D 5 SEG_EL(39,1), // 1E 6 SEG_EL(39,2), // 1F 7 ... SEG_EL(5 ,1), // 11F 165 SEG_EL(4 ,1), // 11G 166 }; 


Then everything was already a matter of technology. The BIG LINE segments are composed like this:

 //     ()  // _______a_______ // |\ | /| // fhjkb |col // | \ | / | // |___g__\ /__m___| // | / \ | | // eqpnc // | / | \ | // |/______d______\| |dp #define SEG_A ((tU32)1<<0) #define SEG_B ((tU32)1<<1) #define SEG_C ((tU32)1<<2) ... #define SEG_DP ((tU32)1<<14) #define SEG_COL ((tU32)1<<15) 

Accordingly, in order to derive, say, the letter C, you need to light a, d, e, f segments, so C looks like this:

 #define Symbol_C (SEG_A | SEG_D | SEG_E | SEG_F) 

A little more had to tinker with a dot to bring it to the same place where the figure is displayed. Well, in general, after quite a long and tedious work, such a footwoman came out:

lcddriver.h
 #include "types.h" //   tU32, tBoolean class cLcdDriver { public: explicit cLcdDriver(void); void showBigString(const char* pStr); private: void updateDisplay(void); tBoolean isReady(void); void showBigSymbol(const tU32 digitPlace, const char character, const tBoolean bDot); static volatile tU32* pTableSegs[]; static const tU32 charToLcdSymbol[]; static const tU32 bigDigitOffset[]; }; 


lcddriver.cpp
 #include "lcddriver.h" //   #include <stm32l1xx.h> // STM32 #include "susuassert.h" // ASSERT #include "types.h" //  tPort, tU16, tU8 #include "bitutil.h" //     SETBIT, CLRBIT #include <stddef.h> // NULL #define BIG_SYMBOLS_COUNT 7 //   ()  //  BitBanding .  49. CD00240194.pdf //   : https://plus.google.com/115316880241890152471/posts/M7tzhpQiC9M #define SEG_MASK(seg) (seg & (32-1)) #define SEG_EL(seg,com) (volatile tU32 *)(PERIPH_BASE + 0x2000000 + ((0x2400 + 0x14 + ((com*2) + ((seg<32)?0:1))*4)*32) + (SEG_MASK(seg)*4)) #define SEG_A ((tU32)1<<0) #define SEG_B ((tU32)1<<1) #define SEG_C ((tU32)1<<2) #define SEG_D ((tU32)1<<3) #define SEG_E ((tU32)1<<4) #define SEG_F ((tU32)1<<5) #define SEG_G ((tU32)1<<6) #define SEG_H ((tU32)1<<7) #define SEG_J ((tU32)1<<8) #define SEG_K ((tU32)1<<9) #define SEG_M ((tU32)1<<10) #define SEG_N ((tU32)1<<11) #define SEG_P ((tU32)1<<12) #define SEG_Q ((tU32)1<<13) #define SEG_DP ((tU32)1<<14) #define SEG_COL ((tU32)1<<15) //     ()  // _______a_______ // |\ | /| // fhjkb |col // | \ | / | // |___g__\ /__m___| // | / \ | | // eqpnc // | / | \ | // |/______d______\| |dp #define Symbol_20 (tU32)0 #define Symbol_21 (tU32)0 #define Symbol_22 (tU32)0 #define Symbol_23 (tU32)0 #define Symbol_24 (tU32)0 #define Symbol_25 (tU32)0 #define Symbol_26 (tU32)0 #define Symbol_27 (tU32)0 #define Symbol_28 (tU32)0 #define Symbol_29 (tU32)0 #define Symbol_2A (tU32)0 #define Symbol_2B (SEG_J | SEG_M | SEG_P | SEG_G) // '+' #define Symbol_2C (SEG_DP) // ',' #define Symbol_2D (SEG_J | SEG_M) // '-' #define Symbol_2E (SEG_DP) // '.' #define Symbol_2F (SEG_K | SEG_Q) // '/' #define Digit_0 (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_H | SEG_N) #define Digit_1 (SEG_B | SEG_C) #define Digit_2 (SEG_A | SEG_B | SEG_G | SEG_E | SEG_D | SEG_M) #define Digit_3 (SEG_A | SEG_B | SEG_C | SEG_D | SEG_G | SEG_M) #define Digit_4 (SEG_F | SEG_B | SEG_C | SEG_G | SEG_M) #define Digit_5 (SEG_A | SEG_F | SEG_G | SEG_C | SEG_D | SEG_M) #define Digit_6 (SEG_A | SEG_F | SEG_G | SEG_C | SEG_D | SEG_E | SEG_M) #define Digit_7 (SEG_A | SEG_B | SEG_C) #define Digit_8 (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_M) #define Digit_9 (SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G | SEG_M) #define Symbol_3A (SEG_DP) //  ':' #define Symbol_3B (SEG_COL) //  ';' #define Symbol_3C (SEG_K | SEG_N) //  '<' #define Symbol_3D (SEG_A | SEG_G | SEG_M) // '=' #define Symbol_3E (SEG_H | SEG_Q) // '>' #define Symbol_3F (tU32)0 // '?'     :) #define Symbol_40 (SEG_D | SEG_E | SEG_F | SEG_A | SEG_B | SEG_M| SEG_J) // '@' #define Symbol_A (SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G | SEG_M) #define Symbol_B (SEG_A | SEG_K | SEG_N | SEG_D | SEG_E | SEG_G | SEG_F) #define Symbol_C (SEG_A | SEG_D | SEG_E | SEG_F) #define Symbol_D (SEG_A | SEG_B | SEG_C | SEG_D | SEG_J | SEG_P) #define Symbol_E (SEG_A | SEG_G | SEG_M | SEG_D | SEG_E | SEG_F) #define Symbol_F (SEG_A | SEG_G | SEG_M | SEG_E | SEG_F) #define Symbol_G (SEG_A | SEG_N | SEG_D | SEG_E | SEG_F) #define Symbol_H (SEG_F | SEG_E | SEG_G | SEG_M | SEG_B | SEG_C) #define Symbol_I (SEG_G | SEG_P) #define Symbol_J (SEG_B | SEG_C | SEG_D) #define Symbol_K (SEG_F | SEG_E | SEG_G | SEG_K | SEG_N) #define Symbol_L (SEG_F | SEG_E | SEG_D) #define Symbol_M (SEG_E | SEG_F | SEG_H | SEG_K | SEG_B | SEG_C) #define Symbol_N (SEG_E | SEG_F | SEG_H | SEG_N | SEG_B | SEG_C) #define Symbol_O Symbol_D #define Symbol_P (SEG_E | SEG_F | SEG_A | SEG_B | SEG_M | SEG_G) #define Symbol_Q (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_N) #define Symbol_R (SEG_A | SEG_B | SEG_M | SEG_N | SEG_G | SEG_E | SEG_F) #define Symbol_S (SEG_A | SEG_F | SEG_G | SEG_M | SEG_C | SEG_D) #define Symbol_T (SEG_A | SEG_J | SEG_P) #define Symbol_U (SEG_F | SEG_E | SEG_D | SEG_C | SEG_B) #define Symbol_V (SEG_H | SEG_N | SEG_C | SEG_B) #define Symbol_W (SEG_F | SEG_E | SEG_Q | SEG_N | SEG_C | SEG_B) #define Symbol_X (SEG_H | SEG_Q | SEG_N | SEG_K) #define Symbol_Y (SEG_H | SEG_K | SEG_P) #define Symbol_Z (SEG_A | SEG_K | SEG_Q | SEG_D) #define BIG_SYMBOL_SIZE (16) //1  16 ,    // 1     2 (+  - ),    pTableSegs #define Big_Digit_0_offset (tU32)2 #define Big_Digit_1_offset (tU32)18 #define Big_Digit_2_offset (tU32)34 #define Big_Digit_3_offset (tU32)50 #define Big_Digit_4_offset (tU32)66 #define Big_Digit_5_offset (tU32)82 #define Big_Digit_6_offset (tU32)97 //   7   ,           . const tU32 cLcdDriver::bigDigitOffset[] = { Big_Digit_0_offset, Big_Digit_1_offset, Big_Digit_2_offset, Big_Digit_3_offset, Big_Digit_4_offset, Big_Digit_5_offset, Big_Digit_6_offset }; //  (ASCI   - ASCI  ' '())      const tU32 cLcdDriver::charToLcdSymbol[] = { Symbol_20, Symbol_21, Symbol_22, Symbol_23, Symbol_24, Symbol_25, Symbol_27, Symbol_27, Symbol_28, Symbol_29, Symbol_2A, Symbol_2B, Symbol_2C, Symbol_2D, Symbol_2E, Symbol_2F, Digit_0, Digit_1, Digit_2, Digit_3, Digit_4, Digit_5, Digit_6, Digit_7, Digit_8, Digit_9, Symbol_3A, Symbol_3B, Symbol_3C, Symbol_3D, Symbol_3D, Symbol_3F, Symbol_40, Symbol_A, Symbol_B, Symbol_C, Symbol_D, Symbol_E, Symbol_F, Symbol_G, Symbol_H, Symbol_I, Symbol_J, Symbol_K, Symbol_L, Symbol_M, Symbol_N, Symbol_O, Symbol_P, Symbol_Q, Symbol_R, Symbol_S, Symbol_T, Symbol_U, Symbol_V, Symbol_W, Symbol_X, Symbol_Y, Symbol_Z }; //    bitBandinga    volatile tU32* cLcdDriver::pTableSegs[] = { SEG_EL(39,3), // + 0 SEG_EL(39,0), // - 1 SEG_EL(37,3), // 1A 2 SEG_EL(37,2), // 1B 3 SEG_EL(37,1), // 1C 4 SEG_EL(37,0), // 1D 5 SEG_EL(39,1), // 1E 6 SEG_EL(39,2), // 1F 7 SEG_EL(38,2), // 1G 8 SEG_EL(38,3), // 1H 9 SEG_EL(36,3), // 1J 10 SEG_EL(36,2), // 1K 11 SEG_EL(36,1), // 1M 12 SEG_EL(36,0), // 1N 13 SEG_EL(38,0), // 1P 14 SEG_EL(38,1), // 1Q 15 SEG_EL(35,0), // 1DP 16 SEG_EL(35,3), // 2COL 17 SEG_EL(33,3), // 2A 18 SEG_EL(33,2), // 2B 19 SEG_EL(33,1), // 2C 20 SEG_EL(33,0), // 2D 21 SEG_EL(35,1), // 2E 22 SEG_EL(35,2), // 2F 23 SEG_EL(34,2), // 2G 24 SEG_EL(34,3), // 2H 25 SEG_EL(32,3), // 2J 26 SEG_EL(32,2), // 2K 27 SEG_EL(32,1), // 2M 28 SEG_EL(32,0), // 2N 29 SEG_EL(34,0), // 2P 30 SEG_EL(34,1), // 2Q 31 SEG_EL(31,0), // 2DP 32 SEG_EL(31,3), // 3COL 33 SEG_EL(29,3), // 3A 34 SEG_EL(29,2), // 3B 35 SEG_EL(29,1), // 3C 36 SEG_EL(29,0), // 3D 37 SEG_EL(31,1), // 3E 38 SEG_EL(31,2), // 3F 39 SEG_EL(30,2), // 3G 40 SEG_EL(30,3), // 3H 41 SEG_EL(28,3), // 3J 42 SEG_EL(28,2), // 3K 43 SEG_EL(28,1), // 3M 44 SEG_EL(28,0), // 3N 45 SEG_EL(30,0), // 3P 46 SEG_EL(30,1), // 3Q 47 SEG_EL(27,0), // 3DP 48 SEG_EL(27,3), // 4COL 49 SEG_EL(25,3), // 4A 50 SEG_EL(25,2), // 4B 51 SEG_EL(25,1), // 4C 52 SEG_EL(25,0), // 4D 53 SEG_EL(27,1), // 4E 54 SEG_EL(27,2), // 4F 55 SEG_EL(26,2), // 4G 56 SEG_EL(26,3), // 4H 57 SEG_EL(24,3), // 4J 58 SEG_EL(24,2), // 4K 59 SEG_EL(24,1), // 4M 60 SEG_EL(24,0), // 4N 61 SEG_EL(26,0), // 4P 62 SEG_EL(26,1), // 4Q 63 SEG_EL(23,0), // 4DP 64 SEG_EL(23,3), // 5COL 65 SEG_EL(21,3), // 5A 66 SEG_EL(21,2), // 5B 67 SEG_EL(21,1), // 5C 68 SEG_EL(21,0), // 5D 69 SEG_EL(23,1), // 5E 70 SEG_EL(23,2), // 5F 71 SEG_EL(22,2), // 5G 72 SEG_EL(22,3), // 5H 73 SEG_EL(20,3), // 5J 74 SEG_EL(20,2), // 5K 75 SEG_EL(20,1), // 5M 76 SEG_EL(20,0), // 5N 77 SEG_EL(22,0), // 5P 78 SEG_EL(22,1), // 5Q 79 SEG_EL(19,0), // 5DP 80 SEG_EL(19,3), // 6COL 81 SEG_EL(17,3), // 6A 82 SEG_EL(17,2), // 6B 83 SEG_EL(17,1), // 6C 84 SEG_EL(17,0), // 6D 85 SEG_EL(19,1), // 6E 86 SEG_EL(19,2), // 6F 87 SEG_EL(18,2), // 6G 88 SEG_EL(18,3), // 6H 89 SEG_EL(16,3), // 6J 90 SEG_EL(16,2), // 6K 91 SEG_EL(16,1), // 6M 92 SEG_EL(16,0), // 6N 93 SEG_EL(18,0), // 6P 94 SEG_EL(18,1), // 6Q 95 SEG_EL(15,0), // 6DP 96 SEG_EL(13,3), // 7A 97 SEG_EL(13,2), // 7B 98 SEG_EL(13,1), // 7C 99 SEG_EL(13,0), // 7D 100 SEG_EL(15,1), // 7E 101 SEG_EL(15,2), // 7F 102 SEG_EL(14,2), // 7G 103 SEG_EL(14,3), // 7H 104 SEG_EL(12,3), // 7J 105 SEG_EL(12,2), // 7K 106 SEG_EL(12,1), // 7M 107 SEG_EL(12,0), // 7N 108 SEG_EL(14,0), // 7P 109 SEG_EL(14,1), // 7Q 110 SEG_EL(1 ,3), // A1 111 SEG_EL(1 ,2), // A2 112 SEG_EL(1 ,1), // A3 113 SEG_EL(1 ,0), // A4 114 SEG_EL(2 ,0), // BRBL 115 SEG_EL(2 ,3), // B0 116 SEG_EL(2 ,2), // B1 117 SEG_EL(2 ,1), // B2 118 SEG_EL(0 ,3), // PL 119 SEG_EL(0 ,2), // P0 120 SEG_EL(0 ,1), // P1 121 SEG_EL(0 ,0), // P2 122 SEG_EL(43,0), // P3 123 SEG_EL(43,1), // P4 124 SEG_EL(43,2), // P5 125 SEG_EL(43,3), // P6 126 SEG_EL(42,3), // P7 127 SEG_EL(42,2), // P8 128 SEG_EL(42,1), // P9 129 SEG_EL(42,0), // PR 130 SEG_EL(3 ,0), // AL 131 SEG_EL(3 ,1), // AU 132 SEG_EL(3 ,2), // AR 133 SEG_EL(3 ,3), // AD 134 SEG_EL(15,3), // SB 135 SEG_EL(10,0), // 8A 136 SEG_EL(10,1), // 8B 137 SEG_EL(10,2), // 8C 138 SEG_EL(11,3), // 8D 139 SEG_EL(11,2), // 8E 140 SEG_EL(11,0), // 8F 141 SEG_EL(11,1), // 8G 142 SEG_EL(10,3), // 8P 143 SEG_EL(8 ,0), // 9A 144 SEG_EL(8 ,1), // 9B 145 SEG_EL(8 ,2), // 9C 146 SEG_EL(9 ,3), // 9D 147 SEG_EL(9 ,2), // 9E 148 SEG_EL(9 ,0), // 9F 149 SEG_EL(9 ,1), // 9G 150 SEG_EL(8 ,3), // 10P 151 SEG_EL(7 ,3), // 10COLON 152 SEG_EL(7 ,0), // 10A 153 SEG_EL(6 ,0), // 10B 154 SEG_EL(6 ,2), // 10C 155 SEG_EL(6 ,3), // 10D 156 SEG_EL(7 ,2), // 10E 157 SEG_EL(7 ,1), // 10F 158 SEG_EL(6 ,1), // 10G 159 SEG_EL(5 ,0), // 11A 160 SEG_EL(4 ,0), // 11B 161 SEG_EL(4 ,2), // 11C 162 SEG_EL(4 ,3), // 11D 163 SEG_EL(5 ,2), // 11E 164 SEG_EL(5 ,1), // 11F 165 SEG_EL(4 ,1), // 11G 166 }; /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ cLcdDriver::cLcdDriver(void) { this->updateDisplay(); } /******************************************************************************* * Function: showBigString * Description:    ()  . ******************************************************************************/ void cLcdDriver::showBigString(const char* pStr) { tU32 digitPlace = 0; tBoolean bDot = FALSE; //   const char *pNextChar = pStr; pNextChar++; //  RAM    //  ,       //       TRUE //    while (digitPlace < BIG_SYMBOLS_COUNT) { if (( *pNextChar == '.' ) || (*pNextChar == ',')) { bDot = TRUE; } if ((*pStr != '.') && (*pStr != ',')) { if (*pStr != NULL) { this->showBigSymbol(digitPlace, *pStr, bDot); } else { this->showBigSymbol(digitPlace, ' ', FALSE); } digitPlace++; } pStr++; pNextChar++; bDot = FALSE; } //   this->updateDisplay(); } /******************************************************************************* * Function: showBigSymbol * Description:  ( )    , *      ******************************************************************************/ void cLcdDriver::showBigSymbol(const tU32 digitPlace, const char character, const tBoolean bDot) { ASSERT(character > 0); ASSERT(character < 61); volatile tU32 **p_data = &this->pTableSegs[this->bigDigitOffset[digitPlace]]; tU32 mask = charToLcdSymbol[character - ' ']; //   ,     if (bDot == TRUE) { mask |= SEG_DP; } //      LCD->RAM   for(tU32 i = 0, j = 1; i < BIG_SYMBOL_SIZE; i++, j <<= 1) { if(mask & j) { **p_data = 1; } else { **p_data = 0; } ++p_data; } } /******************************************************************************* * Function: isReady * Description:    ******************************************************************************/ tBoolean cLcdDriver::isReady(void) { tBoolean result = FALSE; if (!CHECK_BITS_SET(LCD->SR,LCD_SR_UDR)) { result = TRUE; } return result; } /******************************************************************************* * Function: updateDisplay * Description:     ,    *    LCD ******************************************************************************/ void cLcdDriver::updateDisplay(void) { SETBIT(LCD->SR, LCD_SR_UDR); } 


I checked it simply by creating directly the indicator driver object in the main () function like this:

 cLcdDriver *pLcdDriver = new cLcdDriver(); pLcdDriver->showBigString("HELLO"); 


Development: Logic of displaying information on the indicator


Well, that's all done with the driver. It's time to start the logic of displaying information on the indicator. I decided to think a little and not to rush right away to make an active class, but to think about how to form screens. Since we have 3 different variables that should be displayed differently, there should be three different classes of screens for each of the variables. But I wanted to manage them as one. Therefore, it was necessary to first draw a single interface for all screens. All screens should at least have access to the indicator driver and variables. The driver here is the cLcdDriver class, and all the variables are in the cVariableDirector container, and the screen should be able to draw itself. And now we draw:

image

It's time to draw heirs to display the Temperature, Vdda and Trimmer screens. They just have to implement a single virtual show () method, and therefore everything looks very trivial:

image

When implementing, I chose to convert tF32 to a string, and decided not to bathe, I used an old acquaintance sprintf, I could write a utility class for conversion, but I didn’t, and therefore the implementation looks like this:

iscreen.h
 #include "types.h" //   #include "lcddriver.h" // cLcdDriver #include "variablesdirector.h" //  cVariableDirector class iScreen { public: explicit iScreen(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector); virtual void show(void) = 0; protected: cLcdDriver *pLcdDriver; const cVariablesDirector *pVariablesDirector; }; 


iscreen.cpp
 #include "iscreen.h" //   #include "susuassert.h" //  ASSERT /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ iScreen::iScreen(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector) { ASSERT(pLcdDriver != NULL); ASSERT(pVariablesDirector != NULL); this->pLcdDriver = pLcdDriver; this->pVariablesDirector = pVariablesDirector; } 


screentemperature.h
 #include "lcddriver.h" // cLcdDriver #include "variablesdirector.h" //  cVariableDirector #include "iscreen.h" // iScreen class cScreenTemperature : public iScreen { public: explicit cScreenTemperature(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector); void show(void); }; 


screentemperature.cpp
 #include "screentemperature.h" //   #include "types.h" //    #include <stdio.h> //  sprintf /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ cScreenTemperature::cScreenTemperature(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector) : iScreen(pLcdDriver, pVariablesDirector) { } /******************************************************************************* * Function: show * Description:    ******************************************************************************/ void cScreenTemperature::show(void) { char str[10]; tF32 value = this->pVariablesDirector->pTemperature->getValue(); sprintf(str, "T %4.1f C", value); this->pLcdDriver->showBigString(str); } 


screentrimmer.h
 include "lcddriver.h" // cLcdDriver #include "variablesdirector.h" //  cVariableDirector #include "iscreen.h" // iScreen class cScreenTrimmer : public iScreen { public: explicit cScreenTrimmer(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector); void show(void); }; 


screentrimmer.cpp
 #include "screentrimmer.h" //   #include "types.h" //    #include <stdio.h> //  sprintf /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ cScreenTrimmer::cScreenTrimmer(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector) : iScreen(pLcdDriver, pVariablesDirector) { } /******************************************************************************* * Function: show * Description:       ******************************************************************************/ void cScreenTrimmer::show(void) { char str[10]; tF32 value = this->pVariablesDirector->pTrimmer->getValue(); sprintf(str, "P %3.2f V", value); this->pLcdDriver->showBigString(str); } 


Well, now we need a class to manage the entire farm family, and call it cScreenManager. Recall the requirements: SR3: When you press the button 1 , the screen should show the screen with the next variable to be measured. Yeah, we need the NextScreen () method. And the following requirement says: SR5: When you press button 2 , the indicator should change the mode of displaying variables from constantly showing the variable to sequential (changing screens every 1.5 seconds) the next time you press from sequential to constant. It is not difficult to guess that the desired method should be called NextMode () :)
In addition, this cScreenManager should create all types of screens (we have three cTemperatureScreen, cTrimmerScreen and VddaScreen), but must work with them through a single interface, therefore all created screens will be stored in the iScreen array * pScreen [SCREEN_NUM];

So, draw again for clarity:

image

And without departing from the cash register we realize:

screenmanager.h
 #include "types.h" //   #include "iscreen.h" // iScreen #define SCREEN_NUM (tU32)3 #define TEMPERATURE_SCREEN_ID (tU32)0 #define TRIMMER_SCREEN_ID (tU32)1 #define VDDA_SCREEN_ID (tU32)2 typedef enum { SM_single = 0, SM_sequence = 1 }tScreenMode; class cScreenManager { public: explicit cScreenManager(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector); void nextScreen(void); void nextMode(void); void show(void); private: iScreen *pScreen[SCREEN_NUM]; iScreen *pCurrentScreen; tU32 screenId; tScreenMode eMode; }; 


screenmanager.cpp
 #include "screenmanager.h" //   #include "screentemperature.h" // ScreenTemperature #include "screentrimmer.h" // ScreenTrimmer #include "screenvdda.h" // ScreenVdda #include "susuassert.h" //  ASSERT /******************************************************************************* * Function: constructor * Description:  3   ,   Vdda ******************************************************************************/ cScreenManager::cScreenManager(cLcdDriver *pLcdDriver, const cVariablesDirector *pVariablesDirector) { ASSERT(pLcdDriver != NULL); ASSERT(pVariablesDirector != NULL); this->pScreen[TEMPERATURE_SCREEN_ID] = (iScreen*)(new cScreenTemperature(pLcdDriver, pVariablesDirector)); this->pScreen[TRIMMER_SCREEN_ID] = (iScreen*)(new cScreenTrimmer(pLcdDriver, pVariablesDirector)); this->pScreen[VDDA_SCREEN_ID] = (iScreen*)(new cScreenVdda(pLcdDriver, pVariablesDirector)); this->screenId = TEMPERATURE_SCREEN_ID; this->pCurrentScreen = this->pScreen[this->screenId]; this->eMode = SM_single; } /******************************************************************************* * Function: show * Description:   ,           ******************************************************************************/ void cScreenManager::show(void) { switch (eMode) { case SM_single: this->pCurrentScreen->show(); break; case SM_sequence: this->pCurrentScreen->show(); this->nextScreen(); break; default: break; } } /******************************************************************************* * Function: nextScreen * Description:     ******************************************************************************/ void cScreenManager::nextScreen(void) { this->screenId ++; if (this->screenId >= SCREEN_NUM) { this->screenId = TEMPERATURE_SCREEN_ID; } this->pCurrentScreen = this->pScreen[this->screenId]; } /******************************************************************************* * Function: nextMode * Description:      ******************************************************************************/ void cScreenManager::nextMode(void) { if (this->eMode == SM_single) { this->eMode = SM_sequence; } else { this->eMode = SM_single; } } 


Well, the final finishing touch is to make the active object for periodically displaying information on the indicator:

image

And the implementation is generally simple and clear:

lcddirector.h
 #include "iactiveobject.h" //lint !e537   iActiveObject #include "lcddriver.h" //lint !e537  cLcdDriver #include "screenmanager.h" //lint !e537  cScreenManager #include "variablesdirector.h" //lint !e537  pVariableDirector class cLcdDirector : public iActiveObject { public: explicit cLcdDirector(const cVariablesDirector *pVariableDirector); virtual void run(void); private: cLcdDriver* pLcdDriver; cScreenManager *pScreenManager; }; 


 #include "lcddirector.h" //   #include "susuassert.h" //  ASSERT #include "types.h" //    #include "buttonscontroller.h" //  tButton #include <limits.h> //  ULONG_MAX #define LCD_DELAY (tU32) (1500/portTICK_PERIOD_MS) /******************************************************************************* * Function: constructor * Description:    cLcdDriver      *   cScreenManager,     Lcd ******************************************************************************/ cLcdDirector::cLcdDirector(const cVariablesDirector *pVariablesDirector) { ASSERT(pVariablesDirector != NULL); this->pLcdDriver = new cLcdDriver(); this->pScreenManager = new cScreenManager(this->pLcdDriver, pVariablesDirector); } /******************************************************************************* * Function: run * Description:     .     *     ,    . ******************************************************************************/ void cLcdDirector::run(void) { tU32 button = (tU32) 0; tBoolean status = FALSE; tButtons eButton = BT_none; for(;;) { status = (tBoolean)oRTOS.taskNotifyWait((tU32)0, (tU32)ULONG_MAX, &button, LCD_DELAY); if (status == TRUE) //lint !e731     { eButton = (tButtons)button; switch (eButton) { case BT_button1: this->pScreenManager->nextScreen(); break; case BT_button2: this->pScreenManager->nextMode(); break; case BT_none: break; default: break; } } this->pScreenManager->show(); } } 

And here it is the result of a 4 week trial with an AWP controller:



When finished, I cleaned the project with a lint, he found a lot of things, here’s an example of an error found:
Info 750: local macro 'Symbol_26' (line 65, file AHardware \ Lcd \ lcddriver.cpp) not referenced
Info 750: local macro 'Symbol_3E' (line 89, file AHardware \ Lcd \ lcddriver.cpp) not referenced

But the truth is, here in this array of the lcddriver.cpp file, the copy-paste worked and the two characters just disappeared, maybe it would have been discovered during the testing, but maybe not, but the lint is a good thing.

// conversion table (ASCI letter code - ASCI code "(space)) to a value on the indicator segments
const tU32 cLcdDriver :: charToLcdSymbol [] =
{
Symbol_20,
Symbol_21, Symbol_22, Symbol_23, Symbol_24, Symbol_25,
Symbol_27 , Symbol_27, Symbol_28, Symbol_29, Symbol_2A,
Symbol_2B, Symbol_2C, Symbol_2D, Symbol_2E, Symbol_2F,
Digit_0, Digit_1, Digit_2, Digit_3, Digit_4,
Digit_5, Digit_6, Digit_7, Digit_8, Digit_9,
Symbol_3A, Symbol_3B, Symbol_3C, Symbol_3D,
Symbol_3D , Symbol_3F, Symbol_40, Symbol_A,
Symbol_B, Symbol_C, Symbol_D, Symbol_E, Symbol_F,
Symbol_G, Symbol_H, Symbol_I, Symbol_J, Symbol_K,
Symbol_L, Symbol_M, Symbol_N, Symbol_O, Symbol_P,
Symbol_Q, Symbol_R, Symbol_S, Symbol_T, Symbol_U,
Symbol_V, Symbol_W, Symbol_X, Symbol_Y, Symbol_Z
};

Well, that's all for now. All the requirements set for me (me for me) are implemented, the RTOS is used, without a single semaphore and critical section (except for synchronization by notification, no additional “complicated things”, the rest of the data is taken atomically and does not require blocking).

My personal observation and opinion - in 8 years microprocessors have stepped forward. Programming is more and more like a high-level, many new useful blocks have appeared, and I think that students will like it a lot.

Source: https://habr.com/ru/post/261837/


All Articles