Skip to content

Commit 02d1e30

Browse files
drivers: comparator: implement stm32 comparator driver
implement the driver for the stm32 comparator peripheral Signed-off-by: Alexander Kozhinov <[email protected]>
1 parent 0c267cb commit 02d1e30

File tree

4 files changed

+350
-0
lines changed

4 files changed

+350
-0
lines changed

drivers/comparator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_COMP comparator_nrf_comp.c)
1515
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_LPCOMP comparator_nrf_lpcomp.c)
1616
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_SHELL comparator_shell.c)
1717
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RA comparator_renesas_ra.c)
18+
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_STM32_COMP comparator_stm32_comp.c)

drivers/comparator/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ rsource "Kconfig.nrf_comp"
2727
rsource "Kconfig.nrf_lpcomp"
2828
rsource "Kconfig.shell"
2929
rsource "Kconfig.renesas_ra"
30+
rsource "Kconfig.stm32_comp"
3031

3132
endif # COMPARATOR
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Alexander Kozhinov <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config COMPARATOR_STM32_COMP
5+
bool "ST STM32 comparator driver"
6+
default y
7+
depends on DT_HAS_ST_STM32_COMP_ENABLED
8+
select PINCTRL
9+
select EXTI_STM32
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Kozhinov <[email protected]>
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <zephyr/irq.h>
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/pm/device.h>
9+
#include <zephyr/logging/log.h>
10+
#include <zephyr/sys/util_macro.h>
11+
#include <zephyr/drivers/pinctrl.h>
12+
#include <zephyr/drivers/comparator.h>
13+
#include <zephyr/drivers/clock_control.h>
14+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
15+
#include <zephyr/drivers/interrupt_controller/intc_exti_stm32.h>
16+
17+
#include <stm32_ll_comp.h>
18+
#include <stm32_ll_system.h>
19+
20+
LOG_MODULE_REGISTER(stm32_comp, CONFIG_COMPARATOR_LOG_LEVEL);
21+
22+
#define DT_DRV_COMPAT st_stm32_comp
23+
24+
#define LL_COMP_INPUT_PLUS_IN0 LL_COMP_INPUT_PLUS_IO1
25+
#define LL_COMP_INPUT_PLUS_IN1 LL_COMP_INPUT_PLUS_IO2
26+
27+
#define LL_COMP_INPUT_MINUS_IN0 LL_COMP_INPUT_MINUS_IO1
28+
#define LL_COMP_INPUT_MINUS_IN1 LL_COMP_INPUT_MINUS_IO2
29+
30+
#define STM32_COMP_DT_INST_P_IN(inst) \
31+
CONCAT(LL_COMP_INPUT_PLUS_, DT_INST_STRING_TOKEN(inst, positive_input))
32+
33+
#define STM32_COMP_DT_INST_N_IN(inst) \
34+
CONCAT(LL_COMP_INPUT_MINUS_, DT_INST_STRING_TOKEN(inst, negative_input))
35+
36+
#define STM32_COMP_DT_INST_HYST_MODE(inst) \
37+
CONCAT(LL_COMP_HYSTERESIS_, DT_INST_STRING_TOKEN(inst, hysteresis))
38+
39+
#define STM32_COMP_DT_INST_INV_OUT(inst) \
40+
CONCAT(LL_COMP_OUTPUTPOL_, DT_INST_STRING_TOKEN(inst, invert_output))
41+
42+
#define STM32_COMP_DT_INST_BLANK_SEL(inst) \
43+
CONCAT(LL_COMP_BLANKINGSRC_, DT_INST_STRING_TOKEN(inst, st_blank_sel))
44+
45+
#define STM32_COMP_DT_INST_LOCK(inst) DT_INST_PROP(inst, st_lock_enable)
46+
47+
#define STM32_COMP_DT_MILLER_EFFECT_HOLD_DISABLE(inst) \
48+
DT_INST_PROP_OR(inst, st_miller_effect_hold_disable, false)
49+
50+
#define STM32_COMP_DT_EXTI_LINE_NUMBER(inst) DT_INST_PROP(inst, st_exti_line)
51+
52+
/* Value 0 always relates to the default value of COMP PWRMODE bit field */
53+
#define LL_COMP_POWERMODE_DEFAULT 0
54+
55+
#define STM32_COMP_DT_POWER_MODE(inst) \
56+
CONCAT(LL_COMP_POWERMODE_, DT_INST_STRING_TOKEN_OR(inst, st_power_mode, DEFAULT))
57+
58+
struct stm32_comp_config {
59+
COMP_TypeDef *comp;
60+
struct stm32_pclken *pclken;
61+
const struct pinctrl_dev_config *pincfg;
62+
void (*irq_init)(void);
63+
const uint32_t irq_nr;
64+
const uint32_t exti_line_number;
65+
const bool lock_enable;
66+
const bool miller_effect_hold_disable;
67+
const uint32_t power_mode;
68+
const uint32_t input_plus;
69+
const uint32_t input_minus;
70+
const uint32_t hysteresis;
71+
const uint32_t invert_output;
72+
const uint32_t blank_sel;
73+
};
74+
75+
struct stm32_comp_data {
76+
comparator_callback_t callback;
77+
void *user_data;
78+
};
79+
80+
static bool stm32_comp_is_resumed(const struct device *dev)
81+
{
82+
#ifdef CONFIG_PM_DEVICE
83+
enum pm_device_state state;
84+
85+
(void)pm_device_state_get(dev, &state);
86+
return state == PM_DEVICE_STATE_ACTIVE;
87+
#else
88+
return true;
89+
#endif /* CONFIG_PM_DEVICE */
90+
}
91+
92+
static int stm32_comp_get_output(const struct device *dev)
93+
{
94+
const struct stm32_comp_config *cfg = dev->config;
95+
COMP_TypeDef *comp = cfg->comp;
96+
97+
return LL_COMP_ReadOutputLevel(comp);
98+
}
99+
100+
static int stm32_comp_set_trigger(const struct device *dev, enum comparator_trigger trigger)
101+
{
102+
const struct stm32_comp_config *cfg = dev->config;
103+
struct stm32_comp_data *data = dev->data;
104+
stm32_exti_trigger_type exti_trigger = 0U;
105+
COMP_TypeDef *comp = cfg->comp;
106+
int ret = 0;
107+
108+
switch (trigger) {
109+
case COMPARATOR_TRIGGER_NONE:
110+
exti_trigger = STM32_EXTI_TRIG_NONE;
111+
break;
112+
case COMPARATOR_TRIGGER_RISING_EDGE:
113+
exti_trigger = STM32_EXTI_TRIG_RISING;
114+
break;
115+
case COMPARATOR_TRIGGER_FALLING_EDGE:
116+
exti_trigger = STM32_EXTI_TRIG_FALLING;
117+
break;
118+
case COMPARATOR_TRIGGER_BOTH_EDGES:
119+
exti_trigger = STM32_EXTI_TRIG_BOTH;
120+
break;
121+
default:
122+
LOG_ERR("%s: Unsupported trigger mode %d", dev->name, trigger);
123+
return -ENOTSUP;
124+
}
125+
126+
irq_disable(cfg->irq_nr);
127+
LL_COMP_Disable(comp);
128+
129+
ret = stm32_exti_enable(cfg->exti_line_number, exti_trigger,
130+
STM32_EXTI_MODE_IT);
131+
if (ret != 0) {
132+
LOG_ERR("%s: EXTI init failed (%d)", dev->name, ret);
133+
return ret;
134+
}
135+
136+
if (stm32_comp_is_resumed(dev)) {
137+
LL_COMP_Enable(comp);
138+
}
139+
140+
if (data->callback != NULL) {
141+
irq_enable(cfg->irq_nr);
142+
}
143+
144+
return ret;
145+
}
146+
147+
static int stm32_comp_trigger_is_pending(const struct device *dev)
148+
{
149+
const struct stm32_comp_config *cfg = dev->config;
150+
151+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
152+
stm32_exti_clear_pending(cfg->exti_line_number);
153+
return 1;
154+
}
155+
return 0;
156+
}
157+
158+
static int stm32_comp_set_trigger_callback(const struct device *dev, comparator_callback_t callback,
159+
void *user_data)
160+
{
161+
const struct stm32_comp_config *cfg = dev->config;
162+
struct stm32_comp_data *data = dev->data;
163+
164+
irq_disable(cfg->irq_nr);
165+
166+
data->callback = callback;
167+
data->user_data = user_data;
168+
169+
irq_enable(cfg->irq_nr);
170+
171+
if (data->callback != NULL && stm32_comp_trigger_is_pending(dev)) {
172+
callback(dev, user_data);
173+
}
174+
175+
return 0;
176+
}
177+
178+
static int stm32_comp_pm_callback(const struct device *dev, enum pm_device_action action)
179+
{
180+
const struct stm32_comp_config *cfg = dev->config;
181+
COMP_TypeDef *comp = cfg->comp;
182+
183+
if (LL_COMP_IsLocked(comp)) {
184+
LOG_ERR("%s is locked", dev->name);
185+
return -EACCES;
186+
187+
}
188+
189+
if (action == PM_DEVICE_ACTION_RESUME) {
190+
LL_COMP_Enable(comp);
191+
if (cfg->lock_enable) {
192+
LL_COMP_Lock(comp);
193+
}
194+
}
195+
196+
if (IS_ENABLED(CONFIG_PM_DEVICE) && action == PM_DEVICE_ACTION_SUSPEND) {
197+
LL_COMP_Disable(comp);
198+
}
199+
200+
return 0;
201+
}
202+
203+
static void stm32_comp_irq_handler(const struct device *dev)
204+
{
205+
const struct stm32_comp_config *cfg = dev->config;
206+
struct stm32_comp_data *data = dev->data;
207+
208+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
209+
stm32_exti_clear_pending(cfg->exti_line_number);
210+
}
211+
212+
if (data->callback == NULL) {
213+
return;
214+
}
215+
216+
data->callback(dev, data->user_data);
217+
}
218+
219+
static int stm32_comp_init(const struct device *dev)
220+
{
221+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
222+
const struct stm32_comp_config *cfg = dev->config;
223+
COMP_TypeDef *comp = cfg->comp;
224+
int ret = 0;
225+
226+
if (!device_is_ready(clk)) {
227+
LOG_ERR("%s clock control device not ready", dev->name);
228+
return -ENODEV;
229+
}
230+
231+
/* Enable COMP bus clock */
232+
ret = clock_control_on(clk, &cfg->pclken[0]);
233+
if (ret != 0) {
234+
LOG_ERR("%s clock op failed (%d)", dev->name, ret);
235+
return ret;
236+
}
237+
238+
/* Enable COMP clock source */
239+
clock_control_configure(clk, &cfg->pclken[1], NULL);
240+
if (ret != 0) {
241+
LOG_ERR("%s clock configure failed (%d)", dev->name, ret);
242+
return ret;
243+
}
244+
245+
/* Configure COMP inputs as specified in Device Tree, if any */
246+
ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
247+
if (ret < 0 && ret != -ENOENT) {
248+
/*
249+
* If the COMP is used only with internal channels, then no pinctrl is
250+
* provided in Device Tree, and pinctrl_apply_state returns -ENOENT,
251+
* but this should not be treated as an error.
252+
*/
253+
LOG_ERR("%s pinctrl setup failed (%d)", dev->name, ret);
254+
return ret;
255+
}
256+
257+
if (LL_COMP_IsLocked(comp)) {
258+
/* COMP instance shall not be locked */
259+
LOG_ERR("%s COMP instance is locked", dev->name);
260+
return -EACCES;
261+
}
262+
263+
#ifndef CONFIG_SOC_SERIES_STM32G4X
264+
LL_COMP_SetPowerMode(comp, cfg->power_mode);
265+
#endif /* !CONFIG_SOC_SERIES_STM32G4X */
266+
267+
LL_COMP_SetInputMinus(comp, cfg->input_minus);
268+
LL_COMP_SetInputPlus(comp, cfg->input_plus);
269+
LL_COMP_SetInputHysteresis(comp, cfg->hysteresis);
270+
LL_COMP_SetOutputPolarity(comp, cfg->invert_output);
271+
LL_COMP_SetOutputBlankingSource(comp, cfg->blank_sel);
272+
273+
#if defined(CONFIG_SOC_SERIES_STM32G4X)
274+
/* Undocumented bit, refer to st,miller-effect-hold-disable DT binding */
275+
WRITE_BIT(comp->CSR, 1, cfg->miller_effect_hold_disable);
276+
#endif /* CONFIG_SOC_SERIES_STM32G4X */
277+
278+
cfg->irq_init();
279+
280+
return pm_device_driver_init(dev, stm32_comp_pm_callback);
281+
}
282+
283+
static DEVICE_API(comparator, stm32_comp_comp_api) = {
284+
.get_output = stm32_comp_get_output,
285+
.set_trigger = stm32_comp_set_trigger,
286+
.set_trigger_callback = stm32_comp_set_trigger_callback,
287+
.trigger_is_pending = stm32_comp_trigger_is_pending,
288+
};
289+
290+
#define STM32_COMP_IRQ_HANDLER_SYM(inst) stm32_comp_irq_init##inst
291+
292+
#define STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
293+
static void STM32_COMP_IRQ_HANDLER_SYM(inst)(void) \
294+
{ \
295+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
296+
stm32_comp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
297+
irq_enable(DT_INST_IRQN(inst)); \
298+
}
299+
300+
#define STM32_COMP_DEVICE(inst) \
301+
\
302+
static struct stm32_pclken comp_clk[] = STM32_DT_INST_CLOCKS(inst); \
303+
\
304+
PINCTRL_DT_INST_DEFINE(inst); \
305+
\
306+
static struct stm32_comp_data _CONCAT(data, inst); \
307+
\
308+
STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
309+
static const struct stm32_comp_config _CONCAT(config, inst) = { \
310+
.comp = (COMP_TypeDef *)DT_INST_REG_ADDR(inst), \
311+
.pclken = comp_clk, \
312+
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
313+
.irq_init = STM32_COMP_IRQ_HANDLER_SYM(inst), \
314+
.irq_nr = DT_INST_IRQN(inst), \
315+
.exti_line_number = STM32_COMP_DT_EXTI_LINE_NUMBER(inst), \
316+
.lock_enable = STM32_COMP_DT_INST_LOCK(inst), \
317+
.miller_effect_hold_disable = \
318+
STM32_COMP_DT_MILLER_EFFECT_HOLD_DISABLE(inst), \
319+
.power_mode = STM32_COMP_DT_POWER_MODE(inst), \
320+
.input_plus = STM32_COMP_DT_INST_P_IN(inst), \
321+
.input_minus = STM32_COMP_DT_INST_N_IN(inst), \
322+
.hysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \
323+
.invert_output = STM32_COMP_DT_INST_INV_OUT(inst), \
324+
.blank_sel = STM32_COMP_DT_INST_BLANK_SEL(inst) \
325+
}; \
326+
\
327+
PM_DEVICE_DT_INST_DEFINE(inst, stm32_comp_pm_callback); \
328+
\
329+
DEVICE_DT_INST_DEFINE(inst, \
330+
stm32_comp_init, \
331+
PM_DEVICE_DT_INST_GET(inst), \
332+
&_CONCAT(data, inst), \
333+
&_CONCAT(config, inst), \
334+
POST_KERNEL, \
335+
CONFIG_COMPARATOR_INIT_PRIORITY, \
336+
&stm32_comp_comp_api \
337+
);
338+
339+
DT_INST_FOREACH_STATUS_OKAY(STM32_COMP_DEVICE)

0 commit comments

Comments
 (0)