From c84887d021bc0d66352ead42366bd45868e1e4df Mon Sep 17 00:00:00 2001 From: heyuanjie87 Date: Wed, 2 Sep 2015 22:00:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=AE=9A=E6=97=B6=E5=99=A8?= =?UTF-8?q?=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bsp/stm32f40x/drivers/drv_hwtimer.c | 170 ++++++++ bsp/stm32f40x/drivers/drv_hwtimer.h | 8 + bsp/stm32f40x/rtconfig.h | 3 + components/drivers/hwtimer/SConscript | 8 + components/drivers/hwtimer/hwtimer.c | 397 +++++++++++++++++++ components/drivers/include/drivers/hwtimer.h | 83 ++++ components/drivers/include/rtdevice.h | 4 + components/finsh/cmd.c | 1 + include/rtdef.h | 3 +- 9 files changed, 676 insertions(+), 1 deletion(-) create mode 100644 bsp/stm32f40x/drivers/drv_hwtimer.c create mode 100644 bsp/stm32f40x/drivers/drv_hwtimer.h create mode 100644 components/drivers/hwtimer/SConscript create mode 100644 components/drivers/hwtimer/hwtimer.c create mode 100644 components/drivers/include/drivers/hwtimer.h diff --git a/bsp/stm32f40x/drivers/drv_hwtimer.c b/bsp/stm32f40x/drivers/drv_hwtimer.c new file mode 100644 index 000000000..ba6aed9dc --- /dev/null +++ b/bsp/stm32f40x/drivers/drv_hwtimer.c @@ -0,0 +1,170 @@ +/* + * File : gpio.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2015, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2015-09-02 heyuanjie87 the first version + */ + +#include +#include +#include +#include "drv_hwtimer.h" + +#ifdef RT_USING_HWTIMER + +static void NVIC_Configuration(void) +{ + NVIC_InitTypeDef NVIC_InitStructure; + + /* Enable the TIM5 global Interrupt */ + NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +} + +static void timer_init(rt_hwtimer_t *timer) +{ + TIM_TypeDef *tim; + + tim = (TIM_TypeDef *)timer->parent.user_data; + + TIM_DeInit(tim); + NVIC_Configuration(); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); + TIM_CounterModeConfig(tim, TIM_CounterMode_Up); +} + +static void timer_deinit(rt_hwtimer_t *timer) +{ + TIM_TypeDef *tim; + + tim = (TIM_TypeDef *)timer->parent.user_data; + TIM_DeInit(tim); +} + +static void timer_start(rt_hwtimer_t *timer, rt_hwtimer_mode_t opmode) +{ + TIM_TypeDef *tim; + uint16_t m; + + tim = (TIM_TypeDef *)timer->parent.user_data; + m = (opmode == HWTIMER_MODE_ONESHOT)? TIM_OPMode_Single : TIM_OPMode_Repetitive; + TIM_SelectOnePulseMode(tim, m); + TIM_Cmd(tim, ENABLE); +} + +static void timer_stop(rt_hwtimer_t *timer) +{ + TIM_TypeDef *tim; + + tim = (TIM_TypeDef *)timer->parent.user_data; + TIM_Cmd(tim, DISABLE); +} + +static rt_err_t timer_ctrl(rt_hwtimer_t *timer, rt_uint32_t cmd, void *arg) +{ + TIM_TypeDef *tim; + rt_err_t err = RT_EOK; + + tim = (TIM_TypeDef *)timer->parent.user_data; + + switch (cmd) + { + case HWTIMER_CTRL_FREQ_SET: + { + RCC_ClocksTypeDef clk; + uint16_t val; + rt_uint32_t freq; + + RCC_GetClocksFreq(&clk); + + freq = *((rt_uint32_t*)arg); + clk.PCLK1_Frequency *= 2; + val = clk.PCLK1_Frequency/freq; + + TIM_ITConfig(tim, TIM_IT_Update, DISABLE); + TIM_PrescalerConfig(tim, val - 1, TIM_PSCReloadMode_Immediate); + TIM_ClearITPendingBit(TIM2, TIM_IT_Update); + TIM_ITConfig(tim, TIM_IT_Update, ENABLE); + } + break; + default: + { + err = -RT_ENOSYS; + } + break; + } + + return err; +} + +static rt_uint32_t timer_counter_get(rt_hwtimer_t *timer) +{ + TIM_TypeDef *tim; + + tim = (TIM_TypeDef *)timer->parent.user_data; + + return TIM_GetCounter(tim); +} + +static rt_err_t timer_timeout_set(rt_hwtimer_t *timer, rt_uint32_t t) +{ + TIM_TypeDef *tim; + + tim = (TIM_TypeDef *)timer->parent.user_data; + TIM_SetAutoreload(tim, t); + + return RT_EOK; +} + +static const struct rt_hwtimer_info _info = +{ + 1000000, /* 可设置的最大计数时钟 */ + 2000, /* 可设置的最小计数时钟 */ + 0xFFFF, /* 最大超时值 */ + HWTIMER_CNTMODE_UP,/* 递增计数方式 */ +}; + +static const struct rt_hwtimer_ops _ops = +{ + timer_init, + timer_deinit, + timer_start, + timer_stop, + timer_timeout_set, + timer_counter_get, + timer_ctrl, +}; + +static rt_hwtimer_t _timer0; + +int stm32_hwtimer_init(void) +{ + _timer0.info = &_info; + _timer0.ops = &_ops; + + rt_device_hwtimer_register(&_timer0, "timer0", TIM2); + + return 0; +} + +void TIM2_IRQHandler(void) +{ + if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) + { + TIM_ClearITPendingBit(TIM2, TIM_IT_Update); + rt_device_hwtimer_isr(&_timer0); + } +} + +INIT_BOARD_EXPORT(stm32_hwtimer_init); +#endif diff --git a/bsp/stm32f40x/drivers/drv_hwtimer.h b/bsp/stm32f40x/drivers/drv_hwtimer.h new file mode 100644 index 000000000..b45b27dc0 --- /dev/null +++ b/bsp/stm32f40x/drivers/drv_hwtimer.h @@ -0,0 +1,8 @@ +#ifndef __DRV_HWTIMER_H__ +#define __DRV_HWTIMER_H__ + + +int stm32_hwtimer_init(void); + + +#endif diff --git a/bsp/stm32f40x/rtconfig.h b/bsp/stm32f40x/rtconfig.h index 88dfd0b61..9cca3f842 100644 --- a/bsp/stm32f40x/rtconfig.h +++ b/bsp/stm32f40x/rtconfig.h @@ -69,6 +69,9 @@ /* Using GPIO pin framework */ #define RT_USING_PIN +/* Using Hardware Timer framework */ +//#define RT_USING_HWTIMER + /* SECTION: Console options */ #define RT_USING_CONSOLE /* the buffer size of console*/ diff --git a/components/drivers/hwtimer/SConscript b/components/drivers/hwtimer/SConscript new file mode 100644 index 000000000..534e04b6c --- /dev/null +++ b/components/drivers/hwtimer/SConscript @@ -0,0 +1,8 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd + '/../include'] +group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_HWTIMER'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/hwtimer/hwtimer.c b/components/drivers/hwtimer/hwtimer.c new file mode 100644 index 000000000..0d2df8f8e --- /dev/null +++ b/components/drivers/hwtimer/hwtimer.c @@ -0,0 +1,397 @@ +/* + * File : hwtimer.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-08-31 heyuanjie87 first version + */ + +#include +#include + +rt_inline rt_err_t timeout_set(rt_hwtimer_t *timer, rt_hwtimerval_t *tv) +{ + rt_err_t err; + float overflow; + float timeout; + rt_uint32_t counter; + int i, index; + float tv_sec; + float devi_min = 1; + float devi; + + if (timer->ops->stop != RT_NULL) + { + timer->ops->stop(timer); + } + else + { + return -RT_ENOSYS; + } + + /* 把定时器溢出时间和定时时间换算成秒 */ + overflow = timer->info->maxcnt/(float)timer->freq; + tv_sec = tv->sec + tv->usec/(float)1000000; + + if (tv_sec < (1/(float)timer->freq)) + { + /* 定时时间小于计数周期 */ + i = 0; + timeout = 1/(float)timer->freq; + } + else + { + for (i = 1; i > 0; i ++) + { + timeout = tv_sec/i; + + if (timeout <= overflow) + { + counter = timeout*timer->freq; + devi = tv_sec - (counter/(float)timer->freq)*i; + /* 计算最小误差 */ + if (devi > devi_min) + { + i = index; + timeout = tv_sec/i; + break; + } + else if (devi == 0) + { + break; + } + else if (devi < devi_min) + { + devi_min = devi; + index = i; + } + } + } + } + + timer->cycles = i; + timer->reload = i; + timer->period_sec = timeout; + counter = timeout*timer->freq; + + if (timer->ops->timeout_set != RT_NULL) + { + err = timer->ops->timeout_set(timer, counter); + } + + return err; +} + +static rt_err_t rt_hwtimer_init(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + rt_hwtimer_t *timer; + + timer = (rt_hwtimer_t *)dev; + /* 尝试将默认计数频率设为1Mhz */ + if ((1000000 <= timer->info->maxfreq) && (1000000 >= timer->info->minfreq)) + { + timer->freq = 1000000; + } + else + { + timer->freq = timer->info->minfreq; + } + timer->mode = HWTIMER_MODE_ONESHOT; + timer->cycles = 0; + + if (timer->ops->init) + { + timer->ops->init(timer); + } + else + { + result = -RT_ENOSYS; + } + + return result; +} + +static rt_err_t rt_hwtimer_open(struct rt_device *dev, rt_uint16_t oflag) +{ + rt_err_t result = RT_EOK; + rt_hwtimer_t *timer; + + timer = (rt_hwtimer_t *)dev; + if (timer->ops->control != RT_NULL) + { + timer->ops->control(timer, HWTIMER_CTRL_FREQ_SET, &timer->freq); + } + else + { + result = -RT_ENOSYS; + } + + return result; +} + +static rt_err_t rt_hwtimer_close(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + rt_hwtimer_t *timer; + + timer = (rt_hwtimer_t*)dev; + if (timer->ops->deinit != RT_NULL) + { + timer->ops->deinit(timer); + } + else + { + result = -RT_ENOSYS; + } + + dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED; + dev->rx_indicate = RT_NULL; + + return result; +} + +static rt_size_t rt_hwtimer_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_hwtimer_t *timer; + rt_hwtimerval_t tv; + rt_uint32_t cnt; + float t; + + timer = (rt_hwtimer_t *)dev; + if (timer->ops->count_get == RT_NULL) + return 0; + + cnt = timer->ops->count_get(timer); + if (timer->info->cntmode == HWTIMER_CNTMODE_DW) + { + cnt = timer->info->maxcnt - cnt; + } + + t = timer->overflow * timer->period_sec + cnt/(float)timer->freq; + tv.sec = t; + tv.usec = (t - tv.sec) * 1000000; + size = size > sizeof(tv)? sizeof(tv) : size; + rt_memcpy(buffer, &tv, size); + + return size; +} + +static rt_size_t rt_hwtimer_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + rt_size_t len = 0; + rt_hwtimerval_t *t; + rt_err_t err; + + if (size == sizeof(rt_hwtimerval_t)) + { + t = (rt_hwtimerval_t*)buffer; + err = timeout_set((rt_hwtimer_t*)dev, t); + if (err == RT_EOK) + { + len = size; + } + } + + return len; +} + +static rt_err_t rt_hwtimer_control(struct rt_device *dev, rt_uint8_t cmd, void *args) +{ + rt_err_t result = RT_EOK; + rt_hwtimer_t *timer; + + timer = (rt_hwtimer_t *)dev; + + switch (cmd) + { + case HWTIMER_CTRL_START: + { + if (timer->ops->start != RT_NULL) + { + rt_hwtimer_mode_t opm; + + if ((timer->cycles <= 1) && (timer->mode == HWTIMER_MODE_ONESHOT)) + { + opm = HWTIMER_MODE_ONESHOT; + } + else + { + opm = HWTIMER_MODE_PERIOD; + } + + timer->overflow = 0; + + timer->ops->start(timer, opm); + } + else + { + result = -RT_ENOSYS; + } + } + break; + case HWTIMER_CTRL_STOP: + { + if (timer->ops->stop != RT_NULL) + { + timer->ops->stop(timer); + } + else + { + result = -RT_ENOSYS; + } + } + break; + case HWTIMER_CTRL_TIMEOUT_SET: + { + if (args == RT_NULL) + { + result = -RT_EEMPTY; + break; + } + + result = timeout_set(timer, (rt_hwtimerval_t*)args); + } + break; + case HWTIMER_CTRL_FREQ_SET: + { + rt_uint32_t *f; + + if (args == RT_NULL) + { + result = -RT_EEMPTY; + break; + } + + f = (rt_uint32_t*)args; + if ((*f > timer->info->maxfreq) || (*f < timer->info->minfreq)) + { + result = -RT_ERROR; + break; + } + + if (timer->ops->control != RT_NULL) + { + result = timer->ops->control(timer, cmd, args); + if (result == RT_EOK) + { + timer->freq = *f; + } + } + else + { + result = -RT_ENOSYS; + } + } + break; + case HWTIMER_CTRL_INFO_GET: + { + if (args == RT_NULL) + { + result = -RT_EEMPTY; + break; + } + + *((struct rt_hwtimer_info*)args) = *timer->info; + } + case HWTIMER_CTRL_MODE_SET: + { + rt_hwtimer_mode_t *m; + + if (args == RT_NULL) + { + result = -RT_EEMPTY; + break; + } + + m = (rt_hwtimer_mode_t*)args; + + if ((*m != HWTIMER_MODE_ONESHOT) && (*m != HWTIMER_MODE_PERIOD)) + { + result = -RT_ERROR; + break; + } + + timer->mode = *m; + } + break; + default: + { + result = -RT_ENOSYS; + } + break; + } + + return result; +} + +void rt_device_hwtimer_isr(rt_hwtimer_t *timer) +{ + RT_ASSERT(timer != RT_NULL); + + timer->overflow ++; + + if (timer->cycles != 0) + { + timer->cycles --; + } + + if (timer->cycles == 0) + { + timer->cycles = timer->reload; + + if (timer->mode == HWTIMER_MODE_ONESHOT) + { + if (timer->ops->stop != RT_NULL) + { + timer->ops->stop(timer); + } + } + + if (timer->parent.rx_indicate != RT_NULL) + { + timer->parent.rx_indicate(&timer->parent, sizeof(struct rt_hwtimerval)); + } + } +} + +rt_err_t rt_device_hwtimer_register(rt_hwtimer_t *timer, const char *name, void *user_data) +{ + struct rt_device *device; + + RT_ASSERT(timer != RT_NULL); + RT_ASSERT(timer->ops != RT_NULL); + RT_ASSERT(timer->info != RT_NULL); + + device = &(timer->parent); + + device->type = RT_Device_Class_HwTimer; + device->rx_indicate = RT_NULL; + device->tx_complete = RT_NULL; + + device->init = rt_hwtimer_init; + device->open = rt_hwtimer_open; + device->close = rt_hwtimer_close; + device->read = rt_hwtimer_read; + device->write = rt_hwtimer_write; + device->control = rt_hwtimer_control; + device->user_data = user_data; + + return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); +} diff --git a/components/drivers/include/drivers/hwtimer.h b/components/drivers/include/drivers/hwtimer.h new file mode 100644 index 000000000..e5b5d4661 --- /dev/null +++ b/components/drivers/include/drivers/hwtimer.h @@ -0,0 +1,83 @@ +#ifndef __HWTIMER_H__ +#define __HWTIMER_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* 定时器控制命令 */ +typedef enum +{ + HWTIMER_CTRL_TIMEOUT_SET = 0x01, /* 设置超时值 */ + HWTIMER_CTRL_FREQ_SET, /* 设置计数频率 */ + HWTIMER_CTRL_START, /* 启动定时器 */ + HWTIMER_CTRL_STOP, /* 停止定时器 */ + HWTIMER_CTRL_INFO_GET, /* 获取定时器特征信息 */ + HWTIMER_CTRL_MODE_SET /* 设置定时模式 */ +} rt_hwtimer_ctrl_t; + +/* 定时模式 */ +typedef enum +{ + HWTIMER_MODE_ONESHOT = 0x01, + HWTIMER_MODE_PERIOD +} rt_hwtimer_mode_t; + +/* 定时器计数值 */ +typedef struct rt_hwtimerval +{ + rt_int32_t sec; /* 秒 */ + rt_int32_t usec; /* 微秒 */ +} rt_hwtimerval_t; + +#define HWTIMER_CNTMODE_UP 0x01 /* 定时器递增计数方式 */ +#define HWTIMER_CNTMODE_DW 0x02 /* 定时器递减计数方式 */ + +struct rt_hwtimer_device; + +struct rt_hwtimer_ops +{ + void (*init)(struct rt_hwtimer_device *timer); + void (*deinit)(struct rt_hwtimer_device *timer); + void (*start)(struct rt_hwtimer_device *timer, rt_hwtimer_mode_t mode); + void (*stop)(struct rt_hwtimer_device *timer); + rt_err_t (*timeout_set)(struct rt_hwtimer_device *timer, rt_uint32_t t); + rt_uint32_t (*count_get)(struct rt_hwtimer_device *timer); + rt_err_t (*control)(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args); +}; + +/* 定时器基本参数(不可动态更改) */ +struct rt_hwtimer_info +{ + rt_int32_t maxfreq; /* 定时器支持的最大计数时钟 */ + rt_int32_t minfreq; /* 定时器支持的最小计数时钟 */ + rt_uint32_t maxcnt; /* 计数器最大超时值 */ + rt_uint8_t cntmode; /* 计数模式(递增/递减,应用层无需关心此参数) */ +}; + +typedef struct rt_hwtimer_device +{ + struct rt_device parent; + const struct rt_hwtimer_ops *ops; + const struct rt_hwtimer_info *info; + + /* 驱动层不用关心以下参数 */ + rt_int32_t freq; /* 计数频率 */ + rt_int32_t overflow; /* 溢出次数 */ + float period_sec; /* 溢出周期(s) */ + rt_int32_t cycles; /* 循环次数 */ + rt_int32_t reload; /* 重载cycles */ + rt_hwtimer_mode_t mode; /* 定时模式 */ +} rt_hwtimer_t; + +rt_err_t rt_device_hwtimer_register(rt_hwtimer_t *timer, const char *name, void *user_data); +void rt_device_hwtimer_isr(rt_hwtimer_t *timer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index f4d967831..475adebf8 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -370,6 +370,10 @@ rt_inline void rt_work_init(struct rt_work* work, void (*work_func)(struct rt_wo #include "drivers/can.h" #endif +#ifdef RT_USING_HWTIMER +#include "drivers/hwtimer.h" +#endif + #ifdef __cplusplus } #endif diff --git a/components/finsh/cmd.c b/components/finsh/cmd.c index 7d24a33d6..6e2ca631e 100644 --- a/components/finsh/cmd.c +++ b/components/finsh/cmd.c @@ -468,6 +468,7 @@ static long _list_device(struct rt_list_node *list) "Pipe", "Portal Device", "Miscellaneous Device", + "Hardware Timer Device", "Unknown" }; diff --git a/include/rtdef.h b/include/rtdef.h index 2587df9c4..eda5d5da7 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -759,7 +759,8 @@ enum rt_device_class_type RT_Device_Class_Pipe, /**< Pipe device */ RT_Device_Class_Portal, /**< Portal device */ RT_Device_Class_Miscellaneous, /**< Miscellaneous device */ - RT_Device_Class_Unknown /**< unknown device */ + RT_Device_Class_HwTimer, /**< Hardware timer device */ + RT_Device_Class_Unknown /**< unknown device */ }; /** -- GitLab