几个重要c语言中的库函数数的实现

&1&#ifndef&__STDARG_H__&2&#define&__STDARG_H__&3&&4&#define&va_list&&&&&&&&&&&&__builtin_va_list&5&#define&va_start(ap,&last)&__builtin_va_start&(ap,&last)&6&#define&va_arg(ap,&type)&&&__builtin_va_arg&&&(ap,&type)&7&#define&va_end(ap)&&&&&&&&&__builtin_va_end&&&(ap)&8&&9&#endif10&C类型头文件 ctype.h里面都是些常用宏,比如 islower(c)判断c是不是小写字母,isdigit(c)判断c是不是十进制数码,isxdigit(c)判断c是不是十六进制数码isspace(c)判断c是不是空白字符ctype.h&1&#ifndef&__CTYPE_H__&2&#define&__CTYPE_H__&3&&4&#define&islower(c)&(((c)&&=&'a')&&&&((c)&&=&'z'))&5&#define&isupper(c)&(((c)&&=&'A')&&&&((c)&&=&'Z'))&6&#define&isalpha(c)&(islower&(c)&||&isupper&(c))&7&#define&isdigit(c)&(((c)&&=&'0')&&&&((c)&&=&'9'))&8&#define&isalnum(c)&(isalpha&(c)&||&isdigit&(c))&9&#define&ispunct(c)&&&&&&&&&&&&&&&&&&&&&&&&&&\10&&&&&((((c)&&=&0x21)&&&&((c)&&=&0x2f))&||&&&&\11&&&&&&(((c)&&=&0x3a)&&&&((c)&&=&0x40))&||&&&&\12&&&&&&(((c)&&=&0x5b)&&&&((c)&&=&0x60))&||&&&&\13&&&&&&(((c)&&=&0x7b)&&&&((c)&&=&0x7e)))14&#define&isgraph(c)&(((c)&&=&0x21)&&&&((c)&&=&0x7e))15&#define&isspace(c)&((((c)&&=&0x09)&&&&((c)&&=&0x0d))&||&((c)&==&'&'))16&#define&isprint(c)&(((c)&&=&0x20)&&&&((c)&&=&0x7e))17&#define&iscntrl(c)&(((c)&&=&0x1f)&||&((c)&==&0x7f))18&#define&isxdigit(c)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\19&&&&&(isdigit&(c)&||&(((c)&&=&'a')&&&&((c)&&=&'f'))&||&&&\20&&&&&(((c)&&=&'A')&&&&((c)&&=&'F')))21&22&#define&tolower(c)&(isupper&(c)&?&(c)+'a'-'A'&:&(c))23&#define&toupper(c)&(islower&(c)&?&(c)+'A'-'a'&:&(c))24&25&#endif26&字符串处理 string.h string.c关于 memcpy函数和mememove函数的区别请自行Googlestring.h&1&#ifndef&__STRING_H__&2&#define&__STRING_H__&3&&4&#include&"types.h"&5&&6&void&*memcpy&(void&*dest,&const&void&*src,&size_t&n);&7&void&*memmove&(void&*dest,&const&void&*src,&size_t&n);&8&char&*strcpy&(char&*dest,&const&char&*src);&9&char&*strncpy&(char&*dest,&const&char&*src,&size_t&n);10&int&strcmp&(const&char&*s1,&const&char&*s2);11&int&strncmp&(const&char&*s1,&const&char&*s2,&size_t&n);12&int&strlen&(const&char&*s);13&14&#endifstring.c&1&#include&"types.h"&2&#include&"string.h"&3&&4&void&*memcpy&(void&*dest,&const&void&*src,&size_t&n)&{&5&&&&&int&i;&6&&&&&for&(i&=&0;&i&&&(int)n;&i++)&*(char&*)(dest&+&i)&=&*(char&*)(src&+&i);&7&&&&&return&&8&}&9&10&void&*memmove&(void&*dest,&const&void&*src,&size_t&n)&{11&&&&&int&i;12&&&&&if&(src&&&dest)&13&&&&&&&&&for&(i&=&0;&i&&&(int)n;&i++)14&&&&&&&&&&&&&*(char&*)(dest&+&i)&=&*(char&*)(src&+&i);15&&&&&if&(src&&&dest)16&&&&&&&&&for&(i&=&(int)(n&-&1);&i&&=&0;&i--)17&&&&&&&&&&&&&*(char&*)(dest&+&i)&=&*(char&*)(src&+&i);18&&&&&return&19&}20&21&char&*strcpy&(char&*dest,&const&char&*src)&{22&&&&&int&i&=&0;23&&&&&do&{&dest[i]&=&src[i];&}&while&(src[i++]&!=&0);24&&&&&return&25&}26&27&char&*strncpy&(char&*dest,&const&char&*src,&size_t&n)&{28&&&&&int&i&=&0;29&&&&&while&((i&&&(int)n)&&&&(src[i]&!=&0))&{&dest[i]&=&src[i];&i++;&}30&&&&&while&(i&&&(int)n)&dest[i++]&=&0;31&&&&&return&32&}33&34&int&strcmp&(const&char&*s1,&const&char&*s2)&{35&&&&&int&i&=&0;36&&&&&while&((s1[i]&==&s2[i])&&&&(s1[i]&!=&0))&i++;37&&&&&return&s1[i]&-&s2[i];38&}39&40&int&strncmp&(const&char&*s1,&const&char&*s2,&size_t&n)&{41&&&&&int&i&=&0;42&&&&&while&((i&&&(int)n)&&&&(s1[i]&==&s2[i])&&&&(s1[i]&!=&0))&i++;43&&&&&return&(i&==&(int)n)&?&0&:&s1[i]&-&s2[i];44&}45&46&int&strlen&(const&char&*s)&{47&&&&&int&i&=&0;48&&&&&while&(s[i]&!=&0)&i++;49&&&&&return&i;50&}51&打印函数 vsprintf.h vsprintf.cvsprintf.h&1&#ifndef&__VSPRINTF_H__&2&#define&__VSPRINTF_H__&3&&4&#include&"stdarg.h"&5&&6&int&sprintf(char&*str,&const&char&*format,&);&&&&//&将输出重定向到缓冲区&str&7&int&vsprintf&(char&*str,&const&char&*format,&va_list&ap);&&&&//&这是一个中转函数,真正完成格式化功能&8&int&printk(const&char&*format,&);&&&&//&和&printf一样&9&10&#endifvsprintf.c&&1&#include&"types.h"&&2&#include&"ctype.h"&&3&#include&"console.h"&&4&#include&"stdarg.h"&&5&#include&"string.h"&&6&#include&"vsprintf.h"&&7&&&8&/*&flags&*/&&9&#define&LEFT&&&&0x01&10&#define&PLUS&&&&0x02&11&#define&SPACE&&&0x04&12&#define&SPECIAL&0x08&13&#define&ZERO&&&&0x10&14&#define&SIGN&&&&0x20&/*&signed&if&set&*/&15&#define&SMALL&&&0x40&/*&'abcdef'&if&set,&'ABCDEF'&otherwise&*/&16&&17&int32_t&get_wide&(const&char&**s);&18&void&number_to_string&(long&num,&int32_t&base,&int32_t&flags,&int32_t&wide,&int32_t&precision,&char&**s);&19&&20&int&sprintf(char&*str,&const&char&*format,)&{&21&&&&&va_list&&22&&&&&int32_t&&23&&&&&va_start&(args,&format);&24&&&&&res&=&vsprintf(str,&format,&args);&25&&&&&va_end&(args);&26&&&&&return&&27&}&28&&29&int32_t&vsprintf&(char&*str,&const&char&*format,&va_list&ap)&{&30&&&&&char&c;&31&&&&&char&*start&=&&32&&&&&int32_t&&33&&&&&int32_t&&34&&&&&int32_t&&35&&&&&int32_t&&36&&&&&char&*s;&37&&&&&int32_t&i,&len,&base;&38&&39&&&&&while&((c&=&*format++)&!=&0)&{&40&&&&&&&&&if&(c&!=&'%')&{&*str++&=&c;&continue;&}&41&&&&&&&&&if&(*format&==&'%')&{&*str++&=&'%';&format++;&continue;&}&42&&43&&&&&&&&&/*&get&flags&*/&44&&&&&&&&&flags&=&0;&45&&&&&&&&&while&(1)&{&46&&&&&&&&&&&&&if&(*format&==&'-')&{&flags&|=&LEFT;&&&&format++;&continue;&}&47&&&&&&&&&&&&&if&(*format&==&'+')&{&flags&|=+&&&&format++;&continue;&}&48&&&&&&&&&&&&&if&(*format&==&'&')&{&flags&|=&SPACE;&&&format++;&continue;&}&49&&&&&&&&&&&&&if&(*format&==&'#')&{&flags&|=&SPECIAL;&format++;&continue;&}&50&&&&&&&&&&&&&if&(*format&==&'0')&{&flags&|=&ZERO&&&;&format++;&continue;&}&51&&&&&&&&&&&&&break;&52&&&&&&&&&}&53&&54&&&&&&&&&/*&get&wide&*/&55&&&&&&&&&wide&=&-1;&56&&&&&&&&&if&(isdigit&(*format))&wide&=&get_wide&((const&char&**)(&format));&57&&&&&&&&&else&if&(*format&==&'*')&{&wide&=&va_arg&(ap,&int32_t);&format++;&}&58&&59&&&&&&&&&/*&get&precision&*/&60&&&&&&&&&precision&=&-1;&61&&&&&&&&&if&(*format&==&'.')&{&62&&&&&&&&&&&&&format++;&63&&&&&&&&&&&&&if&(isdigit&(*format))&64&&&&&&&&&&&&&&&&&precision&=&get_wide&((const&char&**)(&format));&65&&&&&&&&&&&&&else&if&(*format&==&'*')&{&66&&&&&&&&&&&&&&&&&precision&=&va_arg&(ap,&int32_t);&67&&&&&&&&&&&&&&&&&format++;&68&&&&&&&&&&&&&}&69&&&&&&&&&&&&&else&precision&=&0;&70&&&&&&&&&}&71&&72&&&&&&&&&/*&get&qualifier&*/&73&&&&&&&&&qualifier&=&-1;&74&&&&&&&&&if&((*format&==&'h')&||&(*format&==&'l'))&qualifier&=&*format++;&75&&76&&&&&&&&&/*&get&format&*/&77&&&&&&&&&switch&(*format++)&{&78&&&&&&&&&&&&&case&'i':&79&&&&&&&&&&&&&case&'d':&80&&&&&&&&&&&&&&&&&flags&|=&SIGN;&81&&&&&&&&&&&&&&&&&if&(precision&!=&-1)&flags&&=&~ZERO;&82&&&&&&&&&&&&&&&&&switch&(qualifier)&{&83&&&&&&&&&&&&&&&&&&&&&case&'h':&84&&&&&&&&&&&&&&&&&&&&&&&&&number_to_string&((short)&va_arg&(ap,&int32_t),&10,&flags,&85&&&&&&&&&&&&&&&&&&&&&&&&&&&&&wide,&precision,&&str);&86&&&&&&&&&&&&&&&&&&&&&&&&&break;&87&&&&&&&&&&&&&&&&&&&&&case&'l':&88&&&&&&&&&&&&&&&&&&&&&&&&&number_to_string&(va_arg&(ap,&long),&10,&flags,&89&&&&&&&&&&&&&&&&&&&&&&&&&&&&&wide,&precision,&&str);&90&&&&&&&&&&&&&&&&&&&&&&&&&break;&91&&&&&&&&&&&&&&&&&&&&&default:&92&&&&&&&&&&&&&&&&&&&&&&&&&number_to_string&(va_arg&(ap,&int32_t),&10,&flags,&93&&&&&&&&&&&&&&&&&&&&&&&&&&&&&wide,&precision,&&str);&94&&&&&&&&&&&&&&&&&&&&&&&&&break;&95&&&&&&&&&&&&&&&&&}&96&&&&&&&&&&&&&&&&&break;&97&&98&&&&&&&&&&&&&case&'u':&99&&&&&&&&&&&&&&&&&base&=&10;100&&&&&&&&&&&&&&&&&goto&num_to_str_without_101&102&&&&&&&&&&&&&case&'o':103&&&&&&&&&&&&&&&&&base&=&8;104&&&&&&&&&&&&&&&&&goto&num_to_str_without_105&106&&&&&&&&&&&&&case&'x':107&&&&&&&&&&&&&&&&&flags&|=&SMALL;108&&&&&&&&&&&&&case&'X':109&&&&&&&&&&&&&&&&&base&=&16;110&111&&&&&&&&&&&&&&&&&num_to_str_without_sign:112&&&&&&&&&&&&&&&&&flags&&=&(~PLUS&&&~SPACE);113&&&&&&&&&&&&&&&&&if&(precision&!=&-1)&flags&&=&~ZERO;114&&&&&&&&&&&&&&&&&switch&(qualifier)&{115&&&&&&&&&&&&&&&&&&&&&case&'h':116&&&&&&&&&&&&&&&&&&&&&&&&&number_to_string&((unsigned&short)&va_arg&(ap,&int32_t),&\117&&&&&&&&&&&&&&&&&&&&&&&&&&&&&base,&flags,&wide,&precision,&&str);118&&&&&&&&&&&&&&&&&&&&&&&&&break;119&&&&&&&&&&&&&&&&&&&&&case&'l':120&&&&&&&&&&&&&&&&&&&&&&&&&number_to_string&((unsigned&long)&va_arg&(ap,&long),&\121&&&&&&&&&&&&&&&&&&&&&&&&&&&&&base,&flags,&wide,&precision,&&str);122&&&&&&&&&&&&&&&&&&&&&&&&&break;123&&&&&&&&&&&&&&&&&&&&&default:124&&&&&&&&&&&&&&&&&&&&&&&&&number_to_string((uint32_t)va_arg&(ap,&int32_t),&\125&&&&&&&&&&&&&&&&&&&&&&&&&&base,&flags,&wide,&precision,&&str);126&&&&&&&&&&&&&&&&&&&&&&&&&break;127&&&&&&&&&&&&&&&&&}128&&&&&&&&&&&&&&&&&break;129&130&&&&&&&&&&&&&case&'s':131&&&&&&&&&&&&&&&&&s&=&va_arg&(ap,&char&*);132&&&&&&&&&&&&&&&&&len&=&strlen&(s);133&&&&&&&&&&&&&&&&&if&((precision&&=&0)&&&&(len&&&precision))&len&=&134&135&&&&&&&&&&&&&&&&&/*&rigth&justified&:&pad&with&spaces&*/136&&&&&&&&&&&&&&&&&if&(!(flags&&&LEFT))&while&(len&&&wide--)&*str++&=&'&';137&&&&&&&&&&&&&&&&&for&(i&=&0;&i&&&&i++)&*str++&=&*s++;138&&&&&&&&&&&&&&&&&/*&left&justified&:&pad&with&spaces&*/139&&&&&&&&&&&&&&&&&while&(len&&&wide--)&*str++&=&'&';140&&&&&&&&&&&&&&&&&break;141&142&&&&&&&&&&&&&case&'c':143&&&&&&&&&&&&&&&&&/*&rigth&justified&:&pad&with&spaces&*/144&&&&&&&&&&&&&&&&&if&(!(flags&&&LEFT))&while&(1&&&wide--)&*str++&=&'&';145&&&&&&&&&&&&&&&&&*str++&=&(unsigned&char)&va_arg&(ap,&int32_t);146&&&&&&&&&&&&&&&&&/*&left&justified&:&pad&with&spaces&*/147&&&&&&&&&&&&&&&&&while&(1&&&wide--)&*str++&=&'&';148&&&&&&&&&&&&&&&&&break;149&150&&&&&&&&&&&&&default:151&&&&&&&&&&&&&&&&&return&-1;152&&&&&&&&&}153&&&&&}154&&&&&*str&=&0;155&&&&&&&&&156&&&&&return&(int32_t)(str-start);157&}158&159&int32_t&printk(const&char&*format,&)160&{161&&&&&char&buff[1024];162&&&&&char&*str&=&163&&&&&va_list&164&&&&&int32_t&165&&&&&va_start&(args,&format);166&&&&&res&=&vsprintf&(str,&format,&args);167&&&&&va_end&(args);168&&&&&cons_write(buff);169&&&&&return&170&}171&172&int32_t&get_wide&(const&char&**s)&{173&&&&&int32_t&res&=&0;174&&&&&while&(isdigit&(**s))&res&=&10*res&+&*((*s)++)&-&'0';175&&&&&return&176&}177&178&#define&LONG_STRSIZE_BASE_2&32179&180&void&number_to_string&(long&num,&int32_t&base,&int32_t&flags,&int32_t&wide,&int32_t&precision,&char&**s)&{181&&&&&char&&&/*&sign&printed&:&'+',&'-',&'&',&or&0&(no&sign)&*/182&&&&&int32_t&num_cpy&=&183&&&&&unsigned&long&ul_num&=&(unsigned&long)&&/*&for&unsigned&format&*/184&185&&&&&/*&string&representation&of&num&(reversed)&*/186&&&&&char&tmp[LONG_STRSIZE_BASE_2];187&&&&&int32_t&i&=&0;&/*&number&of&figures&in&tmp&*/188&189&&&&&const&char&*digits&=&"ABCDEF";190&&&&&if&(flags&&&SMALL)&digits&=&"abcdef";191&192&&&&&if&((base&&&2)&||&(base&&&16))&return;193&194&&&&&if&((flags&&&SIGN)&&&&(num&&&0))&{&sign&=&'-';&num&=&-&}195&&&&&else&sign&=&(flags&&&PLUS)&?&'+'&:&((flags&&&SPACE)&?&'&'&:&0);196&&&&&if&(sign)&wide--;197&198&&&&&if&(flags&&&SPECIAL)&{199&&&&&&&&&if&((base&==&16)&&&&(num&!=&0))&wide&-=&2;&&/*&'0x'&or&'0X'&*/200&&&&&&&&&if&(base&==&8)&{&wide--;&precision--;&}&&&&&/*&'0'&*/201&&&&&}202&&&&&203&&&&&if&(num&==&0)&tmp[i++]&=&'0';204&&&&&/*&signed&format&*/205&&&&&if&(flags&&&SIGN)&{206&&&&&&&&&while&(num&!=&0)&{207&&&&&&&&&&&&&tmp[i++]&=&digits[num&%&base];208&&&&&&&&&&&&&num&=&num&/&base;209&&&&&&&&&}&210&&&&&}211&&&&&/*&unsigned&format&*/212&&&&&else&{213&&&&&&&&&while&(ul_num&!=&0)&{214&&&&&&&&&&&&&tmp[i++]&=&digits[ul_num&%&base];215&&&&&&&&&&&&&ul_num&=&ul_num&/&base;216&&&&&&&&&}&217&&&&&}218&219&&&&&if&(i&&&precision)&precision&=&i;220&&&&&wide&-=&221&222&&&&&/*&wide&=&number&of&padding&chars&*/223&&&&&/*&precision&=&number&of&figures&after&the&sign&and&the&special&chars&*/224&225&&&&&/*&right&justified&and&no&zeropad&:&pad&with&spaces&*/226&&&&&if&(!(flags&&&(LEFT&+&ZERO)))&while&(wide--&&&0)&*((*s)++)&=&'&';227&228&&&&&if&(sign)&*((*s)++)&=&229&&&&&if&((flags&&&SPECIAL)&&&&(num_cpy&!=&0))&{230&&&&&&&&&if&(base&==&8)&*((*s)++)&=&'0';231&&&&&&&&&if&(base&==&16)&{232&&&&&&&&&&&&&*((*s)++)&=&'0';233&&&&&&&&&&&&&if&(flags&&&SMALL)&*((*s)++)&=&'x';234&&&&&&&&&&&&&else&*((*s)++)&=&'X';235&&&&&&&&&}236&&&&&}237&238&&&&&/*&rigth&justified&and&zeropad&:&pad&with&0&*/239&&&&&if&(!(flags&&&LEFT))&while&(wide--&&&0)&*((*s)++)&=&'0';240&241&&&&&/*&print&num&*/242&&&&&while&(i&&&precision--)&*((*s)++)&=&'0';243&&&&&while&(i--&&&0)&*((*s)++)&=&tmp[i];244&245&&&&&/*&left&justfied&:&pad&with&spaces&*/246&&&&&while&(wide--&&&0)&*((*s)++)&=&'&';247&}248&这一节的程序和上节的一样,起基础作用,并没有关系到进程,内存管理等OS核心理论。但是它们的正确与否至关重要,这也是OS研究的繁琐之处,想要一探它的奥秘还必须先做一些杂七杂八的玩意儿。测试程序 test.c让该程序做一些算术题目,并格式化输出结果test.c&1&#include&"console.h"&2&#include&"vsprintf.h"&3&&4&int&test(void)&5&{&6&&&&&int&a&=&32;&7&&&&&int&b&=&68;&8&&&&&cons_clear();&9&&&&&printk("\n\n");10&&&&&printk("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&November&8,&2009\n");11&&&&&printk("Hi,John\n\n");&12&&&&&printk("Now,&I&am&capable&of&caculating&math&expressions,\n");13&&&&&printk("and&what's&more,&showing&the&results&to&our&friends.\n");14&&&&&printk("Look!\n\n");15&&&&&printk("&&&&&&&%d&+&%d&=&%d&\n",a,&b,&a+b);16&&&&&printk("&&&&&&&0x%x&+&0x%x&=&0x%x&\n",a,b,a+b);17&&&&&printk("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Your&Tinux\n");18&&&&&return&0;19&}运行结果如下:& & & & & & & &&PS: 此节的程序几乎全部来自CROCOS, 这是个开源OS项目, 它的新颖之处在于OS开发方式, 先像开发用户程序一样开发OS组件,最后再移到真实的硬件环境中。&&& 若有兴趣,请访问 Tinux Kernel源码下载反编译中的库函数识别的实现研究木_图文_百度文库
赠送免券下载特权
10W篇文档免费专享
部分付费文档8折起
每天抽奖多种福利
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
反编译中的库函数识别的实现研究木
&&反编译中的库函数识别的实现研究木
阅读已结束,下载本文需要
想免费下载更多文档?
定制HR最喜欢的简历
你可能喜欢C64xx的DSP怎样做复数运算和矩阵运算?有无这方面的库函数?(用的TI的C6416芯片)_百度知道
C64xx的DSP怎样做复数运算和矩阵运算?有无这方面的库函数?(用的TI的C6416芯片)
用的是c6416的开发板,做移动通信信号处理相关的内容。现在需要用到复数的四则运算,以及复数的矩阵运算,比如求矩阵的逆。不知道有没有相应的库函数能够实现呀?
DSP LIB回复 chy04024:C6416是C64x的核,支持的库少一些(重要的如IQMATH C64x是不支持的),所谓的库也仅仅支持一些基本的运算,高级的运算是需要自己来写的 具体支持哪些库,到TI网站一查便知
采纳率:26%
你好,我也遇到了同样的问题,请问你解决了吗,是如何解决的,我现在非常纠结。
为您推荐:
其他类似问题
复数的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。stm32 中断几个库函数实现过程分析 - 一个无聊的家伙 - 博客园
感谢原文作者:鱼竿的传说,这篇文章写得不错,转载自&http://www.cnblogs.com/chineseboy/archive//2956782.html
  闭门造车,两周了,经过各种的思考和求问,反复阅读了&&M3权威指南&&和&&stm32不完全手册&&的相关章节,以及开发板厂商的实验例程,对stm32这块中断终有所悟,是以记之。
  至于中断的什么优先级,什么优先级分组,使能之类的原理,就不再赘述。这里主要是记载以下如何使用中断,以及中断配置函数的实现过程,其中并叙述我曾经的疑惑和感悟。
  我的开发板里的中断例程是用按键控制一个灯亮和灭的两个状态。
  这个例程的实现过程如下描述:
第一步,将一个I/O口配置成中断输入模式。
  这里需要注意的是,GPIO本身是没有中断功能神马的。如果硬要使他产生中断输入方式,就得将相应的端口映射到相应的外部事件上去。而其他外设是有中断功能的,直接使能/失能其中断即可,比如USART,直接开启其发送/接收中断,那么USART也就相应的采取中断方式进行工作了。
  而这一点,是我开始很疑惑的:为啥GPIO口使用中断方式进行工作的时候就必须要映射到外部事件上去,而其他就不呢?百度网友的解惑是:比如USART产生的中断,是没有经过EXTI,而是直接将中断放入了NVIC;但是GPIO它作为中断源,是要经过EXTI的。仔细参看下面两个图,其实就会恍然大悟:
这第一步就是作为输入中断源的I/O口的相关配置,例程库函数如下:
1 void BUTTON_Configuration(void)
GPIO_InitTypeDef
GPIO_InitS
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_Init(GPIOD, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD , GPIO_PinSource11);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD , GPIO_PinSource12);
因为我板子上的例程是按键输入中断,所以函数名字就写的按键配置吧;
3~5行,就是gpio口的普通配置,学习单片机开天辟地,就先是gpio口,这个没啥稀奇的了,没啥可说的了;我的板子上是PD^11,PD^12两个端口作为中断输入的。
8行,注意这个时候,要使能GPIO口的复用时钟功能。
9~10行,就是将PD^11,PD^12映射到外部事件线上去。在keil中跳转到其函数实现中:
1 void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
tmp = ((uint32_t)0x0F) && (0x04 * (GPIO_PinSource & (uint8_t)0x03));
AFIO-&EXTICR[GPIO_PinSource && 0x02] &= ~
AFIO-&EXTICR[GPIO_PinSource && 0x02] |= (((uint32_t)GPIO_PortSource) && (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
5~6行,用库函数的都知道,就是两个宏定义,起到的作用是对相关的数据神马的进行正确性检查。
8行,GPIO_PinSource这个是外面BUTTON_Configuration()调用GPIO_EXTILineConfig()时传的参数。可能都不知道8行这个式子为啥要这么写。先看看我例程中是如何传的参:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD , GPIO_PinSource11);
也即是GPIO_PinSource &==&GPIO_PinSource11;那么GPIO_PinSource11是个什么东西呢:官方库已经这样定义了:
#define GPIO_PinSource11
((uint8_t)0x0B)
如果按照我的例程,8行这个式子中,tmp == 0x0F && (0x04 *(0x0B & 0x03)==&tmp = 0x0F000;先不管这个数字是个啥意思,反正它就是个数字,它其实是为了第9行寄存器的值服务的--&既然如此,如果用寄存器写的话,我可以直接给寄存器某个值,何必要山路十八弯呢?当然,库函数,是有通用性的,就像一个数学公式的作用。
9行:AFIO_EXTICR:外部事件控制&配置&寄存器。在数据手册中显示,有4个,它们分别对应的是各个外部事件exit_x&x = 0~15&。每个寄存器对应4个外部事件,于是4x4 = 16。,注意数据手册中的编号是从1开始的,而不是0开始的;但是,MDK中是0~3的。于是这里把相关值带进来一看,第九行其实就变成了如下式子:
AFIO-&EXTICR[GPIO_PinSource && 0x02] &= ~AFIO-&EXTICR[2] &= ~0xF000;==&AFIO-&EXTICR[2] &= 0x0FFF;什么意思?不就是将这个寄存器的第12~15清零吗?不就是将数据手册中第AFIO_EXTICR3寄存器的12~15清零么?再次注意:该寄存器在MDK中是0~3的,数据手册中的编号是从1开始的,而不是0开始的;这个样,9行以前的一切的操作,就是为了给该寄存器的某个位进行清零嘛,至于具体清哪一位,还得看你映射到哪一位。10行:引脚选择了,现在就选择这个引脚是哪个端口的,我的是D端口,那么按照官方对D端口的定义如下:
#define GPIO_PortSourceGPIOD
((uint8_t)0x03)
AFIO-&EXTICR[2]& |= 0x03 && 12;查看数据手册,恰好是设置成了D口的第11号端子上嘛。
于是第一步总结是:
1)外部事件寄存器相关位清零;
2)设置输入端子的编号
3)设置端口编号
注:一共有A~G个端口嘛,而每个端口上又有N多端子,这个参看GPIo那章数据手册。
悟出:库函数确实方便,具有公式效应,但山路十八弯,在这里,该例程中,要实现该功能,用寄存器,就两条语句嘛:
1 AFIO-&EXTICR[2] &= 0x0FFF;
2 AFIO-&EXTICR[2] |= 0x03 && 12;
1行:12~15清零
2行:0x03:表示是PD端口 ;0x03 && 12:表示在AFIO_EXTICR寄存器中的第12位开始写入0x03这个值,而括号中的2,说明是第三个寄存器,这样一组合,恰好就是PD^11了.描述起来一长串,如果看对照个看数据手册的话,就一目了然了。而这里唯一会让人凌乱的是:这个寄存器在数据手册上的编号和MDK中的编号不一致。自己在细读了&&stm32不完全手册&&才发现这个问题,开始可是百思不得其解呀。
总结下第一步要做的事情:
1)初始化I/O口为输入;
2)开启I/O复用时钟,并设置外部事件映射关联。
接下来是第二步:
&  第一步是将外部GPIO口映射到某外部事件上去。那么接下来,就该对该外部事件进行配置了,包括外部事件线路的选择、触发条件、使能。这里需要理解清楚的是,GPIO口和外部事件是各自独立的,它们并不是一体的---详细理解第一步,将GPIO口映射到某外部事件,可以看出GPIO和外部事件这个东西是两个不同的东西,在这里,GPIO的映射,无非就是GPIO口搭了外部事件的一趟顺风车。也所以,外部事件依然是要配置和使能的,不能说,将GPIO口映射到外部事件就可以产生中断了。
  接下来看看例程中外部事件的配置函数:
1 void EXTI_Configuration(void)
EXTI_InitTypeDef EXTI_InitS
/*PD11外部中断输入*/
EXTI_InitStructure.EXTI_Line = EXTI_Line11;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_I
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_F
EXTI_InitStructure.EXTI_LineCmd
EXTI_Init(&EXTI_InitStructure);
/*PD12外部中断输入*/
EXTI_InitStructure.EXTI_Line = EXTI_Line12;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_I
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_F
EXTI_InitStructure.EXTI_LineCmd
EXTI_Init(&EXTI_InitStructure);
可以看出,5~8;12~15行,无非就是在填充一个接头体。而真正实在的是9、16行:它们是外部事件初始化函数,把前面填充的结构体地址作为参数,传进EXTI_INit()。
在MDK中右键EXTI_Init()跳转到该函数的实现中去,代码如下:
1 void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
uint32_t tmp = 0;
/* Check the parameters */
assert_param(IS_EXTI_MODE(EXTI_InitStruct-&EXTI_Mode));
assert_param(IS_EXTI_TRIGGER(EXTI_InitStruct-&EXTI_Trigger));
assert_param(IS_EXTI_LINE(EXTI_InitStruct-&EXTI_Line));
assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct-&EXTI_LineCmd));
tmp = (uint32_t)EXTI_BASE;
if (EXTI_InitStruct-&EXTI_LineCmd != DISABLE)
/* Clear EXTI line configuration */
EXTI-&IMR &= ~EXTI_InitStruct-&EXTI_L
EXTI-&EMR &= ~EXTI_InitStruct-&EXTI_L
tmp += EXTI_InitStruct-&EXTI_M
*(__IO uint32_t *) tmp |= EXTI_InitStruct-&EXTI_L
/* Clear Rising Falling edge configuration */
EXTI-&RTSR &= ~EXTI_InitStruct-&EXTI_L
EXTI-&FTSR &= ~EXTI_InitStruct-&EXTI_L
/* Select the trigger for the selected external interrupts */
if (EXTI_InitStruct-&EXTI_Trigger == EXTI_Trigger_Rising_Falling)
/* Rising Falling edge */
EXTI-&RTSR |= EXTI_InitStruct-&EXTI_L
EXTI-&FTSR |= EXTI_InitStruct-&EXTI_L
tmp = (uint32_t)EXTI_BASE;
tmp += EXTI_InitStruct-&EXTI_T
*(__IO uint32_t *) tmp |= EXTI_InitStruct-&EXTI_L
tmp += EXTI_InitStruct-&EXTI_M
/* Disable the selected external lines */
*(__IO uint32_t *) tmp &= ~EXTI_InitStruct-&EXTI_L
第11行,就是存储了一个地址值。可以先看看它们是怎么定义的:
1 #define PERIPH_BASE
((uint32_t)0x) /*!& Peripheral base address in the alias region */
2 #define APB2PERIPH_BASE
(PERIPH_BASE + 0x10000)
3 #define EXTI_BASE
(APB2PERIPH_BASE + 0x0400)
如果对stm32框架掌握的足够熟悉,这里一眼便知端倪:GPIO口是挂在APB2上的,APB2是连在AHB总线上的,AHB再连到总线矩阵上的,环环相套,牵一发而动全身。附图如下:
&第16行、17行,分别是EXTI_IMR中断事件屏蔽寄存器和外部事件屏蔽寄存器,相应的位为0的时候,它们屏蔽相应线上的中断/事件请求。带入例程中的值算算
1 #define EXTI_Line11
((uint32_t)0x00800)
/*!& External interrupt line 11 */
2 #define EXTI_Line12
((uint32_t)0x01000)
/*!& External interrupt line 12 */
先将事件11的值带入下面两个式子,换算如下:
EXTI-&IMR &= ~0x00800;
EXTI-&EMR &= ~0x00800;
表示,将寄存器EXTI_IMR的11位清零,将EXTI_EMR的11位清零&下标从0开始&--意思是,将先关闭11号线上的中断/事件请求功能:有个原则是,在配置某一线上的中断或者事件之前,先将该线上的中断/事件清零,官方库写的比较严谨,所以这里先清零。那意思是,接下来就该对该线上的中断/事件进行配置了。
且看上面外部事件初始化函数中的31行和32行,EXTI_RTSR是上升沿边沿触发寄存器,EXTI_FTSR是下降沿边沿触发器,这里一看便知,是在配置边沿触发模式:边沿触发分3类--上、下、随便。当然,在配置边沿触发的时候,边沿触发器相应的位也应该清零,这在该函数的24、25行已经体现出来了。
另外注意模式的配置,看该函数中的19行:
1 tmp += EXTI_InitStruct-&EXTI_M
这个式子的表达的意思,接上文是:
tmp = (uint32_t)EXTI_BASE + EXTI_InitStruct-&EXTI_M
司马昭知心,路人皆知:就是某个地址 加上一个值后,这个地址也就变成了另外一个地址了。但为啥要这么做呢?
而外部事件中断模式的值定义为如下:
1 typedef enum
EXTI_Mode_Interrupt = 0x00,
EXTI_Mode_Event = 0x04
5 }EXTIMode_TypeDef;
可能不熟悉的&包括开始的我&,会发现,tmp在开始就只是对寄存器清零使用了下,就没再使用了啊?可是在该函数中,为啥后面还有一串关于tmp的代码呢?如果细读开始处赋值的意思就该明白了:是将某一个地址写入了该变量,那么在一定的条件下,该变量就相当于内存地址了嘛。注意是在一定的条件下,而函数中,也给出了这个条件,那就是类型强制转换。请看39行或者47行:
1 *(__IO uint32_t *) tmp |= EXTI_InitStruct-&EXTI_L
4 -----------------
5 *(__IO uint32_t *) tmp &= ~EXTI_InitStruct-&EXTI_L
__IO 表示volatile关键字;*(volatile unsigned int *) 表示了什么?指针的前面加*号,表示一个具体的值,也即是某个地址上具体的数据。这里有个链接:
&http://blog.sina.com.cn/s/blog_6b9f38c60100nv7i.html&
于是这里表示的是对某个内存地址进行操作,也就是向某个内存空间放入某个值,放入的是什么值呢:中断时间线的值;放入什么地址呢?
tmp = (uint32_t)EXTI_BASE + EXTI_InitStruct-&EXTI_M
注意,如果边沿触发不是随机的话,还要加上边沿触发寄存器的值。
由于以上几步开始没有理解,特别是我最后没有把中断事件线的值写入内存空间,导致我的第一次按键中断实验失败了,而且还不知道错在哪,在分析了官方代码后,方才知晓。
到这里,外部事件配置就完成了,可能细心的人会发现并没有使能外部事件/中断,是怎么回事呢?请看看上面tmp所代表的的内存空间地址是多少,然后对照着数据手册上查看下事件/中断屏蔽寄存器的地址是多少,再看看下面这句代码最后的值是多少,就一目了然了。
1 *(__IO uint32_t *) tmp |= EXTI_InitStruct-&EXTI_L
总结下第二步:就是配置外部事件的模式、触发条件、使能外部触发。而在这个过程中,注意先将某功能寄存器相关位清零后再写入值。
这个过程用不写成通用的库函数的话,那么可以用下面代码代替:
1 #define tmp  (*(volatile unsigned int*))0x
3 EXTI-&IMR &= ~0x00800;
4 EXTI-&EMR &= ~0x00800;
5 EXTI-&RTSR &= ~0x00800;
6 EXTI-&FTSR &= ~0x00800;
7 EXTI-&RTSR |= 0x00800;//如果配置成上升沿触发的话
8 tmp |= 0x00800;//使能中断/事件
第三步,现在就该配置中断了。也即是配置中断分组,以及中断优先级。当然,这并不是最后的工作。
中断配置函数如下:
1 void NVIC_Configuration(void)
NVIC_InitTypeDef NVIC_InitS
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/*外部中断线*/
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init(&NVIC_InitStructure);
4行,是一个中断分组函数,跳转,看实现:
1 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB-&AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityG
要查的SCB_AIRCR应用程序及复位控制寄存器的话,手头就必须备有&&xxxM3编程手册&&,&&xxxstm32数据手册&&中是没有这个寄存器的,当然还有许多都没有,比如滴答定时器等等。
其中 AIRCR_VECTKEY_MASK 就是一个钥匙,在改写SCB_AIRCR中的值的时候,必须填入这个值,否则修改无效。AIRCR_VECTKEY_MASK = 0x05FA0000;
而后面就跟着组的编号,注意这个编号不是简单的就是0,1,2,3,4.不能就这么简单的写入这个寄存器了。
注意这个寄存器的名字,它本身并不叫中断分组寄存器,而是借用了这个寄存器的某几位来进行中断分组--换句话说,这个寄存器可能要实现多种控制功能,而中断分组功能是在其中的某几位:当然,编程手册上说明了,是该寄存器的8~10位来进行中断分组控制;所以,组号需要进行位移到8~10位上来。如图:
&存疑部分&
当然,该函数中的值,也就影响到下面中断优先级的配置。在MDK中跳入NVIC_Init()中,看其实现过程:
1 void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct-&NVIC_IRQChannelCmd));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct-&NVIC_IRQChannelPreemptionPriority));
assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct-&NVIC_IRQChannelSubPriority));
if (NVIC_InitStruct-&NVIC_IRQChannelCmd != DISABLE)
/* Compute the Corresponding IRQ Priority --------------------------------*/
tmppriority = (0x700 - ((SCB-&AIRCR) & (uint32_t)0x700))&& 0x08;
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub &&
tmppriority = (uint32_t)NVIC_InitStruct-&NVIC_IRQChannelPreemptionPriority &&
tmppriority |=
NVIC_InitStruct-&NVIC_IRQChannelSubPriority &
tmppriority = tmppriority && 0x04;
NVIC-&IP[NVIC_InitStruct-&NVIC_IRQChannel] =
/* Enable the Selected IRQ Channels --------------------------------------*/
NVIC-&ISER[NVIC_InitStruct-&NVIC_IRQChannel && 0x05] =
(uint32_t)0x01 && (NVIC_InitStruct-&NVIC_IRQChannel & (uint8_t)0x1F);
/* Disable the Selected IRQ Channels -------------------------------------*/
NVIC-&ICER[NVIC_InitStruct-&NVIC_IRQChannel && 0x05] =
(uint32_t)0x01 && (NVIC_InitStruct-&NVIC_IRQChannel & (uint8_t)0x1F);
该函数中的第3行,有3个临时变量,分别是:优先级组--这个组的意思就是中断分组的那个意思,至于是否是那个值,看下文解释;抢断优先级;亚优先级。注意亚优先级初始值是0x0F--具体是啥原因呢?且看下面代码。
第13行,先取出中断控制复位神马神马寄存器的8~10 这3个位上的值& SCB-&AIRCR& 0x07&;然后经过一定的算法得出中断组号:假设SCB_AIRCR的8~10位为0x07,按照上面图来说,也就是第0组,那么按照它里面的这个式子来算,恰好结果tmppriority = 0;至于具体为啥会ST会想到用这么个式子来得出组号,我现在只可意会。
那么,假设是第0组,也就是tmppriority = 0;那么按照中断分组中,第0组的抢断优先级和亚优先级的规定来说,是全4位亚优先级。那么14和15行就很容易明白了,它就是在设置抢断优先级和亚优先级的比例位数。
那么17行,就是像临时变量中写入抢断优先级的值;
18行,就是向临时变量中写入亚优先级的值;注意抢断优先级和亚优先级一个在先一个在后,一旦分组一定,那么它们是会乖乖呆在自己位置上,不会去占用别人的位置的。
21行,就是把优先级配置值写入NVIC_IP寄存器,它是NVIC中断优先级配置寄存器,其定义在&&xxx编程手册&&中,同时可参看&&xx不完全手册&&。
在我例程版本库中,对该寄存器的定义方法是:
__IO uint8_t
/*!& Offset: 0x300
Interrupt Priority Register (8Bit wide) */
如果带入例程中的值来算:EXTI15_10_IRQn 的中断编号为40,即是:
NVIC-&IP[40] =
意思就是向相应的中断上赋予你的优先级配置值,这里特别要注意19行,优先级的值还向左移动了4位,这个是为啥呢?无法回答,带入例程中的值算算:我例程中是,分在中断2组,抢断为0,亚优先级有1,则带入:
tmppriority = 0x10;
NVIC-&IP[40] =0x10;
也即是IP[40]的第5位置1,可能还是无法理解,再次参看正点原子的书,得知,中断占IP寄存器的8位,但是只是用到了其高4位,而我们在设值的时候,也即是设它高4位的值。那么就符合这里的情况了:抢断优先级为0,亚优先级为1,组号为2。 也退出,亚优先级和抢断优先级一共4位,抢断处于高位。这4位中,它俩怎么分,就是一个此消彼长的情况,视不同的组而定了。至于组合优先级之间的对应关系,上图已经清楚的解释了。也即是SCB_AIRCR 这个寄存器处理的事情了。当然,这些寄存器,在&&xxx数据手册&&中也许是找不到的,而在编程手册中去找。
最后,补充一点就是怎么查看EXTI15_10_IRQn 的中断编号,这个数据手册上有,当然,官方也在库中给定义了。数据手册部分截图如下:
其中EXTI15_10中断的位置在该向量表的第40号位置,所以刚才上面的优先级寄存器引脚的值就是40,至于具体为啥要写成IP[40],而不是直接IP,这里有个小小编程技巧,是数组的妙用,属于C语言范畴了。
接下来的第24行和30行,是第一对相反作用的寄存器,即中断使能/失能,注意,别以为写0就可以失能,stm32不认为那样有效,必须是向失能寄存器中写1才可以的。同时注意一点是:这个寄存器定义成了数组,那么数组中就应该有N个元素。它这里就是某个元素相应的位,管理一个区域的中断。这里说复杂了,编程手册上已经讲的非常详细了。另外,它的定义方式如同&&xx不完全手册&&上所讲解的那样,但是定义的模式并不一定就完全相同:世界是变动的。
好吧,中断也使能了,总结下第三步:
1)中断分组:注意是在SCB_AIRCR寄存器的8~10.分组的同时,也就影响到了后面优先级的分配。
2) 配置优先级:其实质还是在SCB_AIRCR寄存器的4~7位,前面8~10位是什么值,那么这里4~7位就该怎么分了;当然,最后的配置值是写入了NVIC_IP的寄存器中了;
3)使能中断:在NVIC_ISER寄存器中,注意该寄存器定义的方式是数组,而非普通的变量,理解的时候,要理解一个数组是由N个变量组成即可&非严谨&。
第四步:中断服务函数:
  九九归一,终于来到了最后一步,也是所有中断必须要经历的一步。
  这里有个重点必须注意:所有中断服务函数的名字,ST官方已经取好了,而且还放在了中断向量表中了&也即是启动文件里&,如果你不自己写启动文件的话,那么你的中断服务函数的名字必须和ST官方的一样,不然,一个中断来了,找不到负责任的函数,它就只有悲剧去了。
看看例程吧:
1 void EXTI15_10_IRQHandler(void)
if(EXTI_GetITStatus(EXTI_Line11)!= RESET)
EXTI_ClearITPendingBit(EXTI_Line11);
Flag = 0x01;
if(EXTI_GetITStatus(EXTI_Line12)!= RESET)
EXTI_ClearITPendingBit(EXTI_Line12);
Flag = 0x02;
1 int main(void)
/* Add your application code here
SystemInit();
/*系统初始化*/
LED_Configuration();
BUTTON_Configuration();
NVIC_Configuration();
EXTI_Configuration();
/* Infinite loop */
switch(Flag)
case 0x01:
case 0x02:
没啥说的,就是定义了一个全局变量Flag,每次中断,都影响Flag的值,然后main函数判断该值,就这么简单。完了。
最后的总结:
  写了3个晚上,由于白天在公司太累了。但是又想深入理解下stm32的中断过程并加以整理,就只有一点一点的啃下来。个人的感觉是:stm32中,用的最多,也是最起码的,便是中断和时钟这两个模块。当然中断还可以自己修改向量表,这个我在s3c2440中就试过,由于时间有限,这里不再赘述。也由于自己被库函数给弄晕了头,在入手stm3的一个月内自己居然搭不起一个工程框架,这个是一个悲哀。用库函数也许简单、快捷。但我更倾向于寄存器:简洁。在网上看见有的老师说:只有高手才玩寄存器。但我师傅和我一致认为:新手就应该从寄存器开始,一点一点,知其然,更知其所以然。等到了随心所欲的时候,可以适当的用下库函数。当然,这仅是我们个人看法。从中断的库函数+寄存器走了一遭,中断并不是想象的那么难,明白了许多,理解了许多,心里非常的开心;如果有人能看到这些,并能对他有所帮助的话,那我就非常开心了。(over)

我要回帖

更多关于 c库函数实现 的文章

 

随机推荐