Skip to content

Commit 532716c

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 78886d0 commit 532716c

File tree

4 files changed

+339
-0
lines changed

4 files changed

+339
-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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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
10+
select USE_STM32_LL_COMP
11+
12+
if COMPARATOR_STM32_COMP
13+
14+
config COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING
15+
bool
16+
default n
17+
default y if SOC_SERIES_STM32G4X
18+
19+
endif # COMPARATOR_STM32_COMP
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
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/sys/util_macro.h>
10+
#include <zephyr/drivers/pinctrl.h>
11+
#include <zephyr/drivers/comparator.h>
12+
#include <zephyr/drivers/clock_control.h>
13+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
14+
#include <zephyr/drivers/interrupt_controller/intc_exti_stm32.h>
15+
16+
#include <stm32_ll_comp.h>
17+
#include <stm32_ll_system.h>
18+
19+
#include <zephyr/logging/log.h>
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_ENABLE(inst) \
48+
DT_INST_PROP(inst, st_miller_effect_hold_enable)
49+
50+
#define STM32_COMP_DT_EXTI_LINE_NUMBER(inst) DT_INST_PROP(inst, st_exti_line)
51+
52+
#define STM32_COMP_DT_POWER_MODE(inst) \
53+
CONCAT(LL_COMP_POWERMODE_, DT_INST_STRING_TOKEN(inst, st_power_mode))
54+
55+
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp)
56+
#define STM32_COMP_DT_INST_COMP_CONFIG_INIT(inst) \
57+
{ \
58+
.PowerMode = STM32_COMP_DT_POWER_MODE(inst), \
59+
.InputPlus = STM32_COMP_DT_INST_P_IN(inst), \
60+
.InputMinus = STM32_COMP_DT_INST_N_IN(inst), \
61+
.InputHysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \
62+
.OutputPolarity = STM32_COMP_DT_INST_INV_OUT(inst), \
63+
.OutputBlankingSource = STM32_COMP_DT_INST_BLANK_SEL(inst), \
64+
}
65+
#else /* st_stm32h7_comp */
66+
#define STM32_COMP_DT_INST_COMP_CONFIG_INIT(inst) \
67+
{ \
68+
.InputPlus = STM32_COMP_DT_INST_P_IN(inst), \
69+
.InputMinus = STM32_COMP_DT_INST_N_IN(inst), \
70+
.InputHysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \
71+
.OutputPolarity = STM32_COMP_DT_INST_INV_OUT(inst), \
72+
.OutputBlankingSource = STM32_COMP_DT_INST_BLANK_SEL(inst), \
73+
}
74+
#endif /* st_stm32h7_comp */
75+
76+
struct stm32_comp_config {
77+
COMP_TypeDef *comp;
78+
struct stm32_pclken *pclken;
79+
const struct pinctrl_dev_config *pincfg;
80+
void (*irq_init)(void);
81+
const uint32_t irq_nr;
82+
const LL_COMP_InitTypeDef comp_config;
83+
const uint32_t exti_line_number;
84+
const bool lock_enable;
85+
const bool miller_effect_hold_enable;
86+
};
87+
88+
struct stm32_comp_data {
89+
comparator_callback_t callback;
90+
void *user_data;
91+
};
92+
93+
__maybe_unused static bool stm32_comp_is_resumed(void)
94+
{
95+
#if CONFIG_PM_DEVICE
96+
enum pm_device_state state;
97+
98+
(void)pm_device_state_get(DEVICE_DT_INST_GET(0), &state);
99+
return state == PM_DEVICE_STATE_ACTIVE;
100+
#else
101+
return true;
102+
#endif /* CONFIG_PM_DEVICE */
103+
}
104+
105+
static int stm32_comp_get_output(const struct device *dev)
106+
{
107+
const struct stm32_comp_config *cfg = dev->config;
108+
109+
return LL_COMP_ReadOutputLevel(cfg->comp);
110+
}
111+
112+
static int stm32_comp_set_trigger(const struct device *dev, enum comparator_trigger trigger)
113+
{
114+
const struct stm32_comp_config *cfg = dev->config;
115+
struct stm32_comp_data *data = dev->data;
116+
stm32_exti_trigger_type exti_trigger = 0U;
117+
int ret = 0;
118+
119+
switch (trigger) {
120+
case COMPARATOR_TRIGGER_NONE:
121+
exti_trigger = STM32_EXTI_TRIG_NONE;
122+
break;
123+
case COMPARATOR_TRIGGER_RISING_EDGE:
124+
exti_trigger = STM32_EXTI_TRIG_RISING;
125+
break;
126+
case COMPARATOR_TRIGGER_FALLING_EDGE:
127+
exti_trigger = STM32_EXTI_TRIG_FALLING;
128+
break;
129+
case COMPARATOR_TRIGGER_BOTH_EDGES:
130+
exti_trigger = STM32_EXTI_TRIG_BOTH;
131+
break;
132+
default:
133+
LOG_DBG("%s: Unsupported trigger mode %d", dev->name, trigger);
134+
return -ENOTSUP;
135+
}
136+
137+
irq_disable(cfg->irq_nr);
138+
LL_COMP_Disable(cfg->comp);
139+
140+
ret = stm32_exti_enable(cfg->exti_line_number, exti_trigger,
141+
STM32_EXTI_MODE_IT);
142+
if (ret != 0) {
143+
LOG_DBG("%s: EXTI init failed (%d)", dev->name, ret);
144+
return ret;
145+
}
146+
147+
if (stm32_comp_is_resumed()) {
148+
LL_COMP_Enable(cfg->comp);
149+
}
150+
151+
if (data->callback != NULL) {
152+
irq_enable(cfg->irq_nr);
153+
}
154+
155+
return ret;
156+
}
157+
158+
static int stm32_comp_trigger_is_pending(const struct device *dev)
159+
{
160+
const struct stm32_comp_config *cfg = dev->config;
161+
162+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
163+
stm32_exti_clear_pending(cfg->exti_line_number);
164+
return 1;
165+
}
166+
return 0;
167+
}
168+
169+
static int stm32_comp_set_trigger_callback(const struct device *dev, comparator_callback_t callback,
170+
void *user_data)
171+
{
172+
const struct stm32_comp_config *cfg = dev->config;
173+
struct stm32_comp_data *data = dev->data;
174+
175+
irq_disable(cfg->irq_nr);
176+
177+
data->callback = callback;
178+
data->user_data = user_data;
179+
180+
irq_enable(cfg->irq_nr);
181+
182+
if (data->callback != NULL && stm32_comp_trigger_is_pending(dev)) {
183+
callback(dev, user_data);
184+
}
185+
186+
return 0;
187+
}
188+
189+
static DEVICE_API(comparator, stm32_comp_comp_api) = {
190+
.get_output = stm32_comp_get_output,
191+
.set_trigger = stm32_comp_set_trigger,
192+
.set_trigger_callback = stm32_comp_set_trigger_callback,
193+
.trigger_is_pending = stm32_comp_trigger_is_pending,
194+
};
195+
196+
static int stm32_comp_pm_callback(const struct device *dev, enum pm_device_action action)
197+
{
198+
const struct stm32_comp_config *cfg = dev->config;
199+
200+
if (action == PM_DEVICE_ACTION_RESUME) {
201+
if (cfg->lock_enable) {
202+
LL_COMP_Lock(cfg->comp);
203+
}
204+
LL_COMP_Enable(cfg->comp);
205+
}
206+
207+
#if CONFIG_PM_DEVICE
208+
if (action == PM_DEVICE_ACTION_SUSPEND) {
209+
LL_COMP_Disable(cfg->comp);
210+
}
211+
#endif
212+
213+
return 0;
214+
}
215+
216+
static void stm32_comp_irq_handler(const struct device *dev)
217+
{
218+
const struct stm32_comp_config *cfg = dev->config;
219+
struct stm32_comp_data *data = dev->data;
220+
221+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
222+
stm32_exti_clear_pending(cfg->exti_line_number);
223+
}
224+
225+
if (data->callback == NULL) {
226+
return;
227+
}
228+
229+
data->callback(dev, data->user_data);
230+
}
231+
232+
static int stm32_comp_init(const struct device *dev)
233+
{
234+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
235+
const struct stm32_comp_config *cfg = dev->config;
236+
int ret = 0;
237+
238+
ret = device_is_ready(clk);
239+
if (!ret) {
240+
LOG_ERR("%s clock control device not ready (%d)", dev->name, ret);
241+
return ret;
242+
}
243+
244+
/* Enable COMP bus clock */
245+
ret = clock_control_on(clk, &cfg->pclken[0]);
246+
if (ret != 0) {
247+
LOG_ERR("%s clock op failed (%d)", dev->name, ret);
248+
return ret;
249+
}
250+
251+
/* Enable COMP clock source */
252+
clock_control_configure(clk, &cfg->pclken[1], NULL);
253+
if (ret != 0) {
254+
LOG_ERR("%s clock configure failed (%d)", dev->name, ret);
255+
return ret;
256+
}
257+
258+
/* Configure COMP inputs as specified in Device Tree, if any */
259+
ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
260+
if (ret < 0 && ret != -ENOENT) {
261+
/*
262+
* If the COMP is used only with internal channels, then no pinctrl is
263+
* provided in Device Tree, and pinctrl_apply_state returns -ENOENT,
264+
* but this should not be treated as an error.
265+
*/
266+
LOG_ERR("%s pinctrl setup failed (%d)", dev->name, ret);
267+
return ret;
268+
}
269+
270+
ret = LL_COMP_Init(cfg->comp, (LL_COMP_InitTypeDef *)&cfg->comp_config);
271+
if (ret != 0) {
272+
LOG_ERR("COMP instance is locked (%d)", ret);
273+
return ret;
274+
}
275+
276+
if (cfg->miller_effect_hold_enable) {
277+
#if defined(CONFIG_COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING)
278+
SET_BIT(cfg->comp->CSR, BIT(1U));
279+
#endif /* CONFIG_COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING */
280+
}
281+
282+
cfg->irq_init();
283+
284+
return pm_device_driver_init(dev, stm32_comp_pm_callback);
285+
}
286+
287+
#define STM32_COMP_IRQ_HANDLER_SYM(inst) _CONCAT(stm32_comp_irq_init, inst)
288+
289+
#define STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
290+
static void STM32_COMP_IRQ_HANDLER_SYM(inst)(void) \
291+
{ \
292+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
293+
stm32_comp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
294+
irq_enable(DT_INST_IRQN(inst)); \
295+
}
296+
297+
#define STM32_COMP_DEVICE(inst) \
298+
static struct stm32_pclken comp_clk[] = STM32_DT_INST_CLOCKS(inst); \
299+
PINCTRL_DT_INST_DEFINE(inst); \
300+
static struct stm32_comp_data _CONCAT(data, inst); \
301+
STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
302+
static const struct stm32_comp_config _CONCAT(config, inst) = { \
303+
.comp = (COMP_TypeDef *)DT_INST_REG_ADDR(inst), \
304+
.pclken = comp_clk, \
305+
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
306+
.irq_init = STM32_COMP_IRQ_HANDLER_SYM(inst), \
307+
.irq_nr = DT_INST_IRQN(inst), \
308+
.comp_config = STM32_COMP_DT_INST_COMP_CONFIG_INIT(inst), \
309+
.exti_line_number = STM32_COMP_DT_EXTI_LINE_NUMBER(inst), \
310+
.lock_enable = STM32_COMP_DT_INST_LOCK(inst), \
311+
.miller_effect_hold_enable = STM32_COMP_DT_MILLER_EFFECT_HOLD_ENABLE(inst) \
312+
}; \
313+
PM_DEVICE_DT_INST_DEFINE(inst, stm32_comp_pm_callback); \
314+
DEVICE_DT_INST_DEFINE(inst, stm32_comp_init, PM_DEVICE_DT_INST_GET(inst), \
315+
&_CONCAT(data, inst), &_CONCAT(config, inst), POST_KERNEL, \
316+
CONFIG_COMPARATOR_INIT_PRIORITY, &stm32_comp_comp_api);
317+
318+
DT_INST_FOREACH_STATUS_OKAY(STM32_COMP_DEVICE)

0 commit comments

Comments
 (0)