Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OwnTech
Power API
Core
Commits
3ae8c917
Commit
3ae8c917
authored
Feb 04, 2022
by
Clément Foucher
Browse files
Implement incremental encoder acquisition in Timer module using Timer 4.
parent
7754eb6e
Changes
5
Hide whitespace changes
Inline
Side-by-side
zephyr/dts/pinout.dtsi
0 → 100644
View file @
3ae8c917
/ {
soc {
pinctrl: pin-controller@48000000 {
tim4_etr_pb3: tim4_etr_pb3 {
pinmux = <STM32_PINMUX('B', 3, AF2)>;
};
};
};
};
\ No newline at end of file
zephyr/modules/owntech_timer_driver/zephyr/public_include/timer.h
View file @
3ae8c917
/*
* Copyright (c) 2021 LAAS-CNRS
* Copyright (c) 2021
-2022
LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
...
...
@@ -18,7 +18,20 @@
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
* @date 2022
* @author Clément Foucher <clement.foucher@laas.fr>
*
* @brief This file is the public include file for the
* Zephyr Timer driver. It provides basic functionnality
* to handle STM32 Timers. Is is for now specific
* to certain capabilities of G4 series Timers,
* and mainly restricted to the use we do of
* the timers in the OwnTech project, but it aims
* at becoming more generic over time.
*
* This version suports:
* * Timer 6 and Timer 7: Periodic call of a callback function with period ranging from 2 to 6553 µs.
* * Timer 4: Incremental coder acquisition with pinout: reset=PB3; CH1=PB6; CH2=PB7.
*/
#ifndef TIMER_H_
...
...
@@ -39,16 +52,44 @@ extern "C" {
typedef
void
(
*
timer_callback_t
)();
typedef
enum
{
no_pull
,
pull_up
,
pull_down
}
pin_mode_t
;
/**
* timer_enable_irq : set to 1 to enable interrupt on timer overflow.
* timer_enable_encoder : set to 1 for timer to act as an incremental coder counter.
*
* *** IRQ mode (ignored if timer_enable_irq=0) ***
* - timer_irq_callback : pointer to a void(void) function that will be
* called on timer overflow.
* - timer_irq_t_usec : period of the interrupt in microsecond (2 to 6553 µs)
*
* *** Incremental code mode (ignored if timer_enable_encoder=0) ***
* - timer_pin_mode : Pin mode for incremental coder interface.
*
* NOTE: At this time, only irq mode is supported on TIM6/TIM7, and
* only incremental coder mode is suppported on TIM4, which makes this
* configuration structure almost pointless (except for callback definition).
* However, it is built this way with future evolutions of the driver
* in mind.
*/
struct
timer_config_t
{
uint32_t
timer_enable_irq
:
1
;
timer_callback_t
timer_callback
;
uint32_t
timer_enable_irq
:
1
;
uint32_t
timer_enable_encoder
:
1
;
timer_callback_t
timer_irq_callback
;
uint32_t
timer_irq_t_usec
;
pin_mode_t
timer_enc_pin_mode
;
};
// API
typedef
void
(
*
timer_api_config
)
(
const
struct
device
*
dev
,
const
struct
timer_config_t
*
config
);
typedef
void
(
*
timer_api_start
)
(
const
struct
device
*
dev
,
uint32_t
t_usec
);
typedef
void
(
*
timer_api_start
)
(
const
struct
device
*
dev
);
typedef
uint32_t
(
*
timer_api_get_count
)(
const
struct
device
*
dev
);
__subsystem
struct
timer_driver_api
...
...
@@ -59,6 +100,12 @@ __subsystem struct timer_driver_api
};
/**
* Configure the timer dev using given configuration structure config.
*
* @param dev Zephyr device representing the timer.
* @param config Configuration holding the timer configuration.
*/
static
inline
void
timer_config
(
const
struct
device
*
dev
,
const
struct
timer_config_t
*
config
)
{
const
struct
timer_driver_api
*
api
=
(
const
struct
timer_driver_api
*
)(
dev
->
api
);
...
...
@@ -66,13 +113,24 @@ static inline void timer_config(const struct device* dev, const struct timer_con
api
->
config
(
dev
,
config
);
}
static
inline
void
timer_start
(
const
struct
device
*
dev
,
uint32_t
t_usec
)
/**
* Configure the timer dev with given time t_usec in microseconds.
*
* @param dev Zephyr device representing the timer.
*/
static
inline
void
timer_start
(
const
struct
device
*
dev
)
{
const
struct
timer_driver_api
*
api
=
(
const
struct
timer_driver_api
*
)(
dev
->
api
);
api
->
start
(
dev
,
t_usec
);
api
->
start
(
dev
);
}
/**
* Get the current timer counter value.
*
* @param dev Zephyr device representing the timer.
* @return Current value of the timer internal counter.
*/
static
inline
uint32_t
timer_get_count
(
const
struct
device
*
dev
)
{
const
struct
timer_driver_api
*
api
=
(
const
struct
timer_driver_api
*
)(
dev
->
api
);
...
...
zephyr/modules/owntech_timer_driver/zephyr/src/stm32_timer_driver.c
View file @
3ae8c917
/*
* Copyright (c) 2021 LAAS-CNRS
* Copyright (c) 2021
-2022
LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
...
...
@@ -18,13 +18,15 @@
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
* @date 2022
* @author Clément Foucher <clement.foucher@laas.fr>
*/
// STM32 LL
#include <stm32_ll_tim.h>
#include <stm32_ll_bus.h>
#include <stm32_ll_gpio.h>
// Current file header
#include "stm32_timer_driver.h"
...
...
@@ -37,7 +39,9 @@ static int timer_stm32_init(const struct device* dev)
{
TIM_TypeDef
*
tim_dev
=
((
struct
stm32_timer_driver_data
*
)
dev
->
data
)
->
timer_struct
;
if
(
tim_dev
==
TIM6
)
if
(
tim_dev
==
TIM4
)
init_timer_4
();
else
if
(
tim_dev
==
TIM6
)
init_timer_6
();
else
if
(
tim_dev
==
TIM7
)
init_timer_7
();
...
...
@@ -58,9 +62,9 @@ static void timer_stm32_callback(const void* arg)
timer_stm32_clear
(
timer_dev
);
if
(
data
->
timer_callback
!=
NULL
)
if
(
data
->
timer_
irq_
callback
!=
NULL
)
{
data
->
timer_callback
();
data
->
timer_
irq_
callback
();
}
}
...
...
@@ -80,26 +84,81 @@ void timer_stm32_config(const struct device* dev, const struct timer_config_t* c
struct
stm32_timer_driver_data
*
data
=
(
struct
stm32_timer_driver_data
*
)
dev
->
data
;
TIM_TypeDef
*
tim_dev
=
data
->
timer_struct
;
if
(
tim_dev
!=
NULL
)
if
(
(
tim_dev
==
TIM6
)
||
(
tim_dev
==
TIM7
)
)
{
if
(
config
->
timer_enable_irq
==
1
)
{
data
->
timer_callback
=
config
->
timer_callback
;
data
->
timer_mode
=
periodic_interrupt
;
data
->
timer_irq_callback
=
config
->
timer_irq_callback
;
data
->
timer_irq_period_usec
=
config
->
timer_irq_t_usec
;
irq_connect_dynamic
(
data
->
interrupt_line
,
data
->
interrupt_prio
,
timer_stm32_callback
,
dev
,
0
);
irq_enable
(
data
->
interrupt_line
);
}
}
else
if
(
tim_dev
==
TIM4
)
{
if
(
config
->
timer_enable_encoder
==
1
)
{
data
->
timer_mode
=
incremental_coder
;
uint32_t
pull
=
0
;
switch
(
config
->
timer_enc_pin_mode
)
{
case
no_pull
:
pull
=
LL_GPIO_PULL_NO
;
break
;
case
pull_up
:
pull
=
LL_GPIO_PULL_UP
;
break
;
case
pull_down
:
pull
=
LL_GPIO_PULL_DOWN
;
break
;
}
// Configure GPIO
LL_AHB2_GRP1_EnableClock
(
LL_AHB2_GRP1_PERIPH_GPIOB
);
LL_GPIO_SetPinMode
(
GPIOB
,
LL_GPIO_PIN_3
,
LL_GPIO_MODE_ALTERNATE
);
LL_GPIO_SetPinSpeed
(
GPIOB
,
LL_GPIO_PIN_3
,
LL_GPIO_SPEED_FREQ_LOW
);
LL_GPIO_SetPinOutputType
(
GPIOB
,
LL_GPIO_PIN_3
,
LL_GPIO_OUTPUT_PUSHPULL
);
LL_GPIO_SetPinPull
(
GPIOB
,
LL_GPIO_PIN_3
,
pull
);
LL_GPIO_SetAFPin_0_7
(
GPIOB
,
LL_GPIO_PIN_3
,
LL_GPIO_AF_2
);
LL_GPIO_SetPinMode
(
GPIOB
,
LL_GPIO_PIN_6
,
LL_GPIO_MODE_ALTERNATE
);
LL_GPIO_SetPinSpeed
(
GPIOB
,
LL_GPIO_PIN_6
,
LL_GPIO_SPEED_FREQ_LOW
);
LL_GPIO_SetPinOutputType
(
GPIOB
,
LL_GPIO_PIN_6
,
LL_GPIO_OUTPUT_PUSHPULL
);
LL_GPIO_SetPinPull
(
GPIOB
,
LL_GPIO_PIN_6
,
pull
);
LL_GPIO_SetAFPin_0_7
(
GPIOB
,
LL_GPIO_PIN_6
,
LL_GPIO_AF_2
);
LL_GPIO_SetPinMode
(
GPIOB
,
LL_GPIO_PIN_7
,
LL_GPIO_MODE_ALTERNATE
);
LL_GPIO_SetPinSpeed
(
GPIOB
,
LL_GPIO_PIN_7
,
LL_GPIO_SPEED_FREQ_LOW
);
LL_GPIO_SetPinOutputType
(
GPIOB
,
LL_GPIO_PIN_7
,
LL_GPIO_OUTPUT_PUSHPULL
);
LL_GPIO_SetPinPull
(
GPIOB
,
LL_GPIO_PIN_7
,
pull
);
LL_GPIO_SetAFPin_0_7
(
GPIOB
,
LL_GPIO_PIN_7
,
LL_GPIO_AF_2
);
}
}
}
void
timer_stm32_start
(
const
struct
device
*
dev
,
uint32_t
t_usec
)
void
timer_stm32_start
(
const
struct
device
*
dev
)
{
TIM_TypeDef
*
tim_dev
=
((
struct
stm32_timer_driver_data
*
)
dev
->
data
)
->
timer_struct
;
struct
stm32_timer_driver_data
*
data
=
(
struct
stm32_timer_driver_data
*
)
dev
->
data
;
TIM_TypeDef
*
tim_dev
=
data
->
timer_struct
;
if
(
tim_dev
!=
NULL
)
if
(
(
tim_dev
==
TIM6
)
||
(
tim_dev
==
TIM7
)
)
{
LL_TIM_SetAutoReload
(
tim_dev
,
(
t_usec
*
10
)
-
1
);
LL_TIM_EnableIT_UPDATE
(
tim_dev
);
LL_TIM_EnableCounter
(
tim_dev
);
if
(
data
->
timer_mode
==
periodic_interrupt
)
{
LL_TIM_SetAutoReload
(
tim_dev
,
(
data
->
timer_irq_period_usec
*
10
)
-
1
);
LL_TIM_EnableIT_UPDATE
(
tim_dev
);
LL_TIM_EnableCounter
(
tim_dev
);
}
}
else
if
(
tim_dev
==
TIM4
)
{
if
(
data
->
timer_mode
==
incremental_coder
)
{
LL_TIM_EnableCounter
(
tim_dev
);
}
}
}
...
...
@@ -124,6 +183,37 @@ uint32_t timer_stm32_get_count(const struct device* dev)
/////
// Per-timer inits
void
init_timer_4
()
{
// Configure Timer in incremental coder mode
// Peripheral clock enable
LL_APB1_GRP1_EnableClock
(
LL_APB1_GRP1_PERIPH_TIM4
);
LL_TIM_InitTypeDef
TIM_InitStruct
=
{
0
};
TIM_InitStruct
.
Prescaler
=
0
;
TIM_InitStruct
.
CounterMode
=
LL_TIM_COUNTERMODE_UP
;
TIM_InitStruct
.
Autoreload
=
65535
;
TIM_InitStruct
.
ClockDivision
=
LL_TIM_CLOCKDIVISION_DIV1
;
LL_TIM_Init
(
TIM4
,
&
TIM_InitStruct
);
LL_TIM_EnableARRPreload
(
TIM4
);
LL_TIM_SetEncoderMode
(
TIM4
,
LL_TIM_ENCODERMODE_X4_TI12
);
LL_TIM_IC_SetActiveInput
(
TIM4
,
LL_TIM_CHANNEL_CH1
,
LL_TIM_ACTIVEINPUT_DIRECTTI
);
LL_TIM_IC_SetPrescaler
(
TIM4
,
LL_TIM_CHANNEL_CH1
,
LL_TIM_ICPSC_DIV1
);
LL_TIM_IC_SetFilter
(
TIM4
,
LL_TIM_CHANNEL_CH1
,
LL_TIM_IC_FILTER_FDIV16_N5
);
LL_TIM_IC_SetPolarity
(
TIM4
,
LL_TIM_CHANNEL_CH1
,
LL_TIM_IC_POLARITY_RISING
);
LL_TIM_IC_SetActiveInput
(
TIM4
,
LL_TIM_CHANNEL_CH2
,
LL_TIM_ACTIVEINPUT_DIRECTTI
);
LL_TIM_IC_SetPrescaler
(
TIM4
,
LL_TIM_CHANNEL_CH2
,
LL_TIM_ICPSC_DIV1
);
LL_TIM_IC_SetFilter
(
TIM4
,
LL_TIM_CHANNEL_CH2
,
LL_TIM_IC_FILTER_FDIV1
);
LL_TIM_IC_SetPolarity
(
TIM4
,
LL_TIM_CHANNEL_CH2
,
LL_TIM_IC_POLARITY_RISING
);
LL_TIM_SetTriggerOutput
(
TIM4
,
LL_TIM_TRGO_RESET
);
LL_TIM_DisableMasterSlaveMode
(
TIM4
);
LL_TIM_ConfigETR
(
TIM4
,
LL_TIM_ETR_POLARITY_NONINVERTED
,
LL_TIM_ETR_PRESCALER_DIV1
,
LL_TIM_ETR_FILTER_FDIV1
);
LL_TIM_ConfigIDX
(
TIM4
,
LL_TIM_INDEX_ALL
|
LL_TIM_INDEX_POSITION_DOWN_DOWN
|
LL_TIM_INDEX_UP_DOWN
);
LL_TIM_EnableEncoderIndex
(
TIM4
);
}
void
init_timer_6
()
{
LL_TIM_InitTypeDef
TIM_InitStruct
=
{
0
};
...
...
@@ -164,15 +254,38 @@ void init_timer_7()
/////
// Device definitions
// Timer4
#if DT_NODE_HAS_STATUS(TIMER4_NODELABEL, okay)
struct
stm32_timer_driver_data
timer4_data
=
{
.
timer_struct
=
TIM4
,
.
interrupt_line
=
TIMER4_INTERRUPT_LINE
,
.
interrupt_prio
=
TIMER4_INTERRUPT_PRIO
,
.
timer_irq_callback
=
NULL
};
DEVICE_DT_DEFINE
(
TIMER4_NODELABEL
,
timer_stm32_init
,
NULL
,
&
timer4_data
,
NULL
,
PRE_KERNEL_1
,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE
,
&
timer_funcs
);
#endif // Timer 4
// Timer 6
#if DT_NODE_HAS_STATUS(TIMER6_NODELABEL, okay)
struct
stm32_timer_driver_data
timer6_data
=
{
.
timer_struct
=
TIM6
,
.
interrupt_line
=
TIMER6_INTERRUPT_LINE
,
.
interrupt_prio
=
TIMER6_INTERRUPT_PRIO
,
.
timer_callback
=
NULL
.
timer_struct
=
TIM6
,
.
interrupt_line
=
TIMER6_INTERRUPT_LINE
,
.
interrupt_prio
=
TIMER6_INTERRUPT_PRIO
,
.
timer_
irq_
callback
=
NULL
};
DEVICE_DT_DEFINE
(
TIMER6_NODELABEL
,
...
...
@@ -192,10 +305,10 @@ DEVICE_DT_DEFINE(TIMER6_NODELABEL,
struct
stm32_timer_driver_data
timer7_data
=
{
.
timer_struct
=
TIM7
,
.
interrupt_line
=
TIMER7_INTERRUPT_LINE
,
.
interrupt_prio
=
TIMER7_INTERRUPT_PRIO
,
.
timer_callback
=
NULL
.
timer_struct
=
TIM7
,
.
interrupt_line
=
TIMER7_INTERRUPT_LINE
,
.
interrupt_prio
=
TIMER7_INTERRUPT_PRIO
,
.
timer_
irq_
callback
=
NULL
};
DEVICE_DT_DEFINE
(
TIMER7_NODELABEL
,
...
...
zephyr/modules/owntech_timer_driver/zephyr/src/stm32_timer_driver.h
View file @
3ae8c917
/*
* Copyright (c) 2021 LAAS-CNRS
* Copyright (c) 2021
-2022
LAAS-CNRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
...
...
@@ -18,7 +18,8 @@
*/
/**
* @author Clément Foucher <clement.foucher@laas.fr>
* @date 2022
* @author Clément Foucher <clement.foucher@laas.fr>
*/
#ifndef STM32_TIMER_DRIVER_H_
#define STM32_TIMER_DRIVER_H_
...
...
@@ -36,6 +37,10 @@ extern "C" {
#endif
#define TIMER4_NODELABEL DT_NODELABEL(timers4)
#define TIMER4_INTERRUPT_LINE DT_IRQN(TIMER4_NODELABEL)
#define TIMER4_INTERRUPT_PRIO DT_IRQ_BY_IDX(TIMER4_NODELABEL, 0, priority)
#define TIMER6_NODELABEL DT_NODELABEL(timers6)
#define TIMER6_INTERRUPT_LINE DT_IRQN(TIMER6_NODELABEL)
#define TIMER6_INTERRUPT_PRIO DT_IRQ_BY_IDX(TIMER6_NODELABEL, 0, priority)
...
...
@@ -44,30 +49,42 @@ extern "C" {
#define TIMER7_INTERRUPT_LINE DT_IRQN(TIMER7_NODELABEL)
#define TIMER7_INTERRUPT_PRIO DT_IRQ_BY_IDX(TIMER7_NODELABEL, 0, priority)
typedef
enum
{
periodic_interrupt
,
incremental_coder
}
timer_mode_t
;
/**
* Members of this structure marked with a "§"
* have to be set when calling DEVICE_DEFINE.
*
* timer_struct§: store the STM32 LL timer structure
* interrupt_line§: interrupt line number (if interrupt has to be enabled)
* interrupt_prio$: interrupt priority (if interrupt has to be enabled)
* timer_callback: user-defined, set by the timer_config call (if interrupt has to be enabled).
* Should be set to NULL in DEVICE_DEFINE
* timer_struct§: stores the STM32 LL timer structure
* interrupt_line§: interrupt line number (if interrupt has to be enabled)
* interrupt_prio$: interrupt priority (if interrupt has to be enabled)
* timer_mode: Mode in which the timer is configured.
* timer_irq_callback: user-defined, set by the timer_config call (if interrupt has to be enabled).
* Should be set to NULL in DEVICE_DEFINE
* timer_irq_period_usec : period of the irq in microseconds.
*/
struct
stm32_timer_driver_data
{
TIM_TypeDef
*
timer_struct
;
unsigned
int
interrupt_line
;
unsigned
int
interrupt_prio
;
timer_callback_t
timer_callback
;
unsigned
int
interrupt_line
;
unsigned
int
interrupt_prio
;
timer_mode_t
timer_mode
;
timer_callback_t
timer_irq_callback
;
uint32_t
timer_irq_period_usec
;
};
static
int
timer_stm32_init
(
const
struct
device
*
dev
);
void
timer_stm32_config
(
const
struct
device
*
dev
,
const
struct
timer_config_t
*
config
);
void
timer_stm32_start
(
const
struct
device
*
dev
,
uint32_t
t_usec
);
void
timer_stm32_start
(
const
struct
device
*
dev
);
uint32_t
timer_stm32_get_count
(
const
struct
device
*
dev
);
void
timer_stm32_clear
(
const
struct
device
*
dev
);
void
init_timer_4
();
void
init_timer_6
();
void
init_timer_7
();
...
...
zephyr/nucleo_g474re.overlay
View file @
3ae8c917
#include "dts/pinout.dtsi"
#include "dts/hrtim.dtsi"
#include "dts/adc-channels.dtsi"
#include "dts/ngnd.dtsi"
...
...
@@ -68,6 +69,11 @@
/* Timer */
/*********/
&timers4 {
pinctrl-0 = <&tim4_etr_pb3 &tim4_ch1_pb6 &tim4_ch2_pb7 >;
status = "okay";
};
&timers6 {
status = "okay";
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment