• Keil5如何生成bin文件

    方法一: 直接使用Keil自带的fromelf 工具 比如用命令行根据axf,生成bin: "D:\Program Files\Keil\ARM\ARMCC\bin\fromelf" --bin --output ./Objects/Led_Reg.bin  ./Objects/Led_Reg.axf 或者在编译器配置中添加: fromelf --bin --output ./Objects/Led_Reg.bin ./Objects/Led_Reg.axf 如果需要srce文件,方法也是类似: "D:\Program Files\Keil\ARM\ARMCC\bin\fromelf" --bin --output ./Objects/Led_Reg.srec ./Objects/Led_Reg.axf 方法二: 使用专用的工具,很多工具都支持不同格式的转换。 比如Hex2bin,可以将hex转换为bin 使用Hex2bin-2.5软件,只需将需要转换的hex文件,拖动到这个小软件上面就会生产所需的bin文件。 生产的bin文件与hex文件在同一个路径下,注意路径不要有中文。 https://sourceforge.net/projects/hex2bin

    11-25 77浏览
  • 数字滤波器在实际工程中的应用有哪些?

    数字滤波器可以分为两大部分:即经典滤波器和现代滤波器。经典滤波器就是假定输入信号x(n)中的有用成分和希望滤除成分分别位于不同的频带,因而我们通过一个线性系统就可以对噪声进行滤除,如果噪声和信号的频谱相...

    09-12 485浏览
  • HDLC数据链路控制协议的三种操作方式

    HDLC是通用的数据链路控制协议,当开始建立数据链路时,允许选用特定的操作方式。所谓链路操作方式,通俗地讲就是某站点以主站方式操作,还是以从站方式操作,或者是二者兼备。在链路上用于控制目的站称为主站,其...

    09-12 505浏览
  • 钽电解电容器在未来的发展方向是什么?

    继电器有很多具体的类型,比如干簧继电器、电磁继电器、计数继电器等等。为增进大家对继电器的认识,本文将对计数继电器、计数继电器的替代方法予以介绍。如果你对继电器或者对计数继电器具有兴趣,不妨和小编一起...

    09-12 461浏览
  • 轮式移动机器人控制系统的硬件设计要点

    1 引言 轮式移动机器人是机器人研究领域的一项重要内容,它集机械、电子、检测技术与智能控制于一体,是一个典型的智能控制系统。智能机器人比赛集高科技、娱乐、竞技于一体,已成为国际上广泛开展的高技术对抗活动...

    09-04 218浏览
  • 分得清自动驾驶中的DCU、MCU、MPU、SOC吗?

    本文探讨自动驾驶中的DCU、MCU和MPU在电子电气架构中的作用,比较了分布式与集中式架构的优势,以及ADAS系统从L0-L2+的发展历程。

    09-04 308浏览
  • Linux内核时钟系统和定时器实现

    1. Linux内核时钟系统和定时器实现 Linux 2.6.16之前,内核只支持低精度时钟,内核定时器的工作方式: 系统启动后,会读取时钟源设备(RTC, HPET,PIT…),初始化当前系统时间; 内核会根据HZ(系统定时器频率,节拍率)参数值,设置时钟事件设备,启动tick(节拍)中断。HZ表示1秒种产生多少个时钟硬件中断,tick就表示连续两个中断的间隔时间。在我电脑上,HZ=250, 一个tick = 1/HZ, 所以默认一个tick为4ms。 cat /boot/config-`uname -r` | grep 'CONFIG_HZ='CONFIG_HZ=250 设置时钟事件设备后,时钟事件设备会定时产生一个tick中断,触发时钟中断处理函数,更新系统时钟,并检测timer wheel,进行超时事件的处理。 在上面工作方式下,Linux 2.6.16 之前,内核软件定时器采用timer wheel多级时间轮的实现机制,维护操作系统的所有定时事件。timer wheel的触发是基于系统tick周期性中断。 所以说这之前,linux只能支持ms级别的时钟,随着时钟源硬件设备的精度提高和软件高精度计时的需求,有了高精度时钟的内核设计。 Linux 2.6.16 ,内核支持了高精度的时钟,内核采用新的定时器hrtimer,其实现逻辑和Linux 2.6.16 之前定时器逻辑区别: hrtimer采用红黑树进行高精度定时器的管理,而不是时间轮; 高精度时钟定时器不在依赖系统的tick中断,而是基于事件触发。 旧内核的定时器实现依赖于系统定时器硬件定期的tick,基于该tick,内核会扫描timer wheel处理超时事件,会更新jiffies,wall time(墙上时间,现实时间),process的使用时间等等工作。 新的内核不再会直接支持周期性的tick,新内核定时器框架采用了基于事件触发,而不是以前的周期性触发。新内核实现了hrtimer(high resolution timer),hrtimer的设计目的,就是为了解决time wheel的缺点: 低精度;timer wheel只能支持ms级别的精度,hrtimer可以支持ns级别; Timer wheel与内核其他模块的高耦合性; 新内核的hrtimer的触发和设置不像之前在定期的tick中断中进行,而是动态调整的,即基于事件触发,hrtimer的工作原理:通过将高精度时钟硬件的下次中断触发时间设置为红黑树中最早到期的 Timer 的时间,时钟到期后从红黑树中得到下一个 Timer 的到期时间,并设置硬件,如此循环反复。 在高精度时钟模式下,操作系统内核仍然需要周期性的tick中断,以便刷新内核的一些任务。前面可以知道,hrtimer是基于事件的,不会周期性出发tick中断,所以为了实现周期性的tick中断(dynamic tick):系统创建了一个模拟 tick 时钟的特殊 hrtimer,将其超时时间设置为一个tick时长,在超时回来后,完成对应的工作,然后再次设置下一个tick的超时时间,以此达到周期性tick中断的需求。 引入了dynamic tick,是为了能够在使用高精度时钟的同时节约能源,,这样会产生tickless 情况下,会跳过一些 tick。这里只是简单介绍,有兴趣可以读kernel源码。 上图1是Linux 2.6.16以来内核定时器实现的结构, 新内核对相关的时间硬件设备进行了统一的封装,定义了主要有下面两个结构: 时钟源设备(closk source device):抽象那些能够提供计时功能的系统硬件,比如 RTC(Real Time Clock)、TSC(Time Stamp Counter),HPET,ACPI PM-Timer,PIT等。不同时钟源提供的精度不一样,现在pc大都是支持高精度模式(high-resolution mode)也支持低精度模式(low-resolution mode)。 时钟事件设备(clock event device):系统中可以触发 one-shot(单次)或者周期性中断的设备都可以作为时钟事件设备。 当前内核同时存在新旧timer wheel 和 hrtimer两套timer的实现,内核启动后会进行从低精度模式到高精度时钟模式的切换,hrtimer模拟的tick中断将驱动传统的低精度定时器系统(基于时间轮)和内核进程调度。 内核定时器系统增加了hrtimer之后,对于用户层开放的定时器相关接口基本都是通过hrtimer进行实现的,从内核源码可以看到: * These timers are currently used for: * - itimers * - POSIX timers * - nanosleep * - precise in-kernel timing * 2. 用户层定时器API接口 上面介绍完linux内核定时器的实现后,下面简单说一下,基于内核定时器实现的,对用户层开放的定时器API:间隔定时器itimer和POSIX定时器。 2.1 常见定时功能的API:sleep系列 在介绍itimer和POSIX定时器之前,我们先看看我们经常遇到过具有定时功能的库函数API接口: alarm()sleep()usleep()nanosleep() alarm: alarm()函数可以设置一个定时器,在特定时间超时,并产生SIGALRM信号,如果不忽略或不捕捉该信号,该进程会被终止。 #include unsigned int alarm(unsigned int seconds); return:0或到期剩余秒数 那么alarm在是如何实现的?Glibc中alarm是基于间隔定时器itimer来实现的(文章后面会说到itimer是基于hrtimer实现的)。如下alarm在库函数下的实现,alarm调用了setitimer系统调用: unsigned int alarm (seconds) unsigned int seconds;{ ... if (__setitimer (ITIMER_REAL, &new, &old) < 0) return 0; ...}libc_hidden_def (alarm) sleep: sleep和alarm的功能类似,不过sleep会让进程挂起, 在定时器超时内核会产生SIGALRM信号,如果不忽略或不捕捉该信号,该进程会被终止。 那么sleep是如何实现的?Glibc的sleep实现如下:可见其实调用alarm实现的,在alarm的基础上注册了SIGALRM信号处理函数,用于在定时器到期时,捕获到信号,回到睡眠的地方。所以其实可以看出sleep就是对alarm的特化。 unsigned int__sleep (unsigned int seconds){ ... struct sigaction act, oact; ... //注册信号回调函数 act.sa_handler = sleep_handler; act.sa_flags = 0; act.sa_mask = oset; if (sigaction (SIGALRM, &act, &oact) < 0) return seconds; ... //调用alarm API进行操作 remaining = alarm (seconds); }weak_alias (__sleep, sleep) usleep: usleep支持精度更高的微妙级别的定时操作, int usleep (useconds_t useconds){ struct timespec ts = { .tv_sec = (long int) (useconds / 1000000), .tv_nsec = (long int) (useconds % 1000000) * 1000ul }; /* Note the usleep() is a cancellation point. But since we call nanosleep() which itself is a cancellation point we do not have to do anything here. */ return __nanosleep (&ts, NULL);} Bsd的usleep实现如下: int usleep (useconds) useconds_t useconds;{ struct timeval delay; delay.tv_sec = 0; delay.tv_usec = useconds; return __select (0, (fd_set *) NULL, (fd_set *) NULL, (fd_set *) NULL, &delay);} nanosleep: nanosleep()glibc的API是直接调用linux内核的nanosleep,内核的nanosleep采用了hrtimer进行实现。 alarm(), sleep()系列,以及后面的间隔定时器itimer都是基于SIGALRM信号进行触发的。所以它们是不能同时使用的。 2.2 间隔定时器itimer 间隔定时器的接口如下: #include int getitimer(int which, struct itimerval *curr_value);int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);结构定义:struct itimerval { struct timeval it_interval; /* next value :间隔时间*/ struct timeval it_value; /* current value:到期时间*/};struct timeval { time_t tv_sec; /* seconds */ s 可以通过调用上面两个API接口来设置和获取间隔定时器信息。系统为每个进程提供了3种itimer,每种定时器在不同时间域递减,当定时器超时,就会向进程发送一个信号,并且重置定时器。3种定时器的类型,如下表所示: 表1 参数which与定时器类型 在Linux 2.6.16 之前,itimer的实现是基于内核定时器timer wheel封装成的定时器接口。内核封装的定时器接口是提供给其他内核模块使用,也是其他定时器的基础。itimer通过内核定时器的封装,生成提供给用户层使用的接口setitimer和getitimer。内核定时器timer wheel提供的内核态调用接口为:可参考 add_timer() del_timer() init_timer() 在Linux 2.6.16 以来,itimer不再采用基于timer wheel的内核定时器进行实现。而是换成了高精度的内核定时器hrtimer进行实现。 如果定时器超时时,进程是处于运行状态,那么超时的信号会被立刻传送给该进程,否则信号会被延迟传送,延迟时间要根据系统的负载情况。所以这里有一个BUG:当系统负载很重的情况下,对于ITIMER_REAL定时器有可能在上一次超时信号传递完成前再次超时,这样就会导致第二次超时的信号丢失。 每个进程中同一种定时器只能使用一次。 该系统调用在POSIX.1-2001中定义了,但在POSIX.1-2008中已被废弃。所以建议使用POSIX定时器API(timer_gettime, timer_settime)代替。 函数alarm本质上设置的是低精确、非重载的ITIMER_REAL类定时器,它只能精确到秒,并且每次设置只能产生一次定时。函数setitimer 设置的定时器则不同,它们不但可以计时到微妙(理论上),还能自动循环定时。在一个Unix进程中,不能同时使用alarm和ITIMER_REAL类定时器。 下面是测试代码: #include #include #include #include void sig_handler(int signo){ std::cout<<"recieve sigal: "<

    08-21 177浏览
  • 简述STM32Cortex-M3

    总结了STM32的一些基本知识,希望能为STM32的学习​打好基础。

    08-20 156浏览
  • 嵌入式开发中的C语言特性

    本文面向的,正是使用单片机、ARM7、Cortex-M3这类微控制器的编程人员。从C语言特性入手,讨论如何编写优质的嵌入式C程序。

    08-20 136浏览
  • 如何在STM32中分配和管理栈空间

    学习STM32单片机的时候,总是能遇到“堆栈”这个概念。分享本文,希望对你理解堆栈有帮助。

    08-13 335浏览
正在努力加载更多...
广告