历史上的今天

今天是:2024年09月18日(星期三)

正在发生

2019年09月18日 | 第8章 自己写库-构建库函数雏形—零死角玩转STM32-F429系列

发布者:EternalSmile 来源: eefocus关键字:库函数  写库  STM32-F429系列 手机看文章 扫描二维码
随时随地手机看文章

本章参考资料:《STM32F4xx 中文参考手册》、《STM32F429规格书》


虽然我们上面用寄存器点亮了 LED,乍看一下好像代码也很简单,但是我们别侥幸以后就可以一直用寄存器开发。在用寄存器点亮 LED 的时候,我们会发现 STM32 的寄存器都是 32 位的,每次配置的时候都要对照着《STM32F4xx参考手册》中寄存器的说明,然后根据说明对每个控制的寄存器位写入特定参数,因此在配置的时候非常容易出错,而且代码还很不好理解,不便于维护。所以学习 STM32 最好的方法是用软件库,然后在软件库的基础上了解底层,学习遍所有寄存器。


8.1 什么是STM32函数库

以上所说的软件库是指"STM32标准函数库",它是由ST公司针对STM32提供的函数接口,即API (Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。


当我们调用库API的时候不需要挖空心思去了解库底层的寄存器操作,就像当年我们刚开始学习C语言的时候,用prinft()函数时只是学习它的使用格式,并没有去研究它的源码实现,但需要深入研究的时候,经过千锤百炼的库API源码就是最佳学习范例。


实际上,库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口。库开发方式与直接配置寄存器方式的区别见图 81。


图 81 开发方式对比图


8.2

为什么采用库来开发及学习?

在以前8位机时代的程序开发中,一般直接配置芯片的寄存器,控制芯片的工作方式,如中断,定时器等。配置的时候,常常要查阅寄存器表,看用到哪些配置位,为了配置某功能,该置1还是置0。这些都是很琐碎的、机械的工作,因为8位机的软件相对来说较简单,而且资源很有限,所以可以直接配置寄存器的方式来开发。


对于STM32,因为外设资源丰富,带来的必然是寄存器的数量和复杂度的增加,这时直接配置寄存器方式的缺陷就突显出来了:


(1)    开发速度慢


(2)    程序可读性差


(3)    维护复杂


这些缺陷直接影响了开发效率,程序维护成本,交流成本。库开发方式则正好弥补了这些缺陷。


而坚持采用直接配置寄存器的方式开发的程序员,会列举以下原因:


(1)    具体参数更直观


(2)    程序运行占用资源少


相对于库开发的方式,直接配置寄存器方式生成的代码量的确会少一点,但因为STM32有充足的资源,权衡库的优势与不足,绝大部分时候,我们愿意牺牲一点CPU资源,选择库开发。一般只有在对代码运行时间要求极苛刻的地方,才用直接配置寄存器的方式代替,如频繁调用的中断服务函数。


对于库开发与直接配置寄存器的方式,就好比编程是用汇编好还是用 C 好一样。在STM32F1系列刚推出函数库时引起程序员的激烈争论,但是,随着ST库的完善与大家对库的了解,更多的程序员选择了库开发。现在STM32F1系列和STM32F4系列各有一套自己的函数库,但是它们大部分是兼容的,F1和F4之间的程序移植,只需要小修改即可。而如果要移植用寄存器写的程序,我只想说:"呵呵"。


用库来进行开发,市场已有定论,用户群说明了一切,但对于STM32的学习仍然有人认为用寄存器好,而且汇编不是还没退出大学教材么?认为这种方法直观,能够了解到是配置了哪些寄存器,怎样配置寄存器。事实上,库函数的底层实现恰恰是直接配置寄存器方式的最佳例子,它代替我们完成了寄存器配置的工作,而想深入了解芯片是如何工作的话,只要直接查看库函数的最底层实现就能理解,相信你会为它严谨、优美的实现方式而陶醉,要想修炼C语言,就从ST的库开始吧。所以在以后的章节中,使用软件库是我们的重点,而且我们通过讲解库API去高效地学习STM32的寄存器,并不至于因为用库学习,就不会用寄存器控制STM32芯片。


8.3 实验:构建库函数雏形

虽然库的优点多多,但很多人对库还是很忌惮,因为一开始用库的时候有很多代码,很多文件,不知道如何入手。不知道您是否认同这么一句话:一切的恐惧都来源于认知的空缺。我们对库忌惮那是因为我们不知道什么是库,不知道库是怎么实现的。


接下来,我们在寄存器点亮 LED 的代码上继续完善,把代码一层层封装,实现库的最初的雏形,相信经过这一步的学习后,您对库的运用会游刃有余。这里我们只讲如何实现GPIO函数库,其他外设的我们直接参考ST标准库学习即可,不必自己写。


下面请打开本章配套例程"构建库函数雏形"来阅读理解,该例程是在上一章的基础上修改得来的。


8.3.1 修改寄存器地址封装

上一章中我们在操作寄存器的时候,操作的是都寄存器的绝对地址,如果每个外设寄存器都这样操作,那将非常麻烦。我们考虑到外设寄存器的地址都是基于外设基地址的偏移地址,都是在外设基地址上逐个连续递增的,每个寄存器占 32 个或者 16 个字节,这种方式跟结构体里面的成员类似。所以我们可以定义一种外设结构体,结构体的地址等于外设的基地址,结构体的成员等于寄存器,成员的排列顺序跟寄存器的顺序一样。这样我们操作寄存器的时候就不用每次都找到绝对地址,只要知道外设的基地址就可以操作外设的全部寄存器,即操作结构体的成员即可。


在工程中的"stm32f4xx.h"文件中,我们使用结构体封装GPIO及RCC外设的的寄存器,见代码清单 81。结构体成员的顺序按照寄存器的偏移地址从低到高排列,成员类型跟寄存器类型一样。如不理解C语言对寄存器的封的语法原理,请参考《C语言对寄存器的封装》小节。


代码清单 81 封装寄存器列表


1 //volatile表示易变的变量,防止编译器优化


2 #define __IO volatile


3 typedef unsigned int uint32_t;


4 typedef unsigned short uint16_t;


5


6 /* GPIO寄存器列表 */


7 typedef struct {


8 __IO uint32_t MODER; /*GPIO模式寄存器地址偏移: 0x00 */


9 __IO uint32_t OTYPER; /*GPIO输出类型寄存器地址偏移: 0x04 */


10 __IO uint32_t OSPEEDR; /*GPIO输出速度寄存器地址偏移: 0x08 */


11 __IO uint32_t PUPDR; /*GPIO上拉/下拉寄存器地址偏移: 0x0C */


12 __IO uint32_t IDR; /*GPIO输入数据寄存器地址偏移: 0x10 */


13 __IO uint32_t ODR; /*GPIO输出数据寄存器地址偏移: 0x14 */


14 __IO uint16_t BSRRL; /*GPIO置位/复位寄存器低16位部分地址偏移: 0x18 */


15 __IO uint16_t BSRRH; /*GPIO置位/复位寄存器高16位部分地址偏移: 0x1A */


16 __IO uint32_t LCKR; /*GPIO配置锁定寄存器地址偏移: 0x1C */


17 __IO uint32_t AFR[2]; /*GPIO复用功能配置寄存器地址偏移: 0x20-0x24 */


18 } GPIO_TypeDef;


19


20 /*RCC寄存器列表*/


21 typedef struct {


22 __IO uint32_t CR; /*!< RCC 时钟控制寄存器,地址偏移: 0x00 */


23 __IO uint32_t PLLCFGR; /*!< RCC PLL配置寄存器,地址偏移: 0x04 */


24 __IO uint32_t CFGR; /*!< RCC 时钟配置寄存器,地址偏移: 0x08 */


25 __IO uint32_t CIR; /*!< RCC 时钟中断寄存器,地址偏移: 0x0C */


26 __IO uint32_t AHB1RSTR; /*!< RCC AHB1 外设复位寄存器,地址偏移: 0x10 */


27 __IO uint32_t AHB2RSTR; /*!< RCC AHB2 外设复位寄存器,地址偏移: 0x14 */


28 __IO uint32_t AHB3RSTR; /*!< RCC AHB3 外设复位寄存器,地址偏移: 0x18 */


29 __IO uint32_t RESERVED0; /*!< 保留, 地址偏移:0x1C */


30 __IO uint32_t APB1RSTR; /*!< RCC APB1 外设复位寄存器,地址偏移: 0x20 */


31 __IO uint32_t APB2RSTR; /*!< RCC APB2 外设复位寄存器,地址偏移: 0x24*/


32 __IO uint32_t RESERVED1[2]; /*!< 保留,地址偏移:0x28-0x2C*/


33 __IO uint32_t AHB1ENR; /*!< RCC AHB1 外设时钟寄存器,地址偏移: 0x30 */


34 __IO uint32_t AHB2ENR; /*!< RCC AHB2 外设时钟寄存器,地址偏移: 0x34 */


35 __IO uint32_t AHB3ENR; /*!< RCC AHB3 外设时钟寄存器,地址偏移: 0x38 */


36 /*RCC后面还有很多寄存器,此处省略*/


37 } RCC_TypeDef;


这段代码在每个结构体成员前增加了一个"__IO"前缀,它的原型在这段代码的第一行,代表了C语言中的关键字"volatile",在C语言中该关键字用于表示变量是易变的,要求编译器不要优化。这些结构体内的成员,都代表着寄存器,而寄存器很多时候是由外设或STM32芯片状态修改的,也就是说即使CPU不执行代码修改这些变量,变量的值也有可能被外设修改、更新,所以每次使用这些变量的时候,我们都要求CPU去该变量的地址重新访问。若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从CPU的某个缓存获取该变量值,这时可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。


8.3.2 定义访问外设的结构体指针

以结构体的形式定义好了外设寄存器后,使用结构体前还需要给结构体的首地址赋值,才能访问到需要的寄存器。为方便操作,我们给每个外设都定义好指向它地址的结构体指针,见代码清单 82。


代码清单 82 指向外设首地址的结构体指针


1 /*定义GPIOA-H 寄存器结构体指针*/


2 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)


3 #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)


4 #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)


5 #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)


6 #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)


7 #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)


8 #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)


9 #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)


10


11 /*定义RCC外设寄存器结构体指针*/


12 #define RCC ((RCC_TypeDef *) RCC_BASE)


这些宏通过强制把外设的基地址转换成GPIO_TypeDef类型的地址,从而得到GPIOA、GPIOB等直接指向对应外设的指针,通过结构体的指针操作,即可访问对应外设的寄存器。


利用这些指针访问寄存器,我们把main文件里对应的代码修改掉,见代码清单 83。


代码清单 83 使用结构体指针方式控制LED灯


1 /**


2 * 主函数


3 */


4 int main(void)


5 {


6


7 RCC->AHB1ENR |= (1<<7);


8


9 /* LED 端口初始化 */


10


11 /*GPIOH MODER10清空*/


12 GPIOH->MODER &= ~( 0x03<< (2*10));


13 /*PH10 MODER10 = 01b 输出模式*/


14 GPIOH->MODER |= (1<<2*10);


15


16 /*GPIOH OTYPER10清空*/


17 GPIOH->OTYPER &= ~(1<<1*10);


18 /*PH10 OTYPER10 = 0b 推挽模式*/


19 GPIOH->OTYPER |= (0<<1*10);


20


21 /*GPIOH OSPEEDR10清空*/


22 GPIOH->OSPEEDR &= ~(0x03<<2*10);


23 /*PH10 OSPEEDR10 = 0b 速率2MHz*/


24 GPIOH->OSPEEDR |= (0<<2*10);


25


26 /*GPIOH PUPDR10清空*/


27 GPIOH->PUPDR &= ~(0x03<<2*10);


28 /*PH10 PUPDR10 = 01b 上拉模式*/


29 GPIOH->PUPDR |= (1<<2*10);


30


31 /*PH10 BSRR寄存器的 BR10置1,使引脚输出低电平*/


32 GPIOH->BSRRH |= (1<<10);


33


34 /*PH10 BSRR寄存器的 BS10置1,使引脚输出高电平*/


35 //GPIOH->BSRRL |= (1<<10);


36


37 while (1);


38


39 }


乍一看,除了最后一部分,把BSRR寄存器分成BSRRH和BSRRL两段,其它部分跟直接用绝对地址访问只是名字改了而已,用起来跟上一章没什么区别。这是因为我们现在只实现了库函数的基础,还没有定义库函数。


打好了地基,下面我们就来建高楼。接下来使用函数来封装GPIO的基本操作,方便以后应用的时候不需要再查询寄存器,而是直接通过调用这里定义的函数来实现。我们把针对GPIO外设操作的函数及其宏定义分别存放在"stm32f4xx_gpio.c"和"stm32f4xx_gpio.h"文件中。


定义位操作函数


在"stm32f4xx_gpio.c"文件定义两个位操作函数,分别用于控制引脚输出高电平和低电平,见代码清单 84。


代码清单 84 GPIO置位函数与复位函数的定义


1 /**


2 *函数功能:设置引脚为高电平


3 *参数说明:GPIOx:该参数为GPIO_TypeDef类型的指针,指向GPIO端口的地址


4 * GPIO_Pin:选择要设置的GPIO端口引脚,可输入宏GPIO_Pin_0-15,


5 * 表示GPIOx端口的0-15号引脚。


6 */


7 void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)


8 {


9 /*设置GPIOx端口BSRRL寄存器的第GPIO_Pin位,使其输出高电平*/


10 /*因为BSRR寄存器写0不影响,


11 宏GPIO_Pin只是对应位为1,其它位均为0,所以可以直接赋值*/


12


13 GPIOx->BSRRL = GPIO_Pin;


14 }


15


16 /**


17 *函数功能:设置引脚为低电平


18 *参数说明:GPIOx:该参数为GPIO_TypeDef类型的指针,指向GPIO端口的地址


19 * GPIO_Pin:选择要设置的GPIO端口引脚,可输入宏GPIO_Pin_0-15,


20 * 表示GPIOx端口的0-15号引脚。


21 */


22 void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)


23 {


24 /*设置GPIOx端口BSRRH寄存器的第GPIO_Pin位,使其输出低电平*/


25 /*因为BSRR寄存器写0不影响,


26 宏GPIO_Pin只是对应位为1,其它位均为0,所以可以直接赋值*/


27


28 GPIOx->BSRRH = GPIO_Pin;


29 }


这两个函数体内都是只有一个语句,对GPIOx的BSRRL或BSRRH寄存器赋值,从而设置引脚为高电平或低电平。其中GPIOx是一个指针变量,通过函数的输入参数我们可以修改它的值,如给它赋予GPIOA、GPIOB、GPIOH等结构体指针值,这个函数就可以控制相应的GPIOA、GPIOB、GPIOH等端口的输出。


对比我们前面对BSRR寄存器的赋值,都是用"|="操作来防止对其它数据位产生干扰的,为何此函数里的操作却直接用"="号赋值,这样不怕干扰其它数据位吗?见代码清单 85。


代码清单 85 赋值方式对比


1 /*使用 "|=" 来赋值*/


2 GPIOH->BSRRH |= (1<<10);


3 /*直接使用 "=" 号赋值*/


4 GPIOx->BSRRH = GPIO_Pin;


根据BSRR寄存器的特性,对它的数据位写"0",是不会影响输出的,只有对它的数据位写"1",才会控制引脚输出。对低16位写"1"输出高电平,对高16位写"1"输出低电平。也就是说,假如我们对BSRRH(高16位)直接用"="操作赋二进制值"0000 0000 0000 0001 b",它会控制GPIO的引脚0输出低电平,赋二进制值"0000 0000 0001 0000 b",它会控制GPIO引脚4输出低电平,而其它数据位由于是0,所以不会受到干扰。同理,对BSRRL(低16位)直接赋值也是如此,数据位为1的位输出高电平。代码清单 86 中的两种方式赋值,功能相同。


代码清单 86 BSRR寄存器赋值等效代码


1 /*使用 "|=" 来赋值*/


2 GPIOH->BSRRH |= (uint16_t)(1<<10);


3 /*直接使用"=" 来赋值,二进制数(0000 0100 0000 0000)*/


4 GPIOH->BSRRH = (uint16_t)(1<<10);


这两行代码功能等效,都把BSRRH的bit10设置为1,控制引脚10输出低电平,且其它引脚状态不变。但第二个语句操作效率是比较高的,因为"|="号包含了读写操作,而"="号只需要一个写操作。因此在定义位操作函数中我们使用后者。


利用这两个位操作函数,就可以方便地操作各种GPIO的引脚电平了,控制各种端口引脚的范例见代码清单 87。


代码清单 87 位操作函数使用范例


1


2 /*控制GPIOH的引脚10输出高电平*/


3 GPIO_SetBits(GPIOH,(uint16_t)(1<<10));


4 /*控制GPIOH的引脚10输出低电平*/


5 GPIO_ResetBits(GPIOH,(uint16_t)(1<<10));


6


7 /*控制GPIOH的引脚10、引脚11输出高电平,使用"|"同时控制多个引脚*/


8 GPIO_SetBits(GPIOH,(uint16_t)(1<<10)|(uint16_t)(1<<11));


9 /*控制GPIOH的引脚10、引脚11输出低电平*/


10 GPIO_ResetBits(GPIOH,(uint16_t)(1<<10)|(uint16_t)(1<<10));


11


12 /*控制GPIOA的引脚8输出高电平*/


13 GPIO_SetBits(GPIOA,(uint16_t)(1<<8));


14 /*控制GPIOB的引脚9输出低电平*/


15 GPIO_ResetBits(GPIOB,(uint16_t)(1<<9));


使用以上函数输入参数,设置引脚号时,还是稍感不便,为此我们把表示16个引脚的操作数都定义成宏,见代码清单 88。


代码清单 88 选择引脚参数的宏


1 /*GPIO引脚号定义*/


2 #define GPIO_Pin_0 (uint16_t)0x0001) /*!< 选择Pin0 (1<<0) */


3 #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< 选择Pin1 (1<<1)*/


4 #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< 选择Pin2 (1<<2)*/


5 #define GPIO_Pin_3 ((uint16_t)0x0008) /*!< 选择Pin3 (1<<3)*/


6 #define GPIO_Pin_4 ((uint16_t)0x0010) /*!< 选择Pin4 */


7 #define GPIO_Pin_5 ((uint16_t)0x0020) /*!< 选择Pin5 */


8 #define GPIO_Pin_6 ((uint16_t)0x0040) /*!< 选择Pin6 */


9 #define GPIO_Pin_7 ((uint16_t)0x0080) /*!< 选择Pin7 */


10 #define GPIO_Pin_8 ((uint16_t)0x0100) /*!< 选择Pin8 */


11 #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< 选择Pin9 */


12 #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< 选择Pin10 */


13 #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< 选择Pin11 */


14 #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< 选择Pin12 */


15 #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< 选择Pin13 */


16 #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< 选择Pin14 */


17 #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< 选择Pin15 */


18 #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< 选择全部引脚 */


这些宏代表的参数是某位置"1"其它位置"0"的数值,其中最后一个"GPIO_Pin_ALL"是所有数据位都为"1",所以用它可以一次控制设置整个端口的0-15所有引脚。利用这些宏, GPIO的控制代码可改为代码清单 89。


代码清单 89 使用位操作函数及宏控制GPIO


1


2 /*控制GPIOH的引脚10输出高电平*/


3 GPIO_SetBits(GPIOH,GPIO_Pin_10);


4 /*控制GPIOH的引脚10输出低电平*/


5 GPIO_ResetBits(GPIOH,GPIO_Pin_10);

[1] [2]
关键字:库函数  写库  STM32-F429系列 引用地址:第8章 自己写库-构建库函数雏形—零死角玩转STM32-F429系列

上一篇:第9章 初识STM32固件库—零死角玩转STM32-F429系列
下一篇:第7章 使用寄存器点亮LED灯—零死角玩转STM32-F429系列

推荐阅读

   根据市场分析机构IHS Markit最新公布的数据显示,面板厂商对于2019年的电视面板的出货量依然保持着相当乐观的态度,预计整体电视面板出货量将首度超过3亿片,相比于2018年增长8%。但是电视厂商却对这一数字保持保守态度。电视厂商预计,2019年电视出货量仅为2.62亿台,年增长率为3%。不难发现,面板厂商预计的出货量与电视厂商的出货量之间的差距,...
Xsens今天宣布MTi 600系列正式提供量产。全新系列体积细小却达到工业级性能,并以更具竞争力的价格为中档惯性运动传感器市场带来突破。此外,包含一个即插即用模板和传感器模块的全新MTi 600系列开发套装现在仅以449欧元发售。开发套装提供了全面的硬件环境为MTi 600系列传感器进行性能评估,并附送免费的MT软件套装,让用户充份体验MTi 系列传感器最受欢...
优派(ViewSonic)宣布推出新一代 LED 一体机,为大尺寸演示显示器产品线添加新生力量。不同于传统的 LED 显示屏,优派全新 LED 一体机不仅拥有超薄轻巧的显示屏,还可将图像显示、拼接、电源和控制系统整合到一台设备中,方便用户安装、操作和维护;还可与周围环境快速融为一体,为大堂、礼堂、会议室等不同应用场景的各类型用户,均可带来无缝的视觉体验...
随着新能源汽车赛道日益火热,占据市场先天优势的韩国电池制造商LG 、SK和三星正持续大力投资。但近期,三星SDI传出为筹集投资资金陷入僵局。据THE ELEC报道,三星SDI正在评估拆分电池业务来获得资金的办法。该公司内部消息人士透露,在最近一次高层会议上,这一计划得到了讨论。据称,这也是三星SDI高层会议上唯一被讨论的事情。不过据消息人士称,该公...

史海拾趣

问答坊 | AI 解惑

比较完整的12864资料

分享了很多大家有用的资料,不知道我的资料对大家有没有帮助…

查看全部问答∨

USB_ISP下载线制作方法

电路图,驱动,全在下面,还有声明一下,这是从别人那里转过来的,只为和大家分享, 芯片用的是CH341电路很稳定,很实用,简单。…

查看全部问答∨

LPC2148问题:只有接上JLINK程序才正常的运行

嗯,最近弄了一个LPC2148的板子。      一样的程序,我下载到LPC2131的板子的FLASH里,拔掉JLINK就可以正常的运行,下载到LPC2148的板子里就行.(操作过程是一样 的。) 这是我们的ADS设置:  程序包上 ...…

查看全部问答∨

开关电源的启动噪声(变压器吱一声)--如何解决

反激隔离开关电源的启动噪声 怎么解决; 上电的时候变压器响的都会吱 一声; 该电源为恒压恒流:25W,14串14并 变压器才有ER25/10 是电流一上电过大引起的吗?还是变压器太小?目前变压器不能加大了。  …

查看全部问答∨

望各位前辈指点前途!小小菜鸟路坎坷,金融危机谋生路!

    希望大家耐心看完我的经历,帮我指点迷津,痛哭不已,感激涕零。 先介绍小的情况:      男,深圳,年龄25,2007年三流大学毕业,计算机科学与技术 本科        06年大三暑假 ...…

查看全部问答∨

C8051 F005单片机可否利用内部时钟进行串口通讯

如题! 请教一下单片机高手,由于我的电路版上晶振电路老出问题,启动不成功。可否不用外部晶振而只用内部晶振,并进行串口通讯。 …

查看全部问答∨

用foxice烧写K9F1G08U0B NAND Flash 的0x0 地址失败

今天,我用foxice仿真器烧写 引导程序 到K9F1G08U0B NAND Flash的0x0地址,烧写/读取过程中系统提示operation successed,但是用edit32打开存盘的文件,却发现写的全是FFF..., 改换成烧写一个字符串进去也是同样的结果。如果把地址换成其它地址(如0 ...…

查看全部问答∨

通过at指令给手机拨号,语音提示下如何再次按数字键,类似于10086

我用at指令通过手机拨号到10086,然后听到提示音后再通过at指令发送相应的数字键 ATD10086;  //手机拨通 OK ??????????????? ATH  //挂机命令。 OK 但是手机接通后按提示音发送数字用什么at命令,请各位达人明示,多谢 ...…

查看全部问答∨

DirectDrawCreate执行异常

各位大侠,小弟有个项目要用DirectDraw,但是在执行到DirectDrawCreate时总是发生异常,实在是弄不明白,请各位帮帮忙,分析分析到底是什么问题。我的开发工具是PB5.0。…

查看全部问答∨

LM3S8962的耗电量

   LM3S8962具有256KB的FLASH,64KB的RAM,50 MHz的主频。     他的功率消耗如下(来自官方PDF):                  50M时,外设 ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved