常常在手持量測工具的開發中,會使用到LCD來顯示量測資訊,如電壓等,在這邊就簡單的介紹我之前用來開發某量測工具使用LCD的控制方式!
由於並非所有STM32 MCU都有LCD顯示的功能,因此這邊選擇STM32L100C6U6A為例,假設要來點亮88.8V這樣字符的LCD,88.8V需要23個點,假設選擇4COMx6SEG共有24 pixels可以滿足23個點的需求,所以會占用STM32L100C6U6A 10 pin I/O(4COM
+ 6SEG),LCD設計圖如下:
#define
LCD_SEG0_Pin GPIO_Pin_1
#define
LCD_SEG0_GPIO_Port GPIOA
#define
LCD_SEG1_Pin GPIO_Pin_2
#define
LCD_SEG1_GPIO_Port GPIOA
#define
LCD_SEG2_Pin GPIO_Pin_3
#define
LCD_SEG2_GPIO_Port GPIOA
#define
LCD_SEG3_Pin GPIO_Pin_6
#define
LCD_SEG3_GPIO_Port GPIOA
#define
LCD_SEG4_Pin GPIO_Pin_7
#define
LCD_SEG4_GPIO_Port GPIOA
#define
LCD_SEG5_Pin GPIO_Pin_0
#define
LCD_SEG5_GPIO_Port GPIOB
#define
LCD_COM0_Pin GPIO_Pin_8
#define
LCD_COM0_GPIO_Port GPIOA
#define
LCD_COM1_Pin GPIO_Pin_9
#define
LCD_COM1_GPIO_Port GPIOA
#define
LCD_COM2_Pin GPIO_Pin_10
#define
LCD_COM2_GPIO_Port GPIOA
#define
LCD_COM3_Pin GPIO_Pin_9
#define
LCD_COM3_GPIO_Port GPIOB
void
LCD_Config(void)
{
LCD_InitTypeDef
LCD_InitStructure;
GPIO_InitTypeDef
GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA
| RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource8, GPIO_AF_LCD); // com0
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource9, GPIO_AF_LCD); // com1
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource10, GPIO_AF_LCD); // com2
GPIO_PinAFConfig(GPIOB,
GPIO_PinSource9, GPIO_AF_LCD); // com3
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource1, GPIO_AF_LCD); // seg0
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource2, GPIO_AF_LCD); //
seg1
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource3, GPIO_AF_LCD); // seg2
GPIO_PinAFConfig(GPIOA,
GPIO_PinSource6, GPIO_AF_LCD); // seg3
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7,
GPIO_AF_LCD); // seg4
GPIO_PinAFConfig(GPIOB,
GPIO_PinSource0, GPIO_AF_LCD); // seg5
GPIO_InitStructure.GPIO_Pin
= LCD_SEG0_Pin | LCD_SEG1_Pin |
LCD_SEG2_Pin | LCD_SEG3_Pin | LCD_SEG4_Pin
| LCD_COM0_Pin |
LCD_COM1_Pin | LCD_COM2_Pin;
GPIO_InitStructure.GPIO_Mode
= GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType
= GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed
= GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_PuPd
= GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,
&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin
= LCD_SEG5_Pin | LCD_COM3_Pin;
GPIO_InitStructure.GPIO_Mode
= GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType
= GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed
= GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_PuPd
= GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB,
&GPIO_InitStructure);
/*!< Configure the LCD interface */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_LCD,
ENABLE);
LCD_BlinkConfig(LCD_BlinkMode_Off,
LCD_BlinkFrequency_Div8);
LCD_InitStructure.LCD_Prescaler =
LCD_Prescaler_4;
LCD_InitStructure.LCD_Divider =
LCD_Divider_16;
LCD_InitStructure.LCD_Duty = LCD_Duty_1_4;
}
由於一個com會對應到44 seg,一個com對seg共需要2個32 bit(S00~S43,其餘空白)的大小來存放顯示資料,但LCD圖面只用到4COMx6SEG,因此只須宣告一個32bit變數來對應S00~S31,由於使用了4COM所以必須宣告四個變數如下:
uint32_t
LCD_COM0_SEGx = 0;
uint32_t
LCD_COM1_SEGx = 0;
uint32_t
LCD_COM2_SEGx = 0;
uint32_t
LCD_COM3_SEGx = 0;
LCD register
map and reset values
#define
COMx_S00 0x00000001 << 0
#define
COMx_S01 0X00000001 <<
1
#define
COMx_S02 0X00000001 <<
2
#define
COMx_S03 0X00000001 <<
3
#define
COMx_S04 0X00000001 <<
4
#define
COMx_S05 0X00000001 <<
5
#define
COM0_S0_S31 0 // COM0-S0~S31
#define
COM1_S0_S31 2 // COM1-S0~S31
#define
COM2_S0_S31 4 // COM2-S0~S31
#define
COM3_S0_S31 6 // COM3-S0~S31
enum
{
CHAR_0 = 0,
CHAR_1 = 1,
CHAR_2 = 2,
CHAR_3
= 3,
CHAR_4 = 4,
CHAR_5 = 5,
CHAR_6 = 6,
CHAR_7 = 7,
CHAR_8 = 8,
CHAR_9 = 9,
CHAR_A = 10,
CHAR_a = 10,
CHAR_B = 11,
CHAR_b = 11,
CHAR_C = 12,
CHAR_c = 12,
CHAR_D = 13,
CHAR_d = 13,
CHAR_E = 14,
CHAR_e = 14,
CHAR_F = 15,
CHAR_f = 15,
CHAR_H = 16,
CHAR_h = 16,
CHAR_I = 17,
CHAR_i = 17,
CHAR_J = 18,
CHAR_j = 18,
CHAR_L = 19,
CHAR_l = 19,
CHAR_N = 20,
CHAR_n = 20,
CHAR_O = 21,
CHAR_o = 21,
CHAR_P = 22,
CHAR_p = 22,
CHAR_R = 23,
CHAR_r = 23,
CHAR_S = 24,
CHAR_s = 24,
CHAR_T = 25,
CHAR_t = 25,
CHAR_U = 26,
CHAR_u = 26,
CHAR_Y = 27,
CHAR_y = 27,
CHAR_DESH = 28,
CHAR_NULL = 29
};
#define
DIG1 0
#define
DIG2 1
#define
DIG3 2
#define
DIG4 3
另外我會撰寫LCD的TABLE如下:
//
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, b, C, d, E, F, H, I, J, L, n, O, P, r, S, t,
U, Y, -, NULL
const
uint8_t LCD_Digit_Table[30] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F,0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x06, 0x0E, 0x38, 0x54,
0x3F, 0x73, 0x50, 0x6D, 0x78, 0X3e, 0x6E, 0x40, 0x00};
由於TABLE屬於常數資料,因此需宣告為const,另外再寫一個用來顯示控制的七結構變數,
union
LCD_Digit
{
uint8_t Byte;
struct
{
unsigned a : 1;
unsigned b : 1;
unsigned c : 1;
unsigned d : 1;
unsigned e : 1;
unsigned f : 1;
unsigned g : 1;
unsigned p_v : 1;
};
}Dig[3]
= {0, 0, 0};
void
LCD_COM0_SEGx_Update(void)
{
LCD_COM0_SEGx = 0;
if(Dig[0].d)
LCD_COM0_SEGx |= COMx_S00; // COM0-SEG0
if(Dig[2].d)
LCD_COM0_SEGx |= COMx_S02; // COM0-SEG2
if(Dig[3].p_v)
LCD_COM0_SEGx |= COMx_S03; // COM0-SEG3
if(Dig[4].d)
LCD_COM0_SEGx |= COMx_S04; // COM0-SEG4
if(Dig[5].p_v)
LCD_COM0_SEGx |= COMx_S05; // COM0-SEG5
return;
}
void
LCD_COM1_SEGx_Update(void)
{
LCD_COM0_SEGx = 0;
if(Dig[0].e)
LCD_COM0_SEGx |= COMx_S00; // COM1-SEG0
if(Dig[0].c)
LCD_COM0_SEGx |= COMx_S01; // COM1-SEG1
if(Dig[1].e)
LCD_COM0_SEGx |= COMx_S02; // COM1-SEG2
if(Dig[1].c)
LCD_COM0_SEGx |= COMx_S03; // COM1-SEG3
if(Dig[2].e)
LCD_COM0_SEGx |= COMx_S04; // COM1-SEG4
if(Dig[2].c)
LCD_COM0_SEGx |= COMx_S05; // COM1-SEG5
return;
}
void
LCD_COM2_SEGx_Update(void)
{
LCD_COM0_SEGx = 0;
if(Dig[0].g)
LCD_COM0_SEGx |= COMx_S00; // COM2-SEG0
if(Dig[0].b)
LCD_COM0_SEGx |= COMx_S01; // COM2-SEG1
if(Dig[1].g)
LCD_COM0_SEGx |= COMx_S02; // COM2-SEG2
if(Dig[1].b)
LCD_COM0_SEGx |= COMx_S03; // COM2-SEG3
if(Dig[2].g)
LCD_COM0_SEGx |= COMx_S04; // COM2-SEG4
if(Dig[2].b)
LCD_COM0_SEGx |= COMx_S05; // COM2-SEG5
return;
}
void
LCD_COM3_SEGx_Update(void)
{
LCD_COM0_SEGx = 0;
if(Dig[0].f)
LCD_COM0_SEGx |= COMx_S00; // COM3-SEG0
if(Dig[0].a)
LCD_COM0_SEGx |= COMx_S01; // COM3-SEG1
if(Dig[1].f)
LCD_COM0_SEGx |= COMx_S02; // COM3-SEG2
if(Dig[1].a)
LCD_COM0_SEGx |= COMx_S03; // COM3-SEG3
if(Dig[2].f)
LCD_COM0_SEGx |= COMx_S04; // COM3-SEG4
if(Dig[2].a)
LCD_COM0_SEGx |= COMx_S05; // COM3-SEG5
return;
}
void LCD_Digit_Display(uint8_t Digit, const unsigned char Table)
{
switch(Digit)
{
case 0 : Dig[0].Byte = Table; break;
case 1 : Dig[1].Byte = Table; break;
case 2 : Dig[2].Byte = Table; break;
case 3 : Dig[3].Byte = Table; break;
default : break;
}
return;
}
void
LCD_Char_Display(const uint8_t *ptr)
{
uint8_t cnt = 0;
uint8_t LCD_Digit[4] = {CHAR_NULL,
CHAR_NULL, CHAR_NULL, CHAR_NULL};
while(*ptr != 0x00)
{
switch(*ptr)
{
case '0':
LCD_Digit[cnt] = CHAR_0;
break;
case '1':
LCD_Digit[cnt]
= CHAR_1; break;
case '2':
LCD_Digit[cnt] = CHAR_2;
break;
case '3':
LCD_Digit[cnt] = CHAR_3;
break;
case '4':
LCD_Digit[cnt] = CHAR_4;
break;
case '5':
LCD_Digit[cnt] = CHAR_5;
break;
case '6':
LCD_Digit[cnt] = CHAR_6;
break;
case '7':
LCD_Digit[cnt] = CHAR_7;
break;
case '8':
LCD_Digit[cnt] = CHAR_8;
break;
case '9':
LCD_Digit[cnt] = CHAR_9; break;
case 'A':
case 'a': LCD_Digit[cnt] =
CHAR_A; break;
case 'B':
case 'b': LCD_Digit[cnt] =
CHAR_B; break;
case 'C':
case 'c': LCD_Digit[cnt] =
CHAR_C; break;
case 'D':
case 'd': LCD_Digit[cnt] =
CHAR_D; break;
case 'E':
case 'e': LCD_Digit[cnt] =
CHAR_E; break;
case 'F':
case 'f': LCD_Digit[cnt] =
CHAR_F; break;
case 'H':
case 'h': LCD_Digit[cnt] =
CHAR_H; break;
case 'I':
case 'i': LCD_Digit[cnt] =
CHAR_I; break;
case 'J':
case
'j': LCD_Digit[cnt] = CHAR_J; break;
case 'L':
case 'l': LCD_Digit[cnt] =
CHAR_L; break;
case 'N':
case 'n': LCD_Digit[cnt] =
CHAR_N; break;
case 'O':
case 'o': LCD_Digit[cnt] =
CHAR_O; break;
case 'P':
case 'p': LCD_Digit[cnt] =
CHAR_P; break;
case 'R':
case 'r': LCD_Digit[cnt] =
CHAR_R; break;
case 'S':
case 's': LCD_Digit[cnt] =
CHAR_S; break;
case 'T':
case 't': LCD_Digit[cnt] =
CHAR_T; break;
case 'U':
case 'u': LCD_Digit[cnt] =
CHAR_U; break;
case 'Y':
case 'y': LCD_Digit[cnt] =
CHAR_Y; break;
case '-': LCD_Digit[cnt] =
CHAR_DESH; break;
default : LCD_Digit[cnt] =
CHAR_NULL; break;
}
ptr++;
cnt++;
}
LCD_Digit_Display(DIG1,
LCD_Digit_Table[LCD_Digit[0]]);
LCD_Digit_Display(DIG2,
LCD_Digit_Table[LCD_Digit[1]]);
LCD_Digit_Display(DIG3,
LCD_Digit_Table[LCD_Digit[2]]);
LCD_Digit_Display(DIG4,
LCD_Digit_Table[LCD_Digit[3]]);
}
}
void
LCD_Integer_Display(int16_t Number)
{
uint8_t LCD_Digit[3] = {CHAR_NULL,
CHAR_NULL, CHAR_NULL, CHAR_NULL};
LCD_Digit[0] = Number / 100;
LCD_Digit[1] = (Number / 10) % 10;
LCD_Digit[2] = Number % 10;
if(LCD_Digit[0] == 0)
{
LCD_Digit[0] = CHAR_NULL;
}
else
LeadZero = 0;
LCD_Digit_Display(DIG1,
LCD_Digit_Table[LCD_Digit[0]]);
LCD_Digit_Display(DIG2,
LCD_Digit_Table[LCD_Digit[1]]);
LCD_Digit_Display(DIG3,
LCD_Digit_Table[LCD_Digit[2]]);
LCD_Digit_Display(DIG4,
LCD_Digit_Table[LCD_Digit[3]]);
}
void
LCD_Handler(void)
{
LCD_COM0_SEGx_Update();
LCD_COM1_SEGx_Update();
LCD_COM2_SEGx_Update();
LCD_COM3_SEGx_Update();
LCD_Write(LCD_RAMRegister_0,
LCD_COM0_SEGx); // COM0SEG0~COM0SEG43
LCD_Write(LCD_RAMRegister_2,
LCD_COM1_SEGx); // COM1SEG0~COM1SEG43
LCD_Write(LCD_RAMRegister_4,
LCD_COM2_SEGx); // COM2SEG0~COM2SEG43
LCD_Write(LCD_RAMRegister_6,
LCD_COM3_SEGx); // COM3SEG0~COM3SEG43
LCD_UpdateDisplayRequest();
/* Wait until the update display is finished
*/
while(LCD_GetFlagStatus(LCD_FLAG_UDD) ==
RESET)
{}
}
假設要顯示12.0V,請先將12.0放大10倍成120,然後套用以下程式,
LCD_Integer_Display(120);
Dig[DIG2].p_v
= 1; // “.”
Dig[DIG3].p_v
= 1; // “V”