summaryrefslogtreecommitdiffstats
path: root/libopencm3/lib/stm32/l1/lcd.c
blob: 68173ab87996f6d901eb3286ffe5846d64056012 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * This file is part of the libopencm3 project.
 *
 * Copyright (C) 2014 Nikolay Merinov <nikolay.merinov@member.fsf.org>
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <libopencm3/stm32/l1/lcd.h>
#include <libopencm3/stm32/rcc.h>

void lcd_enable(void)
{
	LCD_CR |= LCD_CR_LCDEN;
}

void lcd_update(void)
{
	LCD_SR |= LCD_SR_UDR;
}

void lcd_wait_for_lcd_enabled(void)
{
	while ((LCD_SR & LCD_SR_ENS) == 0);
}

void lcd_wait_for_step_up_ready(void)
{
	while ((LCD_SR & LCD_SR_RDY) == 0);
}

void lcd_wait_for_update_ready(void)
{
	while ((LCD_SR & LCD_SR_UDR) != 0);
}

int lcd_is_enabled(void)
{
	return ((LCD_SR & LCD_SR_ENS) != 0);
}

int lcd_is_step_up_ready(void)
{
	return ((LCD_SR & LCD_SR_RDY) != 0);
}

int lcd_is_for_update_ready(void)
{
	return ((LCD_SR & LCD_SR_UDR) == 0);
}

void lcd_set_contrast(uint8_t contrast)
{
	LCD_FCR &= ~(LCD_FCR_CC_MASK << LCD_FCR_CC_SHIFT);
	LCD_FCR |= contrast << LCD_FCR_CC_SHIFT;
}

void lcd_set_bias(uint8_t bias)
{
	LCD_CR &= ~(LCD_CR_BIAS_MASK << LCD_CR_BIAS_SHIFT);
	LCD_CR |= bias << LCD_CR_BIAS_SHIFT;
}

void lcd_set_duty(uint8_t duty)
{
	LCD_CR &= ~(LCD_CR_DUTY_MASK << LCD_CR_DUTY_SHIFT);
	LCD_CR |= duty << LCD_CR_DUTY_SHIFT;
}

void lcd_set_prescaler(uint8_t ps)
{
	LCD_FCR &= ~(LCD_FCR_PS_MASK << LCD_FCR_PS_SHIFT);
	LCD_FCR |= ps << LCD_FCR_PS_SHIFT;
}

void lcd_set_divider(uint8_t div)
{
	LCD_FCR &= ~(LCD_FCR_DIV_MASK << LCD_FCR_DIV_SHIFT);
	LCD_FCR |= div << LCD_FCR_DIV_SHIFT;
}

void lcd_enable_segment_multiplexing(void)
{
	LCD_CR |= LCD_CR_MUX_SEG;
}

void lcd_disable_segment_multiplexing(void)
{
	LCD_CR &= ~LCD_CR_MUX_SEG;
}

void lcd_set_refresh_frequency(uint32_t frequency)
{
	uint32_t duty, lcd_clock;
	switch ((LCD_CR >> LCD_CR_DUTY_SHIFT) & LCD_CR_DUTY_MASK) {
	case LCD_CR_DUTY_STATIC:
		duty = 1;
		break;
	case LCD_CR_DUTY_1_2:
		duty = 2;
		break;
	case LCD_CR_DUTY_1_3:
		duty = 3;
		break;
	case LCD_CR_DUTY_1_4:
		duty = 4;
		break;
	case LCD_CR_DUTY_1_8:
		duty = 8;
		break;
	default:
		/* Incorrect duty */
		return;
	}

	switch ((RCC_CSR >> RCC_CSR_RTCSEL_SHIFT) & RCC_CSR_RTCSEL_MASK) {
	case RCC_CSR_RTCSEL_LSE:
		lcd_clock = 32786;
		break;
	case RCC_CSR_RTCSEL_LSI:
		lcd_clock = 37000;
		break;
	case RCC_CSR_RTCSEL_HSI:
		lcd_clock = 16000000;
		break;
	default:
		/* RCC Clock not selected */
		return;
	}

	/* PS * DIV = lcd_clock/(duty * freq) */
	uint32_t ps_mul_div = lcd_clock / (duty * frequency);

	int div, ps = 0;
	while (ps_mul_div > 32)	{
		ps_mul_div >>= 1;
		ps++;
	}
	div = ps_mul_div - 16;

	lcd_set_prescaler(ps);
	lcd_set_divider(div);
}