Skip to content

Instantly share code, notes, and snippets.

@chrisgward
Last active February 19, 2019 03:24
Show Gist options
  • Save chrisgward/6af26dafa5bf6ed7313116e5ec11a2d3 to your computer and use it in GitHub Desktop.
Save chrisgward/6af26dafa5bf6ed7313116e5ec11a2d3 to your computer and use it in GitHub Desktop.
LCD library for STM32L476VG Discovery
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Chris Ward <chris@chrisgward.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some
* day, and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
.syntax unified
.section .rodata
// CCSSSSSS (CC = com bits, 0-3, SSSSSS = seg number (0-63))
.macro segment com seg
.byte \com << 6 | \seg
.endm
.global lcd_segmap
.global lcd_seg0
.global lcd_seg1
.global lcd_seg2
.global lcd_seg3
.global lcd_seg4
.global lcd_seg5
.global lcd_bars
lcd_segmap:
.align 4
lcd_seg0:
segment 1 22 // a
segment 0 22 // b
segment 1 23 // c
segment 1 4 // d
segment 0 4 // e
segment 1 3 // f
segment 0 3 // g1
segment 0 23 // g2
segment 3 3 // h
segment 3 22 // i
segment 2 22 // j
segment 2 3 // k
segment 2 4 // l
segment 3 4 // m
segment 2 23 // colon
segment 3 23 // dp
lcd_seg1:
segment 1 12 // a
segment 0 12 // b
segment 1 13 // c
segment 1 6 // d
segment 0 6 // e
segment 1 5 // f
segment 0 5 // g1
segment 0 13 // g2
segment 3 5 // h
segment 3 12 // i
segment 2 12 // j
segment 2 5 // k
segment 2 6 // l
segment 3 6 // m
segment 2 13 // colon
segment 3 13 // dp
lcd_seg2:
segment 1 28 // a
segment 0 28 // b
segment 1 29 // c
segment 1 15 // d
segment 0 15 // e
segment 1 14 // f
segment 0 14 // g1
segment 0 29// g2
segment 3 14 // h
segment 3 28 // i
segment 2 28 // j
segment 2 14 // k
segment 2 15 // l
segment 3 15 // m
segment 2 29 // colon
segment 3 29 // dp
lcd_seg3:
segment 1 32 // a
segment 0 32 // b
segment 1 33 // c
segment 1 31 // d
segment 0 31 // e
segment 1 30 // f
segment 0 30 // g1
segment 0 33 // g2
segment 3 30 // h
segment 3 32 // i
segment 2 32 // j
segment 2 30 // k
segment 2 31 // l
segment 3 31 // m
segment 2 33 // colon
segment 3 33 // dp
lcd_seg4:
segment 1 24 // a
segment 0 24 // b
segment 1 25 // c
segment 1 35 // d
segment 0 35 // e
segment 1 34 // f
segment 0 34 // g1
segment 0 25 // g2
segment 3 34// h
segment 3 24 // i
segment 2 24 // j
segment 2 34 // k
segment 2 35 // l
segment 3 35 // m
segment 0 0 // colon
segment 0 0 // dp
lcd_seg5:
segment 1 9 // a
segment 0 9 // b
segment 1 8 // c
segment 1 17 // d
segment 0 17 // e
segment 1 26 // f
segment 0 26 // g1
segment 0 8 // g2
segment 3 26 // h
segment 3 9 // i
segment 2 9 // j
segment 2 26 // k
segment 2 17 // l
segment 3 17 // m
segment 0 0 // colon
segment 0 0 // dp
lcd_bars:
segment 2 25
segment 3 25
segment 2 8
segment 3 8
lcd_font:
// ABCDEFGGHIJKLMCD
.hword 0b0000000000000000 // 20 (space)
.hword 0b1111111111111100 // 21 ! (light em up)
.hword 0b0100000001000000 // 22 "
.hword 0b0111001101001000 // 23 #
.hword 0b1011011101001000 // 24 $
// ABCDEFGGHIJKLMCD
.hword 0b0010011111111100 // 25 %
.hword 0b1001101011000100 // 26 &
.hword 0b0000000001000000 // 27 '
.hword 0b0000000000100100 // 28 (
.hword 0b0000000010010000 // 29 )
// ABCDEFGGHIJKLMCD
.hword 0b0000001111111100 // 2A *
.hword 0b0000001101001000 // 2B +
.hword 0b0000000000010000 // 2C ,
.hword 0b0000001100000000 // 2D -
// ABCDEFGGHIJKLMCD
.hword 0b0000000000000001 // 2E .
.hword 0b0000000000110000 // 2F /
.hword 0b1111110000000000 // 30 0
.hword 0b0110000000100000 // 31 1
.hword 0b1101101100000000 // 32 2
// ABCDEFGGHIJKLMCD
.hword 0b1111001100000000 // 33 3
.hword 0b0110011100000000 // 34 4
.hword 0b1011011100000000 // 35 5
.hword 0b1011111100000000 // 36 6
.hword 0b1110000000000000 // 37 7
// ABCDEFGGHIJKLMCD
.hword 0b1111111100000000 // 38 8
.hword 0b1111011100000000 // 39 9
.hword 0b0000000000000010 // 3A :
.hword 0b0000000001010000 // 3B ;
.hword 0b0000001000100100 // 3C <
// ABCDEFGGHIJKLMCD
.hword 0b0001001100000000 // 3D =
.hword 0b0000000110010000 // 3E >
.hword 0b1100010100001000 // 3F ?
.hword 0b1101110101000000 // 40 @
.hword 0b1110111100000000 // 41 A
// ABCDEFGGHIJKLMCD
.hword 0b1111000101001000 // 42 B
.hword 0b1001110000000000 // 43 C
.hword 0b1111000001001000 // 44 D
.hword 0b1001111000000000 // 45 E
.hword 0b1000111000000000 // 46 F
// ABCDEFGGHIJKLMCD
.hword 0b1011110100000000 // 47 G
.hword 0b0110111100000000 // 48 H
.hword 0b1001000001001000 // 49 I
.hword 0b0111100000000000 // 4A J
.hword 0b0000111000100100 // 4B K
// ABCDEFGGHIJKLMCD
.hword 0b0001110000000000 // 4C L
.hword 0b0110110010100000 // 4D M
.hword 0b0110110010000100 // 4E N
.hword 0b1111110000000000 // 4F O
.hword 0b1100111100000000 // 50 P
// ABCDEFGGHIJKLMCD
.hword 0b1111110000000100 // 51 Q
.hword 0b1100111100000100 // 52 R
.hword 0b1011011100000000 // 53 S
.hword 0b1000000001001000 // 54 T
.hword 0b0111110000000000 // 55 U
// ABCDEFGGHIJKLMCD
.hword 0b0000110000110000 // 56 V
.hword 0b0110110000010100 // 57 W
.hword 0b0000000010110100 // 58 X
.hword 0b0111011100000000 // 59 Y
.hword 0b1001000000110000 // 5A Z
// ABCDEFGGHIJKLMCD
.hword 0b1001110000000000 // 5B [
.hword 0b0000000010000100 // 5C backslash
.hword 0b1111000000000000 // 5D ]
.hword 0b0000000000010100 // 5E ^
.hword 0b0001000000000000 // 5F _
// ABCDEFGGHIJKLMCD
.hword 0b0000000010000000 // 60 `
.hword 0b0001101000001000 // 61 a
.hword 0b0011111100000000 // 62 b
.hword 0b0001101100000000 // 63 c
.hword 0b0111101100000000 // 64 d
// ABCDEFGGHIJKLMCD
.hword 0b0001101000010000 // 65 e
.hword 0b0000001100101000 // 66 f
.hword 0b0111000100100000 // 67 g
.hword 0b0010111100000000 // 68 h
.hword 0b0000000000001000 // 69 i
// ABCDEFGGHIJKLMCD
.hword 0b0000100001010000 // 6A j
.hword 0b0000111000100100 // 6B k
.hword 0b0000110000000000 // 6C l
.hword 0b0010101100001000 // 6D m
.hword 0b0010101100000000 // 6E n
// ABCDEFGGHIJKLMCD
.hword 0b0011101100000000 // 6F o
.hword 0b1100111100000000 // 70 p
.hword 0b1110011100000000 // 71 q
.hword 0b0000101100000000 // 72 r
.hword 0b1011011100000000 // 73 s
// ABCDEFGGHIJKLMCD
.hword 0b0001111000000000 // 74 t
.hword 0b0011100000000000 // 75 u
.hword 0b0000110000110000 // 76 v
.hword 0b0010100000010100 // 77 w
.hword 0b0000000010110100 // 78 x
// ABCDEFGGHIJKLMCD
.hword 0b0111000101000000 // 79 y
.hword 0b1001000000110000 // 7A z
.hword 0b1001001010010000 // 7B {
.hword 0b0000000001001000 // 7C |
.hword 0b1001000100100100 // 7D }
.hword 0b0010101000000100 // 7E -
// 7F DEL
.text
.macro lcd_gpio pin
ldr r1, [r0] // set alternate flag on pins
orr r1, 1<<(\pin*2+1)
bic r1, 1<<(\pin*2)
str r1, [r0]
.if \pin < 8 // afrh/l
ldr r1, [r0, 0x20]
.else
ldr r1, [r0, 0x24]
.endif
mov r2, 0xF << ((\pin % 8) * 4)
bic r1, r2 // clear all relevant bits
mov r2, 11 << ((\pin % 8) * 4)// set the bits for LCD
orr r1,r2
.if \pin < 8
str r1, [r0, 0x20] // store
.else
str r1, [r0, 0x24]
.endif
.endm
.global lcd_setup
.type lcd_setup,%function
lcd_setup:
push {r0-r2,lr}
ldr r0, =0x40021000 // RCC
ldr r1, [r0, 0x4C] // enable GPIO port clocks
orr r1, 0b1111 // ports A-D
str r1, [r0, 0x4C]
ldr r1, [r0, 0x58] // enable LCD (9) and PWR (28) clocks
orr r1, 1<<9
orr r1, 1<<28
str r1, [r0, 0x58]
ldr r1, [r0, 0x94] // enable LSI clock
orr r1, 1<<0
str r1, [r0, 0x94]
ldr r0, =#0x40007000 // PWR controller base
ldr r1, [r0]
orr r1, 1<<8 // unlock RTC/Backup registers
str r1, [r0]
ldr r0, =0x40021000 // RCC
ldr r1, [r0, 0x90]
orr r1, 1<<9
bic r1, 1<<8 // select LSI clock for LCD/RTC clock source
str r1, [r0, 0x90]
ldr r0, =#0x40007000 // PWR controller base
ldr r1, [r0]
bic r1, 1<<8 // lock RTC registers
str r1, [r0]
ldr r0,=0x48000000 // port a
lcd_gpio 6 // SEG23 SEG3
lcd_gpio 7 // SEG0 SEG4
lcd_gpio 8 // COM0 COM0
lcd_gpio 9 // COM1 COM1
lcd_gpio 10 // COM2 COM2
lcd_gpio 15 // SEG10 SEG17
ldr r0,=0x48000400 // pb
lcd_gpio 0 // SEG21 SEG5
lcd_gpio 1 // SEG2 SEG6
lcd_gpio 4 // SEG11 SEG8
lcd_gpio 5 // SEG12 SEG9
lcd_gpio 9 // COM3 COM3
lcd_gpio 12 // SEG20 SEG12
lcd_gpio 13 // SEG3 SEG13
lcd_gpio 14 // SEG19 SEG14
lcd_gpio 15 // SEG4 SEG15
ldr r0,=0x48000800 // pc
lcd_gpio 3 // VLCD VLCD
lcd_gpio 4 // SEG22 SEG22
lcd_gpio 5 // SEG1 SEG23
lcd_gpio 6 // SEG14 SEG24
lcd_gpio 7 // SEG9 SEG25
lcd_gpio 8 // SEG13 SEG26
ldr r0,=0x48000C00 // pd
lcd_gpio 8 // SEG18 SEG28
lcd_gpio 9 // SEG5 SEG29
lcd_gpio 10 // SEG17 SEG30
lcd_gpio 11 // SEG6 SEG31
lcd_gpio 12 // SEG16 SEG32
lcd_gpio 13 // SEG7 SEG33
lcd_gpio 14 // SEG15 SEG34
lcd_gpio 15 // SEG8 SEG35
ldr r1, =#0x40002400 // LCD base
ldr r2, [r1] @LCD Control reg
orr r2, #(0b011<<2) @ duty cycle bits
orr r2, #(0b01<<5) @Bias bits
str r2, [r1]
@frame rate & contrast setup
ldr r2, [r1,0x04] @Frame control reg
orr r2, #(0b0011<<22) @Framerate Prescaler (PS)
orr r2, #(0b0100<<18) @Framrate Divisor (DIV)
orr r2, #(0b000<<10) @constrast (CON)
orr r2, #(0b100<<4) @Pulse ON duration (PON)
str r2, [r1,0x04]
ldr r2, [r1] @LCD Control reg
orr r2, #0x01 @Enable the LCD Controller (LCDEN)
str r2, [r1]
bl lcd_wait_for_ready
bl lcd_clear
pop {r0-r2,lr}
bx lr
.size lcd_setup,.-lcd_setup
.global lcd_update
.type lcd_update,%function
lcd_update:
push {r0-r1,lr}
bl lcd_wait_for_ready
ldr r0, =0x40002400
ldr r1, [r0,0x08] @LCD status reg
orr r1, #(1<<2) @Update screen request (UDR) bit
str r1, [r0,0x08]
pop {r0-r1,lr}
bx lr
.size lcd_update,.-lcd_update
.global lcd_clear
.type lcd_clear,%function
lcd_clear:
push {r0,r1,lr}
ldr r0, =#0x40002400 // LCD base
ldr r1, =0
str r1, [r0,0x14] @com0 segment ram
str r1, [r0,0x18] @com0 segment ram
str r1, [r0,0x1c] @com1 segment ram
str r1, [r0,0x20] @com1 segment ram
str r1, [r0,0x24] @com2 segment ram
str r1, [r0,0x28] @com2 segment ram
str r1, [r0,0x2c] @com3 segment ram
str r1, [r0,0x30] @com3 segment ram
//bl lcd_update
pop {r0,r1,lr}
bx lr
.size lcd_clear,.-lcd_clear
.global lcd_wait_for_ready
.type lcd_wait_for_ready,%function
lcd_wait_for_ready:
push {r0,r1,r2}
lcd_wait_for_ready_loop:
ldr r0, =0x40002400
ldr r1, [r0, 0x8]
ands r1, 1<<2
bne lcd_wait_for_ready_loop
pop {r0,r1,r2}
bx lr
.size lcd_wait_for_ready,.-lcd_wait_for_ready
// r0 -> [CCSSSSSS]
.macro lcd_segment_op name op
.global lcd_\name\()_segment
.type lcd_\name\()_segment,%function
lcd_\name\()_segment:
push {r0-r5,lr}
ldr r0,[r0]
and r2,r0,0b111111 // seg
and r1,r0,3<<6
lsr r1,6 // com
ldr r3,=#0x40002414
cmp r2,32
blt lcd_set_segment_seg_lt_32\name\()
lcd_set_segment_seg_ge_32\name\():
sub r2,32
add r3,4
lcd_set_segment_seg_lt_32\name\():
mov r4,1 // seg
lsl r4,r2
mov r5,8
mul r1,r5
add r3,r1
ldr r5,[r3]
\op r5,r4
str r5,[r3]
//bl lcd_update
pop {r0-r5,lr}
bx lr
.size lcd_\name\()_segment,.-lcd_\name\()_segment
.endm
lcd_segment_op set orr
lcd_segment_op clear bic
lcd_segment_op toggle eor
.global lcd_set_character
// r0 -> seg number (0-5)
// r1 -> char (0-255)
lcd_set_character:
cmp r1,0x1F
it le
bxle lr // do nothing
push {r0-r6,lr}
sub r1,0x20
add r1,r1
ldr r2, =lcd_font
add r2,r1
ldrh r2,[r2] // font in r2
ldr r3, =lcd_segmap // segmap + 16 * r0 = start address of segments
mov r4, 16
mul r4, r0
add r3, r4 // address of segment map
mov r0,r3 // r0 = address of segment map
mov r5, 1<<15 // leftmost bit in font
loop:
ands r6,r5,r2
it ne
blne lcd_set_segment
add r0,1
lsrs r5, 1
beq done
b loop
done:
pop {r0-r6,lr}
bx lr
.size lcd_set_character,.-lcd_set_character
// r0 -> address of string
// r1 -> first segment
.global lcd_set_string_at
.type lcd_set_string_at,%function
lcd_set_string_at:
push {r0-r2,lr}
b lcd_set_string_loop
.size lcd_set_string_at,.-lcd_set_string
// r0 -> address of string
.global lcd_set_string
.type lcd_set_string,%function
lcd_set_string:
push {r0-r2,lr}
mov r1,0
lcd_set_string_loop:
ldrb r2,[r0] // r2 = character
cmp r2,0
beq lcd_set_string_done // if nul, end
cmp r1,6 // if end of screen, end
beq lcd_set_string_done
push {r0,r1}
mov r0,r1
mov r1,r2
bl lcd_set_character
pop {r0,r1}
add r0,1
add r1,1
b lcd_set_string_loop
lcd_set_string_done:
pop {r0-r2,lr}
bx lr
.size lcd_set_string,.-lcd_set_string
.data
lcd_itoa_buf:
.word 0,0
.text
// r0 -> integer to display
.global lcd_set_integer
.type lcd_set_integer,%function
lcd_set_integer:
push {r1-r2,lr}
ldr r1,=lcd_itoa_buf
mov r2,10
bl itoa // sneaky c
pop {r1-r2,lr}
b lcd_set_string
.size lcd_set_integer,.-lcd_set_integer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment