[时钟管理] arm 时间系统 2

*******************************************************************************************
cpu0 local timer clock_event_device注册过程:
start_kernel()->reset_init()->kernel_init()->smp_prepare_cpus()
   ->percpu_timer_setup()->twd_timer_setup()->clockevents_config_and_register() 
   ->twd_local_timer_common_register()->local_timer_register()
这里也是板级相关代码,不过内核提供了一些core方法。当然只有smp才会执行到这里的路径。
这个时候cpu1仍然处于wfi状态。不过随后cpu1被唤醒之后同样也会执行类似方法设置相应的local timer。
twd_timer是arm每个核内部的timer。

local timer的中断处理函数基本上就是调用了关联的clock_event_device->event_handler()方法。
clock_event_device对象是每cpu分配的对象。同上面一样,event_handler方法也是后面才初始化的。
static irqreturn_t twd_handler(int irq, void *dev_id)
{
        struct clock_event_device *evt = *(struct clock_event_device **)dev_id;

        if (twd_timer_ack()) {
                evt->event_handler(evt);
                return IRQ_HANDLED;
        }

        return IRQ_NONE;
}

可以看出来local timer同sp804 clock_event_device流程非常相似,中断处理函数都是调用了自己的
clock_event_device的event_handler方法。不过这两个clock_event_device代表了不同的硬件,他们的
set_next_event触发中断方法不同。rating优先级不一样,一般localtimer=350,sp804=300,就是说localtimer的
优先级要高。irq中断号当然也不一样。。

通过tick_check_new_device()新的clock_event_device注册到系统中的时候,rating有很大的作用。
当增加一个新的newdevice,如果系统当前没有device,则设置当前device等于newdevice。
当增加一个新的newdevice,如果newdevice的rating小于当前device的rating,则会忽略这个newdevice。
反之newdevice通过clockevents_exchange_device关闭当前的device,然后切换当前device等于newdevice。
接着通过tick_setup_device继续设置event_handler=tick_handle_periodic。
所以,这里local timer会替代之前注册的sp804 timer(只有cpu0才有)。从这里开始,sp804 timer0实际上等同
disable的,它的next_event中断时间是一个超大的值。而local timer中断不断的产生并且处理了。


*******************************************************************************************
接着看看tick_handle_periodic如何切换成hrtimer_interrupt的。

首先在init_jiffies_clocksource中注册了jiffies clocksource到kernel。
static int __init init_jiffies_clocksource(void)
{
        return clocksource_register(&clocksource_jiffies);
}
core_initcall(init_jiffies_clocksource);

clocksource_enqueue()实际上就是按照优先级rating将所有clocksource链接到clocksource_list上。
并且链表头部的clocksource是rating最高的。
clocksource_select()会选择best clocksource,等于当前最高优先级的clocksource,
或者是用户指定的clocksource(如果指定了的话)。这时,如果best clocksource不等于当前clocksource,
调用timekeeping_notify通知内核新的clocksource生效了。
int clocksource_register(struct clocksource *cs)
{
        /* calculate max adjustment for given mult/shift */
        cs->maxadj = clocksource_max_adjustment(cs);
        WARN_ONCE(cs->mult + cs->maxadj < cs->mult,
                "Clocksource %s might overflow on 11%% adjustment\n",
                cs->name);

        /* calculate max idle time permitted for this clocksource */
        cs->max_idle_ns = clocksource_max_deferment(cs);

        mutex_lock(&clocksource_mutex);
        clocksource_enqueue(cs);
        clocksource_enqueue_watchdog(cs);
        clocksource_select();
        mutex_unlock(&clocksource_mutex);
        return 0;
}

timekeeping_notify实际上会调用到change_clocksource方法来更新timekeeper的clocksource为best clocksource。
tick_clock_notify()会触发kernel去检测是否要切换到hrtimer模式。
void timekeeping_notify(struct clocksource *clock)
{
        struct timekeeper *tk = &timekeeper;

        if (tk->clock == clock)
                return;
        stop_machine(change_clocksource, clock, NULL);
        tick_clock_notify();
}

static int change_clocksource(void *data)
{
        struct timekeeper *tk = &timekeeper;
        struct clocksource *new, *old;
        unsigned long flags;

        new = (struct clocksource *) data;

        write_seqlock_irqsave(&tk->lock, flags);

        timekeeping_forward_now(tk);
        if (!new->enable || new->enable(new) == 0) {
                old = tk->clock;
                tk_setup_internals(tk, new);
                if (old->disable)
                        old->disable(old);
        }
        timekeeping_update(tk, true);

        write_sequnlock_irqrestore(&tk->lock, flags);

        return 0;
}

其实这个时候,sp804 timer1作为free run timer的clocksource早已经注册到系统中了。
所以现在注册jiffies clocksource的时候,就会触发timekeeping_notify来更新timekeeper的clocksource,
同时通知kernel去检查是否要切换hrtimer模式。


当前的时钟中断处理函数回调是tick_handle_periodic()
tick_periodic()->update_process_times->run_local_timers()激活TIMER_SOFTIRQ处理timer软中断。
hrtimer_run_queues如果kernel没有启动hrtimer模式的话,就在这里处理hrtimer事件。所以hrtimer api
仍然可以使用,但是是通过tick来触发的。
void run_local_timers(void)
{
        hrtimer_run_queues();
        raise_softirq(TIMER_SOFTIRQ);
}


timer软中断其实还在hrtimer_run_pending检测是否要切换到hrtimer模式。
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);        
static void run_timer_softirq(struct softirq_action *h)
{
        struct tvec_base *base = __this_cpu_read(tvec_bases);

        hrtimer_run_pending();

        if (time_after_eq(jiffies, base->timer_jiffies))
                __run_timers(base);
}
void hrtimer_run_pending(void)
{
        if (hrtimer_hres_active())
                return;

        /*
         * This _is_ ugly: We have to check in the softirq context,
         * whether we can switch to highres and / or nohz mode. The
         * clocksource switch happens in the timer interrupt with
         * xtime_lock held. Notification from there only sets the
         * check bit in the tick_oneshot code, otherwise we might
         * deadlock vs. xtime_lock.
         */
        if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
                hrtimer_switch_to_hres();
}

int tick_check_oneshot_change(int allow_nohz)
{
        struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
        //tick_clock_notify会设置check_clocks位。所以这里就会要继续检测了。
        //然后又清0,所以系统绝大部分事件这个方法下面都不会走的。
        if (!test_and_clear_bit(0, &ts->check_clocks))
                return 0;

        if (ts->nohz_mode != NOHZ_MODE_INACTIVE)
                return 0;

        if (!timekeeping_valid_for_hres() || !tick_is_oneshot_available())
                return 0;

        if (!allow_nohz)
                return 1;

        tick_nohz_switch_to_nohz();
        return 0;
}

检查timekeeper的clocksource时候有hrtimer能力。当然我们的sp804 timer1是符合要求的。
int timekeeping_valid_for_hres(void)
{
        struct timekeeper *tk = &timekeeper;
        unsigned long seq;
        int ret;

        do {
                seq = read_seqbegin(&tk->lock);

                ret = tk->clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;

        } while (read_seqretry(&tk->lock, seq));

        return ret;
}

然后通过hrtimer_switch_to_hres切换perodioc到oneshot hrtimer模式。
tick_setup_sched_timer()是通过hrtimer模拟的tick中断,当然系统在idle的时候,
这个hrtimer中断会关闭的。
static int hrtimer_switch_to_hres(void)
{
        int i, cpu = smp_processor_id();
        struct hrtimer_cpu_base *base = &per_cpu(hrtimer_bases, cpu);
        unsigned long flags;

        if (base->hres_active)
                return 1;

        local_irq_save(flags);

        if (tick_init_highres()) {
                local_irq_restore(flags);
                printk(KERN_WARNING "Could not switch to high resolution "
                                    "mode on CPU %d\n", cpu);
                return 0;
        }
        base->hres_active = 1;
        for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
                base->clock_base .resolution = KTIME_HIGH_RES;

        tick_setup_sched_timer();
        /* "Retrigger" the interrupt to get things going */
        retrigger_next_event(NULL);
        local_irq_restore(flags);
        return 1;
}

切换每cpu的tick_cpu_device->clock_event_device的event_handler方法为hrtimer_interrupt。
当然在tick_check_new_device的时候每cpu的tick_cpu_device->clock_event_device已经指向了localtimer device。
int tick_init_highres(void)
{
        return tick_switch_to_oneshot(hrtimer_interrupt);
}

todo:
cpu1 唤醒流程。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 基于ARM Cortex-M3的STM32嵌入式系统是一种高性能、低功耗的嵌入式系统,可广泛应用于各种领域。它的工作原理是基于ARM Cortex-M3内核的芯片,该内核具有较强的计算能力和多重存储器接口。STM32系列芯片结合了低功耗、高性能和丰富的外设功能,能够满足不同的应用需求。 STM32嵌入式系统的应用十分广泛。首先,它可以应用于工业自动化控制领域。通过与各种传感器和执行器的连接,STM32可以实现对温度、湿度、压力等各种参数的实时监测和控制。其低功耗特性也使其适合在长时间运行的智能仪表和机械设备中使用。 其次,STM32嵌入式系统可应用于物联网设备。通过内置的无线通信模块,如Wi-Fi、蓝牙和LoRa等,STM32可以与其他设备进行无线通信,并实现数据的传输和远程控制,适用于智能家居、智能城市等场景。 此外,STM32还可以应用于消费电子产品领域。其丰富的外设接口可以与LCD显示屏、摄像头、音频设备等进行连接,从而实现手机、平板电脑和音频播放器等产品的功能。 总体来说,基于ARM Cortex-M3的STM32嵌入式系统具有高性能、低功耗、丰富的外设接口等特点,可以广泛应用于工业自动化、物联网设备和消费电子等众多领域,为各类产品提供强大的计算和控制能力。 ### 回答2: 基于ARM Cortex-M3的STM32嵌入式系统是一种高性能、低功耗的解决方案。STM32系列微控制器基于ARM Cortex-M3内核,具有丰富的外设和功能,适用于各种嵌入式应用。 STM32的嵌入式系统原理是基于ARM Cortex-M3内核的运行机制。ARM Cortex-M3是一种32位的低功耗处理器,具有较高的性能和灵活性。STM32通过在芯片上集成了丰富的外设模块(例如通用输入输出、串行通信接口、模数转换器等)以及存储器和时钟管理模块,实现了完整的嵌入式系统功能。 嵌入式系统应用方面,STM32可广泛应用于工控、家电、汽车电子、医疗设备等领域。在工控领域,STM32可用于控制和监测生产过程,实现智能化管理。在家电领域,STM32可用于控制空调、洗衣机等家电设备,提高其性能和用户体验。在汽车电子领域,STM32可用于车载娱乐、导航和车身控制等系统。在医疗设备领域,STM32可用于监测和控制医疗设备,提高诊断和治疗效果。 STM32的优点包括低功耗、高性能、丰富的外设、易于开发和灵活的应用性。低功耗使得STM32在电池供电或移动设备应用中非常适用。高性能和丰富的外设让STM32能够处理复杂的任务和连接多种外部设备。易于开发的特性使得开发者能够快速上手并进行系统开发。灵活的应用性使得STM32能够应对不同的应用需求和系统架构。 总之,基于ARM Cortex-M3的STM32嵌入式系统以其优异的性能和丰富的外设被广泛应用于各种嵌入式系统领域,为各种应用提供了高效、低功耗的解决方案。 ### 回答3: STM32是意法半导体公司推出的一种基于ARM Cortex-M3内核的嵌入式系统。它采用了先进的芯片设计和先进的嵌入式开发技术,具有高性能、低功耗和丰富的外设接口等特点。 基于ARM Cortex-M3的STM32嵌入式系统原理主要包括以下几个方面: 1. ARM Cortex-M3内核:ARM Cortex-M3是一种高性能、低功耗的32位处理器内核。它具有高性能的运算能力和丰富的指令集,能够处理复杂的计算任务。 2. 外设接口:STM32嵌入式系统提供了丰富的外设接口,包括多个GPIO引脚、UART、SPI、I2C、ADC等。这些外设接口可与外部设备进行通信,实现数据的输入和输出。 3. 存储器:STM32嵌入式系统具有不同类型的存储器,包括闪存、RAM和EEPROM。闪存用于存储程序代码和数据,RAM用于临时数据存储,EEPROM用于非易失性数据存储。 4. 中断系统:STM32嵌入式系统具有强大的中断系统,可实现多任务并发执行。通过中断系统,可以及时响应外部事件,并执行相应的任务。 基于ARM Cortex-M3的STM32嵌入式系统应用广泛,主要包括以下几个方面: 1. 工业控制:STM32可以广泛应用于工业自动化领域,用于控制和调节各种设备和系统,如PLC、人机界面等。 2. 智能家居:STM32可以用于智能家居控制系统,实现对家居设备的远程控制和管理,如智能灯光、安防系统等。 3. 汽车电子:STM32可以应用于汽车电子领域,用于发动机控制、车载娱乐系统、导航系统等。 4. 医疗设备:STM32可以应用于医疗设备,如医疗监护仪、心电图仪等,实现对患者的监测和治疗。 总之,基于ARM Cortex-M3的STM32嵌入式系统具有高性能、低功耗和丰富的外设接口等特点,广泛应用于各个行业的嵌入式系统开发。它为开发者提供了强大的功能和丰富的接口选项,可以满足不同领域的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值