单片机的一些总结

  1. 按键没有按下的时候是高电平,按下时低电平。(接地)
  2. 当型循环,输入空语句可以停止整个主程序的循环。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

STM32小说明

1、数据手册标注FT的IO口,都是兼容5V。有ADC都不兼容5V。

2、重映射?F103有效,F4无重映射概念。

3、ADC/PI/PWM

原理图

  1. 波特率一般用于描述串口通讯的速度、速率的指标,指串口每秒能传输多少位数据
    所谓波特率是比特每秒的翻译,就是位每秒的传输速度单位
    比如波特率为9600,即传输速度是9600bit/s,等于1200字节每秒的传输速度,那么19200bit/s=2400字节每秒,比9600bit/s快一倍。
  2.  

跳线帽的作用是控制线路板上电流流动的小开关。

1  主板跳线是主机板上的手动开关,通过跳线帽连接不同的跳线PIN,可以改变主板电路;

2 主板上最常见的跳线主要有两种,一种是只有两根针,另一部分是跳线帽;

3 跳线帽,这是一个可以活动的部件,外层是绝缘塑料,内层是导电材料,可以插在跳线针上面,将两根跳线针连接起来;

4 当跳线帽扣在两根跳线针上时是接通状态,有电流通过,我们称之为ON;

5 反之不扣上跳线帽时,就说明是断开的,称之为OFF。

 

6、tx是发送(transport),rx是接收(receive)。
7、user文件夹 用来存放main函数、启动文件     OBJ主要用来生成过程文件,hex文件(flymcu直接搞定,不需要烧录器)
 
 
8、
 
 

 

 

9、USART

 编辑

USART:(Universal Synchronous/Asynchronous Receiver/Transmitter)

通用同步/异步串行接收/发送器

USART是一个全双工通用同步/异步串行收发模块,该接口是一个高度灵活的串行通信设备。

中文名

USART

外文名

Universal Synchronous/Asynchronous Receiver/Transmitter

主要特点

支持同步和异步操作

结构组成

时钟发生器、数据发送器和接收器

三大部分

时钟发生器、数据发送器和接收器

 

  1. Asynchronous

 同步和异步通常用来形容一次方法调用。

同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

 

 

 

 

 

 

 

 

 

  1. FWlib叫固件库
    FW的单词是Firmware
    即固件的意思。
    STM32的固件库包含C文件和H文件,
    主要用于存放STM32的一些寄存器的定义及一些底层驱动函数

12.

CMSISARM公司与多家不同的芯片和软件供应商一起紧密合作定义的,提供了内核与外设、实时操作系统和中间设备之间的通用接口。

13gpio

 General Purpose Input Output (通用输入/输出)简称为GPIO,或总线扩展器,人们利用工业标准I2CSMBusSPI接口简化了I/O口的扩展。当微控制器或芯片组没有足够的I/O端口,或当系统需要采用远端串行通信或控制时,GPIO产品能够提供额外的控制和监视功能。

还可以复用为外设功能引脚:如看门狗、定时器

14

https://baike.baidu.com/item/%E5%BC%95%E8%84%9A/10879873?fr=aladdin

 

引脚

引脚,又叫管脚,英文叫Pin。就是从集成电路(芯片)内部电路引出与外围电路的接线,所有的引脚就构成了这块芯片的接口。引线末端的一段,通过软钎焊使这一段与印制板上的焊盘共同形成焊点。引脚可划分为脚跟(bottom)、脚趾(toe)、脚侧(side)等部分。

15

https://baike.baidu.com/item/%E4%B8%B2%E8%A1%8C%E6%8E%A5%E5%8F%A3?fromtitle=%E4%B8%B2%E5%8F%A3&fromid=1250303

 

串行接口

同义词 串口一般指串行接口

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。

  1. 比特率

https://baike.baidu.com/item/BPS/607603?fr=aladdin

 

bpsbits per second),即比特率、比特/秒、位/秒、每秒传送位数,数据传输速率的常用单位。详见Mbps

比特(bit)是信息技术中的最小单位。文件大小(例如文本或图像文件)通常以字节Byte)为单位。一字节对应八比特。在数据传输中,数据通常是串行传输的,即一个比特接一个比特地传输。数据速率的单位是比特每秒(bps),含义是每秒串行通过的位数。

Bps (Bytes per second), 即字节每秒,因为一字节对应八比特,所以1 Bps = 8bps

 

 

  1. GPIO四种输入模式

输入浮空、上拉、下拉、模拟输入

 

18

https://baike.baidu.com/item/Cortex-M3/5614686

 

Cortex-M3是一个32位处理器内核。内部的数据路径是32位的,寄存器是32位的,存储器接口也是32位的。CM3采用了哈佛结构,拥有独立的指令总线和数据总线,可以让取指与数据访问并行不悖。这样一来数据访问不再占用指令总线,从而提升了性能。为实现这个特性,CM3内部含有好几条总线接口,每条都为自己的应用场合优化过,并且它们可以并行工作。但是另一方面,指令总线和数据总线共享同一个存储器空间(一个统一的存储器系统)。换句话说,不是因为有两条总线,可寻址空间就变成8GB了。

 

19

https://baike.baidu.com/item/Cortex-M4/7085070?fr=aladdin

 

ARMCortex-M4处理器是由ARM专门开发的最新嵌入式处理器,在M3的基础上强化了运算能力,新加了浮点、DSP、并行计算等,用以满足需要有效且易于使用的控制和信号处理功能混合的数字信号控制市场。其高效的信号处理功能与Cortex-M处理器系列的低功耗、低成本和易于使用的优点的组合,旨在满足专门面向电动机控制、汽车、电源管理、嵌入式音频和工业自动化市场的新兴类别的灵活解决方案。

20

ARM

 (英国ARM公司)

 

英国ARM公司是全球领先的半导体知识产权 (IP) 提供商。全世界超过95%的智能手机和平板电脑都采用ARM架构[1]  ARM设计了大量高性价比、耗能低的RISC处理器、相关技术及软件。2014年基于ARM技术的全年全球出货量是120亿颗,从诞生到现在为止基于ARM技术的芯片有600亿颗[2]  。技术具有性能高、成本低和能耗省的特点。在智能机、平板电脑、嵌入控制、多媒体数字等处理器领域拥有主导地位。

 

 

 

 

  1. 上拉、下拉
  2. https://baike.baidu.com/item/AD%E8%BD%AC%E6%8D%A2/7950805?fr=aladdin

AD转换

 

AD转换就是模数转换。顾名思义,就是把模拟信号转换成数字信号。主要包括积分型、逐次逼近型、并行比较型/串并行型、Σ-Δ调制型、电容阵列逐次比较型及压频变换型。

A/D转换器是用来通过一定的电路将模拟量转变为数字量。模拟量可以是电压、电流等电信号,也可以是压力、温度、湿度、位移、声音等非电信号。但在A/D转换前,输入到A/D转换器的输入信号必须经各种传感器把各种物理量转换成电压信号。

23、

施密特触发器

24、

红色贴片LED 电压1.6V至2.4V

万用表

限流电阻 

25.

Flash 程序储存空间 断电 程序仍存在rom 擦写十万次

Ram 内存 无限次擦写 读取速度特别快 存储变量 和一些中间计算的值

26.

大写P1 P0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ARDUINO

1输入输出函数
Arduino 内含了一些处理输出与输入的切换功能,相信已经从书中程式范例略知一二。

pinMode(pin, mode)

将数位脚位(digital pin)指定为输入或输出。

范例 :

pinMode(7,INPUT); // 将脚位 7 设定为输入模式

digitalWrite(pin, value)

将数位脚位指定为开或关。脚位必须先透过pinMode明示为输入或输出模式digitalWrite才能生效。

范例 :

digitalWrite(8,HIGH); //将脚位 8设定输出高电位

int digitalRead(pin)

将输入脚位的值读出,当感测到脚位处于高电位时时回传HIGH,否则回传LOW。

范例 :

val = digitalRead(7); // 读出脚位 7 的值并指定给 val

int analogRead(pin)

读出类比脚位的电压并回传一个 0到1023 的数值表示相对应的0到5的电压值。

范例 :

val = analogRead(0); //读出类比脚位 0 的值并指定给 val变数

analogWrite(pin, value)

改变PWM脚位的输出电压值,脚位通常会在3、5、6、9、10与11。Value变数范围0-255,例如:输出电压2.5伏特(V),该值大约是128。

范例 :

analogWrite(9,128); // 输出电压约2.5伏特(V)

unsigned long pulseIn(pin, value)

设定读取脚位状态的持续时间,例如使用红外线、加速度感测器测得某一项数值时,在时间单位内不会改变状态。

范例 :

time = pulsein(7,HIGH); // 设定脚位7的状态在时间单位内保持为HIGH

shiftOut(dataPin, clockPin, bitOrder, value)

把资料传给用来延伸数位输出的暂存器,函式使用一个脚位表示资料、一个脚位表示时脉。bitOrder用来表示位元间移动的方式(LSBFIRST最低有效位元或是MSBFIRST最高有效位元),最后value会以byte形式输出。此函式通常使用在延伸数位的输出。

范例 :

shiftOut(dataPin, clockPin, LSBFIRST, 255);

时间函数
控制与计算晶片执行期间的时间


unsigned long millis()

回传晶片开始执行到目前的毫秒

范例:

duration = millis()-lastTime; // 表示自"lastTime"至当下的时间

delay(ms)

暂停晶片执行多少毫秒

范例:

delay(500); //暂停半秒(500毫秒)


delay Microseconds(us)

暂停晶片执行多少微秒

范例:

delayMicroseconds(1000); //暂停1豪秒


数学函式
三角函数以及基本的数学运算


min(x, y)

回传两数之间较小者

范例:

val = min(10,20); // 回传10


max(x, y)

回传两数之间较大者

范例:

val = max(10,20); // 回传20


abs(x)

回传该数的绝对值,可以将负数转正数。

范例:

val = abs(-5); // 回传5


constrain(x, a, b)

判断x变数位于a与b之间的状态。x若小于a回传a;介于a与b之间回传x本身;大于b回传b

范例:

val = constrain(analogRead(0), 0, 255); // 忽略大于255的数


map(value, fromLow, fromHigh, toLow, toHigh)

将value变数依照fromLow与fromHigh范围,对等转换至toLow与toHigh范围。时常使用于读取类比讯号,转换至程式所需要的范围值。


例如:

val = map(analogRead(0),0,1023,100, 200); // 将analog0 所读取到的讯号对等转换至100 – 200之间的数值。


double pow(base, exponent)

回传一个数(base)的指数(exponent)值。

范例:

double x = pow(y, 32); // 设定x为y的32次方


double sqrt(x)

回传double型态的取平方根值。

范例:

double a = sqrt(1138); // 回传1138平方根的近似值 33.73425674438


double sin(rad)

回传角度(radians)的三角函数sine值。

范例:

double sine = sin(2); // 近似值 0.90929737091


double cos(rad)

回传角度(radians)的三角函数cosine值。

范例:

double cosine = cos(2); //近似值-0.41614685058


double tan(rad)

回传角度(radians)的三角函数tangent值。

范例:

double tangent = tan(2); //近似值-2.18503975868


乱数函式
产生乱数


randomSeed(seed)

事实上在Arduino里的乱数是可以被预知的。所以如果需要一个真正的乱数,可以呼叫此函式重新设定产生乱数种子。你可以使用乱数当作乱数的种子,以确保数字以随机的方式出现,通常会使用类比输入当作乱数种子,藉此可以产生与环境有关的乱数(例如:无线电波、宇宙雷射线、电话和萤光灯发出的电磁波等)。

范例:

randomSeed(analogRead(5)); // 使用类比输入当作乱数种子


long random(max)

long random(min, max)

回传指定区间的乱数,型态为long。如果没有指定最小值,预设为0。


范例:

long randnum = random(0, 100); // 回传0 – 99 之间的数字

long randnum = random(11); // 回传 0 -10之间的数字

序列通讯
你可以在第五章看见一些使用序列埠与电脑交换讯息的范例,以下是函式解释。


Serial.begin(speed)

你可以指定Arduino从电脑交换讯息的速率,通常我们使用9600 bps。当然也可以使用其他的速度,但是通常不会超过115,200 bps(每秒位元组)。


范例:

Serial.begin(9600);


Serial.print(data)

Serial.print(data, encoding)

经序列埠传送资料,提供编码方式的选项。如果没有指定,预设以一般文字传送。


范例:

Serial.print(75); // 列印出 "75"

Serial.print(75, DEC); //列印出 "75"

Serial.print(75, HEX); // "4B" (75 的十六进位)

Serial.print(75, OCT); // "113" (75 in的八进位)

Serial.print(75, BIN); // "1001011" (75的二进位)

Serial.print(75, BYTE); // "K" (以byte进行传送,显示以ASCII编码方式)


Serial.println(data)

Serial.println(data, encoding)

与Serial.print()相同,但会在资料尾端加上换行字元( )。意思如同你在键盘上打了一些资料后按下Enter。

范例:

Serial.println(75); //列印出"75 "

Serial.println(75, DEC); //列印出"75 "

Serial.println(75, HEX); // "4B "

Serial.println(75, OCT); // "113 "

Serial.println(75, BIN); // "1001011 "

Serial.println(75, BYTE); // "K "

int Serial.available()

回传有多少位元组(bytes)的资料尚未被read()函式读取,如果回传值是0代表所有序列埠上资料都已经被read()函式读取。

范例:

int count = Serial.available();


int Serial.read()

读取1byte的序列资料

范例:

int data = Serial.read();


Serial.flush()

有时候因为资料速度太快,超过程式处理资料的速度,你可以使用此函式清除缓冲区内的资料。经过此函式可以确保缓冲区(buffer)内的资料都是最新的。

范例:

Serial.flush();

 

2--analogWrite()

功能:给端口写入一个模拟值(PWM)

可以用来控制LED灯的亮度变化,或者以不同的速度驱动马达。

当执行analogWrite()命令后,端口会输出一个稳定的占空比的方波。除非有下一个命令来改变它。

PWM信号的频率大约为490Hz.

在使用analogWrite()命令前,可以不使用pinMode()命令把端口定义为输出端口,当然如果定义了更好,这样利于程序语言规范。

 

 

 

 

 

 

C语言条件编译

有些程序在调试、兼容性、平台移植等情况下可能想要通过简单地设置一些参数就生成一个不同的软件,这当然可以通过变量设置,把所有可能用到的代码都写进去,在初始化时配置,但在不同的情况下可能只用到一部分代码,就没必要把所有的代码都写进去,就可以用条件编译,通过预编译指令设置编译条件,在不同的需要时编译不同的代码。

   (一)条件编译方法

   条件编译是通过预编译指令来实现的,主要方法有:

   1、#if, #elif, #else, #endif

 #if 条件 1
 代码段 1
#elif 条件 2
   代码段 2
...
#elif 条件 n
 代码段 n
#else
 代码段 n+1
#endif

  即可以设置不同的条件,在编译时编译不同的代码,预编译指令中的表达式与C语言本身的表达式基本一至如逻辑运算、算术运算、位运算等均可以在预编译指令中使用。之所以能够实现条件编译是因为预编译指令是在编译之前进行处理的,通过预编译进行宏替换、条件选择代码段,然后生成最后的待编译代码,最后进行编译。

   #if的一般含义是,如果#if后面的常量表达式为true,则编译它所控制的代码,如条件1成立时就代码段1,条件1不成立再看条件2是否成立,如果条件2成立则编译代码段2,否则再依次类推判断其它条件,如果条件1-N都不成力则会编译最后的代码段n+1.

   2、#ifdef, #else, #endif或#ifndef, #else, #endif

  条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示“如果有定义”及“如果无定义”。有定义是指在编译此段代码时是否有某个宏通过 #define 指令定义的宏,#ifndef指令指找不到通过#define定义的某宏,该宏可以是在当前文件此条指令的前面定义的,也可以是在其它文件中,但在此指令之前包含到该文件中的。

#ifdef的一般形式是:

 #ifdef macro_name
    代码段 1
#else
    代码段 2
#endif


#ifdef的一般形式是:

#ifndef macro_name
    代码段 2
#else
    代码段 1
#endif

   这两段代码的效果是完全一样的。

   3、通过宏函数defined(macro_name)

  参数为宏名(无需加""),如果该macro_name定义过则返回真,否则返回假,用该函数则可以写比较复杂的条件编译指令如

 #if defined(macro1) || (!defined(macro2) && defined(macro3))
...
#else
...
#endif

   (二)条件编译技巧与示例

   (1)#ifdef和#defined()比较

  首先比较一下这两种方法,第一种方法只能判断一个宏,如果条件比较复杂实现起来比较烦锁,用后者就比较方便。如有两个宏MACRO_1,MACRO_2只有两个宏都定义过才会编译代码段A,分别实现如下:

 #ifdef MACRO_1
#ifdef MACRO_2
    代码段 A
#endif
#endif

或者
#if defined(MACRO_1) && defined(MACRO_2)
#endif

  同样,要实现更复杂的条件用#ifdef更麻烦,所以推荐使用后者,因为即使当前代码用的是简单的条件编译,以后在维护、升级时可能会增加,用后者可维护性较强。旧的编译器可能没有实现#defined()指令,C99已经加为标准。要兼容老的编译器,还需用#ifdef指令。

   2、#if与 #ifdef或#if defined()比较

   比如自己写了一个printf函数,想通过一个宏MY_PRINTF_EN实现条件编译,用#if可实现如下

C语言的条件编译。

 #define MY_PRINTF_EN 1

#if MYS_PRINTF_EN == 1
 int printf(char* fmt, char* args, ...)
{
    ...
}
#endif

  如果宏MY_PRINTF_EN定义为1则编译这段代码,如果宏定义不为1或者没有定义该宏,则不编译这段代码。同样也可以通过#ifdef或者#defined()实现,如

 #define MY_PRINTF_EN 1

#if defined(MY_PRINTF_EN)
 int printf(char* fmt, char* args, ...)
{
    ...
}
#endif

  在这种情况下两种方法具有异曲同工之妙,但试想如果你为了节约代码写了两个printf函数,在不同情况下使用不同的printf函数,一个是精简版一个是全功能标准版,如:

 #define MY_PRINTF_SIMPLE

#ifdef MY_PRINTF_SIMPLE
   void printf(*str) // 向终端简单地输出一个字符串
{...
}
#endif
#ifdef MY_PRINTF_STANDARD
 int printf(char* fmt, char* args, ...)
{...
}
#endif

同样可以用#if defined()实现
#define MY_PRINTF_SIMPLE

#if defined(MY_PRINTF_SIMPLE)
   void printf(*str) // 向终端简单地输出一个字符串
{
    ...
}
#elif defined(MY_PRINTF_STANDARD)
 int printf(char* fmt, char* args, ...)
{
    ...
}
#endif

  两种方法都可以实现,但可见后者更方便。但试想如果你有三个版本,用前者就更麻烦了,但方法相似,用后者就更方便,但仍需三个宏进行控制,你要住三个宏,改进一下就用#if可以用一个宏直接控制N种情况如:

 #define MY_PRINTF_VERSION     1

#if MY_PRINTF_VERSION == 1
   void printf(*str) // 向终端简单地输出一个字符串
{
    ...
}
#elif MY_PRINTF_VERSION == 2
 int printf(char* fmt, char* args, ...)
{
    ...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
    ...
}
#else
    默认版本
#endif

   这样,你只需修改一下数字就可以完成版本的选择了

   看来好像用#if 比较好了,试想如下情况:你写了一个配置文件叫做config.h用来配置一些宏,通过这些宏来控制代码,如你在config.h的宏

   #define MY_PRINTF_EN 1

   来控制是否需要编译自己的printf函数,而在你的源代码文件printf.c中有如下指令

 #i nclude "config.h"
#if MY_PRINTF_EN == 1
 int printf(char* fmt, char* args, ...)
{
    ...
}
#endif

  但这样也会有一个问题,就是如果你忘了在config.h中添加宏MY_PRINTF_EN,那么自己写的printf函数也不会被编译,有些编译器会给出警告:MY_PRINTF_EN未定义。如果你有两个版本的想有一个默认版本,可以在printf.c中这样实现

 #incldue "config.h"
#if !defined(MY_PRINTF_VERSION)
  #define MY_PRINTF_VERSION   1
#endif

#if MY_PRINTF_VERSION == 1
   void printf(*str) // 向终端简单地输出一个字符串
{
    ...
}
#elif MY_PRINTF_VERSION == 2
 int printf(char* fmt, char* args, ...)
{
    ...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
    ...
}
#endif

 这种情况下还得用到#ifdef或#if defined(),你可以不用动主体的任何代码,只需要修改printf.c文件中MY_RPINTF_VERSION宏的数字就可以改变了,如果用前面那种方法还得拖动代码,在拖动中就有可能造成错误。

   再试想,如果软件升级了,或者有了大的改动,原来有三个版本,现在只剩下两个版本了,如

 #if MY_PRINTF_VERSION == 2
 int printf(char* fmt, char* args, ...)
{
    ...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
    ...
}
#endif

  因为这些核心代码不想让使用这些代码的人关心,他们只需要修改config.h文件,那就要在printf.c中实现兼容性。如果以前有人在config.h配置宏MY_PRINTF_VERSION为1,即有

   #define MY_PRINTF_VERSION   1

   而现在没有1版本了,要想兼容怎么办?那当然可以用更复杂的条件实现如:

 #if MY_PRINTF_VERSION == 2 || MY_PRINTF_VERSION == 1
 int printf(char* fmt, char* args, ...)
{
    ...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
    ...
}
#endif

   不过还有另外一种方法,即使用#undef命令

 #if MY_PRINTF_VERSION == 1
  #undef MY_PRINTF_VERSION
  #define MY_PRINTF_VERSION  2
#endif
#if MY_PRINTF_VERSION == 2
 int printf(char* fmt, char* args, ...)
{
    ...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
    ...
}
#endif

 

   用#if还有一个好处,如果你把宏名记错了,把MY_PRINTF_EN定义成了MY_PRINT_EN,那么你用#ifdef MY_PRINTF_EN或者#if defined(MY_PRINTF_EN)控制的代码就不能被编译,查起来又不好查,用#if MY_PRINTF_EN ==1控制就很好查,因为你把MY_PRINTF_EN定义成MY_PRINT_EN,则MY_PRINTF_EN实际上没有定义,那么编译器会给出警告#if MY_PRINTF_EN == 1中的MY_PRINTF_EN没有定义,但错就比较快。

 

 

 

 

C语言static

道德static 赋值语句只有一个有效,而且只能在本函数中使用(赋值,判断),

但是分配一个永久内存。

 

 

 

 

C语言

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void turnL(int m)     //左转

{

  motor(4,5,1,m);

  motor(7,6,0,m);

}

 

void motor(char pin,char pwmpin,char state,int val)

{

    pinMode(pin, OUTPUT);  

 

  if(state==1)

  { 

    analogWrite(pwmpin,val);

    digitalWrite(pin,1);

   }

  else if(state==2)

 { 

   analogWrite(pwmpin,val);

   digitalWrite(pin,0);

 }

 else if(state==0)

 {

    analogWrite(pwmpin,0);

    digitalWrite(pin,0);

 }

}

 

 

 

 

 

按键函数

u8 KEY_Scan(u8 mode)

{     

       static u8 key_up=1;

       if(mode)key_up=1;           

       if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))

       {

              delay_ms(10);

              key_up=0;

              if(KEY0==0)return 1;

              else if(KEY1==0)return 2;

              else if(KEY2==0)return 3;

              else if(WK_UP==1)return 4;

       }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;        

      return 0;

}

 

  1. 如果都是返回同一个值,一个是隐条件语句,一个是明条件,明条件语句写在最后。
  2. 先写最复杂情况条件的语句,再去想从里面分离出简单情况,简单情况就是复杂情况的某些变量为一个定值。
  3. 追根溯源,flag要为常量,那为什么会改变呢?——赋值语句改变——那么再用赋值语句变回来不相当于常量了吗?——或者根据条件判断之后,再进行赋值语句,就相当于判断模式之后,再决定它是常量还是变量。

 

比较大小函数的新奇写法

 

for (int i = 0; i <w; i++)

       {

              int j = 0;//重新开始,从头比较

              for (; j<w; j++)

              {

                     if (p[i]->S() > p[j]->S())break;

              }

              if (j == w)

              {

                     cout <<"面积最小的图形的面积为:"<< p[i]->S() << endl << "面积最小的图形的周长为:"<<p[i]->C() << endl;

              }

       }

 

 

 

 

一个符号的意义:

P1SEL &= ~0x3

0x3=0011 &=~就是把有1的位全被清零,有0的位全部不变,比如,这个例子,P1SEL的最后两位清零,其余不变。

 

\r回到该句子的首端

\n到达下一个句子的首端

 

 

串口标志位

http://www.openedv.com/posts/list/0/58919.htm

TXE是指“弹仓”空;
TC是“枪膛”空。


这个形象。
也就是说,你写数据到串口时,是装入弹仓,硬件会将数据移到枪膛,这时,TXE为1,TC为0,STM32硬件的TX脚正在发送数据,但你还可以装入数据到弹仓,装入后,TXE为0,TC为0.
TX发送完一个数据后,立即将数据从弹仓移入枪膛,这时,TXE为1,TC为0.
最后TX发送完数据,你又没有装入新数据,这时。TXE为1,TC为1.

 

USART里面TXETC的用法

 

http://blog.csdn.net/u014170207/article/details/48225189

 

STM32库函数void USART_SendData的缺陷和解决方法

修改前的函数定义体

void USART_SendData(USART_TypeDef* USARTx, u16 Data)

{

assert_param(IS_USART_ALL_PERIPH(USARTx));

assert_param(IS_USART_DATA(Data));

USARTx->DR = (Data & (u16)0x01FF);

}

修改后的函数定义体

void USART_SendData(USART_TypeDef* USARTx, u16 Data)

{

assert_param(IS_USART_ALL_PERIPH(USARTx));

assert_param(IS_USART_DATA(Data));

USARTx->DR = (Data & (u16)0x01FF);

while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){} //等待发送缓冲区空才能发送下一个字符

}

方案3. 不修改原来的库函数,在每一个字符发送后检测状态位。

USART_SendData(USART1, RxBuffer[TxCounter]);

while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){} //等待发送缓冲区空才能发送下一个字符

 

使用USART_SendData()函数非连续发送单个字符是没有问题的;当连续发送字符时(两个字符间没有延时),就会发现发送缓冲区有溢出现象。若发送的数据量很小时,此时串口发送的只是最后一个字符,当发送数据量大时,就会导致发送的数据莫名其妙的丢失。

http://blog.csdn.net/qq_27114397/article/details/50601548

 

GPIO  功能描述

根据数据手册中列出的每个 I/O 端口的特性,可通过软件将通用 I/O (GPIO) 端口的各个端口

位分别配置为多种模式:

● 输入浮空

● 输入上拉

● 输入下拉

● 模拟功能

● 具有上拉或下拉功能的开漏输出

● 具有上拉或下拉功能的推挽输出

● 具有上拉或下拉功能的复用功能推挽

● 具有上拉或下拉功能的复用功能开漏

 

 

调试

单步调试时,调试窗口显示寄存器的数值时已经读过了DR寄存

所以你再次单步时就看到标志已经被清除。

请始终记住,调试器只有通过对各个寄存器进行读操作才能显示它们的内容。

 

 

 

 

 

 

 

STM32F1与F4的差别

 

STM32F4相对于STM32F1的改进不只一点点,为了便于初学者了解,我们比对相关资料将改进点进行了汇总。


STM32F1和STM32F4 区别   (安富莱整理)
 F1采用Crotex M3内核,F4采用Crotex M4内核。
  F1最高主频 72MHz, F4最高主频168MHz。
 F4具有单精度浮点运算单元,F1没有浮点运算单元。
  F4的具备增强的DSP指令集。F4的执行16位DSP指令的时间只有F1的30%~70%。F4执行32位DSP指令 的时间只有F1的25%~60%。
  F1内部SRAM最大64K字节, F4内部SRAM有192K字节(112K+64K+16K)。
  F4有备份域SRAM(通过Vbat供电保持数据),F1没有备份域SRAM。
  F4从内部SRAM和外部FSMC存储器执行程序的速度比F1快很多。F1的指令总线I-Bus只接到Flash上,从SRAM和FSMC取指令只能通过S-Bus,速度较慢。F4的I-Bus不但连接到Flash上,而且还连接到SRAM和FSMC上,从而加快从SRAM或FSMC取指令的速度。
  F1最大封装为144脚,可提供112个GPIO;F4最大封装有176脚,可提供140个GPIO。
  F1的GPIO的内部上下拉电阻配置仅仅针对输入模式有用,输出时无效。而F4的GPIO在设置为输出模式时,上下拉电阻的配置依然有效。即F4可以配置为开漏输出,内部上拉电阻使能,而F1不行。
  F4的GPIO最高翻转速度为84MHz,F1最大翻转速度只有18MHz。
  F1最多可提供5个UART串口,F4最多可以提供6个UART串口。
  F1可提供2个I2C接口,F4可以提供3个I2C接口。
  F1和F4都具有3个12位的独立ADC,F1可提供21个输入通道,F4可以提供24个输入通道。F1的ADC最大采样频率为1Msps,2路交替采样可到2Msps(F1不支持3路交替采样)。F4的ADC最大采样频率为2.4Msps,3路交替采样可到7.2Msps。
F1只有12个DMA通道,F4有16个DMA通道。F4的每个DMA通道有4*32位FIFO,F1没有FIFO。
  F1的SPI时钟最高速度为 18MHz, F4可以到37.5MHz。
  F1没有独立的32位定时器(32位需要级联实现),F4的TIM2和TIM5具有32位上下计数功能。
  F1和F4都有2个I2S接口,但是F1的I2S只支持半双工(同一时刻要么放音,要么录音),而F4的I2S支持全双工,放音和录音可以同时进行。

 

 

 

 

 

串口数据设置为8位,加上一个起始位和一个停止位,应该是十位,为什么发送出来的是十一位 

原因是每次发送都是采用以下语句来判断 while( USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET ); 这条语句检测到完成标志时 会产生 断开符 会在后面自行插多一个停止位进去,才造成有两个停止位的现象!!

在我发送一个数据包,在前面在数据都是使用while( USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET );来判断,数据位长度正确; 
在最后一个字节是使用while( USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET );来判断的,其在停止位后面又插多了一个“1”

 

 

 

 

 

 

 

 

 

 

 

 

 

 

HC-05进入AT模式的两种方法及步骤演示

 

 

https://wenku.baidu.com/view/a87f5d06f46527d3250ce002.html

 

 

蓝牙小车代码

https://github.com/cstlrx/BlueTooth-and-detector-Car

 

 

 

 

 

 

 

很多标志位,只能由硬件置1,软件置1是无效的,但是软件置0有效,用来清除。

 

C++

 

引用形参和指针形参前面没有const限定符时,实参必须是非const的,而前面有const限定符时对实参也没有什么影响。

为什么会出现这种情况?

原因在于实参的传递方式不同,函数中的形参是普通形参的时,函数只是操纵的实参的副本,而无法去修改实参,实参会想,你形参反正改变不了我的值,那么你有没有const还有什么意义吗?引用形参和指针形参就下不同了,函数是对实参直接操纵,没有const的形参时实参的值是可以改变的,这种情况下怎能用函数来操纵const实参呢。

我一直这样记忆:“对于变量的约束,允许加强,当绝对不能削弱.....”
例如:实参是const,那么形参如果是非const意味着可以在函数体中改变形参的值,使约束削弱了所以不行。对于使用&,自然也是这个道理。同样的,指针里面的const也是这个样子的,如果让非const指针指向了const对象的地址,那么必然是无法通过编译的,因为如果这样做了,意味着可以通过这个指针改变本该是const的值了,显然是使约束削弱了

#include <iostream>  

#include <string>  

using namespace std;  

void print_str( string & s)  

{  

      cout<<s<<endl;  

}  

int main()  

{     

      print_str("hello world");  

      return 0;  

}  

发现编译不通过,如果在第4行的string前加上一个const,就会通过编译。进一步研究我们会发现指针形参与引用形参会出现类似的情况。

双引号输入的字符串是const类型。如果要用指针或引用,必须加const

 

 

 

 

 

 

 

 

关于整个流程:

  1. 使能时钟,初始化本身,使能本身。
  2. IO口不用使能本身。
  3. 延时需要初始化。delay_init(168);
  4. 中断需要初始化。
  5. https://wenku.baidu.com/view/6e3f5f3b83c4bb4cf7ecd19f.html###

这是串口通信比较好的透解,9600波特率包括了起始位和停止位,数据不会进入DR寄存器,而且停止位和下一个起始位之间有空闲位

 

 

嵌入式中的volatile

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。 
    而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

http://blog.csdn.net/xy010902100449/article/details/48441785

定时器种类

位数

计数器模式

产生DMA请求

捕获/比较通道

互补输出

特殊应用场景

 

高级定时器

TIM1,TIM8)

16

向上,向下,向上/

可以

4

带可编程死区的互补输出

 

通用定时器(TIM2,TIM5

32

向上,向下,向上/

可以

4

通用。定时计数,PWM输出,输入捕获,输出比较

 

通用定时器(TIM3,TIM4

16

向上,向下,向上/

可以

4

通用。定时计数,PWM输出,输入捕获,输出比较

 

通用定时器(TIM9~TIM14

16

向上

没有

2

通用。定时计数,PWM输出,输入捕获,输出比较

 

基本定时器

(TIM6,TIM7)

16

向上,向下,向上/

可以

0

主要应用于驱动DAC

 

 

STM32F4F1的时钟区别

Stm32F103只有八个16位的定时器

Stm32f407ZG有两个32位的定时器,12个16位的定时器

一个定时器的输出可以作为另一个定时器的输入

 

DR寄存器提供了内部总线和移位寄存器之间的并行接口,波特率包括了起始位和停止位,在移位寄存器中,起始位和停止位读取但不发送,只将数据位存储起来,用并行通信方式传给DR寄存器,软件读取时(res=receivedata();)也是DR用并行方式传给内部总线。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

边沿:上升沿,下降沿

定时器通道,外部产生波形,边沿捕获

输出比较,通过对输出/捕获寄存器设置值,可以在定时器通道输出一个PWM波形

 

31 通用定时器基本原理 -M4

边沿:上升沿,下降沿

定时器通道,外部产生波形,边沿捕获

输出比较,通过对输出/捕获寄存器设置值,可以在定时器通道输出一个PWM波形

极性,就是高低电平,是高电平有效还是低电平有效

 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值