Skip to content

Commit 56efc4e

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 58d9a89 commit 56efc4e

File tree

4 files changed

+341
-0
lines changed

4 files changed

+341
-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: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
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_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+
#if !defined(LL_COMP_POWERMODE_NONE)
53+
#define LL_COMP_POWERMODE_NONE 0
54+
#endif /* !LL_COMP_POWERMODE_NONE */
55+
56+
#define STM32_COMP_DT_POWER_MODE(inst) \
57+
CONCAT(LL_COMP_POWERMODE_, DT_INST_STRING_TOKEN_OR(inst, st_power_mode, NONE))
58+
59+
struct stm32_comp_config {
60+
COMP_TypeDef *comp;
61+
struct stm32_pclken *pclken;
62+
const struct pinctrl_dev_config *pincfg;
63+
void (*irq_init)(void);
64+
const uint32_t irq_nr;
65+
const uint32_t exti_line_number;
66+
const bool lock_enable;
67+
const bool miller_effect_hold_enable;
68+
const uint32_t power_mode;
69+
const uint32_t input_plus;
70+
const uint32_t input_minus;
71+
const uint32_t hysteresis;
72+
const uint32_t invert_output;
73+
const uint32_t blank_sel;
74+
};
75+
76+
struct stm32_comp_data {
77+
comparator_callback_t callback;
78+
void *user_data;
79+
};
80+
81+
static bool stm32_comp_is_resumed(void)
82+
{
83+
#if CONFIG_PM_DEVICE
84+
enum pm_device_state state;
85+
86+
(void)pm_device_state_get(DEVICE_DT_INST_GET(0), &state);
87+
return state == PM_DEVICE_STATE_ACTIVE;
88+
#else
89+
return true;
90+
#endif /* CONFIG_PM_DEVICE */
91+
}
92+
93+
static int stm32_comp_get_output(const struct device *dev)
94+
{
95+
const struct stm32_comp_config *cfg = dev->config;
96+
97+
return LL_COMP_ReadOutputLevel(cfg->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+
int ret = 0;
106+
107+
switch (trigger) {
108+
case COMPARATOR_TRIGGER_NONE:
109+
exti_trigger = STM32_EXTI_TRIG_NONE;
110+
break;
111+
case COMPARATOR_TRIGGER_RISING_EDGE:
112+
exti_trigger = STM32_EXTI_TRIG_RISING;
113+
break;
114+
case COMPARATOR_TRIGGER_FALLING_EDGE:
115+
exti_trigger = STM32_EXTI_TRIG_FALLING;
116+
break;
117+
case COMPARATOR_TRIGGER_BOTH_EDGES:
118+
exti_trigger = STM32_EXTI_TRIG_BOTH;
119+
break;
120+
default:
121+
LOG_DBG("%s: Unsupported trigger mode %d", dev->name, trigger);
122+
return -ENOTSUP;
123+
}
124+
125+
irq_disable(cfg->irq_nr);
126+
LL_COMP_Disable(cfg->comp);
127+
128+
ret = stm32_exti_enable(cfg->exti_line_number, exti_trigger,
129+
STM32_EXTI_MODE_IT);
130+
if (ret != 0) {
131+
LOG_DBG("%s: EXTI init failed (%d)", dev->name, ret);
132+
return ret;
133+
}
134+
135+
if (stm32_comp_is_resumed()) {
136+
LL_COMP_Enable(cfg->comp);
137+
}
138+
139+
if (data->callback != NULL) {
140+
irq_enable(cfg->irq_nr);
141+
}
142+
143+
return ret;
144+
}
145+
146+
static int stm32_comp_trigger_is_pending(const struct device *dev)
147+
{
148+
const struct stm32_comp_config *cfg = dev->config;
149+
150+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
151+
stm32_exti_clear_pending(cfg->exti_line_number);
152+
return 1;
153+
}
154+
return 0;
155+
}
156+
157+
static int stm32_comp_set_trigger_callback(const struct device *dev, comparator_callback_t callback,
158+
void *user_data)
159+
{
160+
const struct stm32_comp_config *cfg = dev->config;
161+
struct stm32_comp_data *data = dev->data;
162+
163+
irq_disable(cfg->irq_nr);
164+
165+
data->callback = callback;
166+
data->user_data = user_data;
167+
168+
irq_enable(cfg->irq_nr);
169+
170+
if (data->callback != NULL && stm32_comp_trigger_is_pending(dev)) {
171+
callback(dev, user_data);
172+
}
173+
174+
return 0;
175+
}
176+
177+
static DEVICE_API(comparator, stm32_comp_comp_api) = {
178+
.get_output = stm32_comp_get_output,
179+
.set_trigger = stm32_comp_set_trigger,
180+
.set_trigger_callback = stm32_comp_set_trigger_callback,
181+
.trigger_is_pending = stm32_comp_trigger_is_pending,
182+
};
183+
184+
static int stm32_comp_pm_callback(const struct device *dev, enum pm_device_action action)
185+
{
186+
const struct stm32_comp_config *cfg = dev->config;
187+
188+
if (action == PM_DEVICE_ACTION_RESUME) {
189+
if (cfg->lock_enable) {
190+
LL_COMP_Lock(cfg->comp);
191+
}
192+
LL_COMP_Enable(cfg->comp);
193+
}
194+
195+
#if CONFIG_PM_DEVICE
196+
if (action == PM_DEVICE_ACTION_SUSPEND) {
197+
LL_COMP_Disable(cfg->comp);
198+
}
199+
#endif
200+
201+
return 0;
202+
}
203+
204+
static void stm32_comp_irq_handler(const struct device *dev)
205+
{
206+
const struct stm32_comp_config *cfg = dev->config;
207+
struct stm32_comp_data *data = dev->data;
208+
209+
if (stm32_exti_is_pending(cfg->exti_line_number)) {
210+
stm32_exti_clear_pending(cfg->exti_line_number);
211+
}
212+
213+
if (data->callback == NULL) {
214+
return;
215+
}
216+
217+
data->callback(dev, data->user_data);
218+
}
219+
220+
static int stm32_comp_init(const struct device *dev)
221+
{
222+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
223+
const struct stm32_comp_config *cfg = dev->config;
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(cfg->comp)) {
258+
/* COMP instance shall not be locked */
259+
LOG_ERR("%s COMP instance is locked", dev->name);
260+
return -EACCES;
261+
}
262+
263+
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp)
264+
MODIFY_REG(cfg->comp->CFGR, COMP_CFGRx_PWRMODE, cfg->power_mode);
265+
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32h7_comp) */
266+
267+
LL_COMP_SetInputMinus(cfg->comp, cfg->input_minus);
268+
LL_COMP_SetInputPlus(cfg->comp, cfg->input_plus);
269+
LL_COMP_SetInputHysteresis(cfg->comp, cfg->hysteresis);
270+
LL_COMP_SetOutputPolarity(cfg->comp, cfg->invert_output);
271+
LL_COMP_SetOutputBlankingSource(cfg->comp, cfg->blank_sel);
272+
273+
if (cfg->miller_effect_hold_enable) {
274+
#if defined(CONFIG_COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING)
275+
SET_BIT(cfg->comp->CSR, BIT(1U));
276+
#endif /* CONFIG_COMPARATOR_STM32_COMP_MILLER_EFFECT_HANDLING */
277+
}
278+
279+
cfg->irq_init();
280+
281+
return pm_device_driver_init(dev, stm32_comp_pm_callback);
282+
}
283+
284+
#define STM32_COMP_IRQ_HANDLER_SYM(inst) _CONCAT(stm32_comp_irq_init, inst)
285+
286+
#define STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
287+
static void STM32_COMP_IRQ_HANDLER_SYM(inst)(void) \
288+
{ \
289+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
290+
stm32_comp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
291+
irq_enable(DT_INST_IRQN(inst)); \
292+
}
293+
294+
#define STM32_COMP_DEVICE(inst) \
295+
static struct stm32_pclken comp_clk[] = STM32_DT_INST_CLOCKS(inst); \
296+
PINCTRL_DT_INST_DEFINE(inst); \
297+
static struct stm32_comp_data _CONCAT(data, inst); \
298+
STM32_COMP_IRQ_HANDLER_DEFINE(inst) \
299+
static const struct stm32_comp_config _CONCAT(config, inst) = { \
300+
.comp = (COMP_TypeDef *)DT_INST_REG_ADDR(inst), \
301+
.pclken = comp_clk, \
302+
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
303+
.irq_init = STM32_COMP_IRQ_HANDLER_SYM(inst), \
304+
.irq_nr = DT_INST_IRQN(inst), \
305+
.exti_line_number = STM32_COMP_DT_EXTI_LINE_NUMBER(inst), \
306+
.lock_enable = STM32_COMP_DT_INST_LOCK(inst), \
307+
.miller_effect_hold_enable = STM32_COMP_DT_MILLER_EFFECT_HOLD_ENABLE(inst), \
308+
.power_mode = STM32_COMP_DT_POWER_MODE(inst), \
309+
.input_plus = STM32_COMP_DT_INST_P_IN(inst), \
310+
.input_minus = STM32_COMP_DT_INST_N_IN(inst), \
311+
.hysteresis = STM32_COMP_DT_INST_HYST_MODE(inst), \
312+
.invert_output = STM32_COMP_DT_INST_INV_OUT(inst), \
313+
.blank_sel = STM32_COMP_DT_INST_BLANK_SEL(inst) \
314+
}; \
315+
PM_DEVICE_DT_INST_DEFINE(inst, stm32_comp_pm_callback); \
316+
DEVICE_DT_INST_DEFINE(inst, stm32_comp_init, PM_DEVICE_DT_INST_GET(inst), \
317+
&_CONCAT(data, inst), &_CONCAT(config, inst), POST_KERNEL, \
318+
CONFIG_COMPARATOR_INIT_PRIORITY, &stm32_comp_comp_api);
319+
320+
DT_INST_FOREACH_STATUS_OKAY(STM32_COMP_DEVICE)

0 commit comments

Comments
 (0)