单片机基础

单片机

单片机定义

内含CPU
内存RAM
存储空间ROM
通过外部或内部时钟输入完成指令的处理
能够通过外部编程使其独立完成工作
8051内核、AVR内核、ARM内核(部分)

可以把单片机看成微型电脑,内部带有了内存和硬盘,只不过比较小、功耗低因此性能也差

可实现的功能

CPU可以处理指令进行运算

RAM相当于电脑内存,可以临时存放运算结果 

ROM相当于电脑硬盘,可以存储程序 

实现外部引脚从电源电压与GND的切换

点亮小灯、读取按键、串行数据输入输出、读写外部器件等

单片机程序框架

语句→函数→文件→框架→库

学习单片机过程会遇到的坑

不要速成

打牢基础软硬件知识

先弄明白怎么做,再弄明白为什么

做完如何改进、实现更多的功能,然后再看原理、单片机内部如何实现

不要翻一本书从头翻到尾,看不进去的

看不懂没关系,先用,用会了再回头弄明白原理

充分学习和吸收优秀代码(郭天祥的代码其实不是很规范,不值得学习)

找大厂如华为、中兴内部培训的资料,借鉴它们的编程规范

OpenHarmony开发者文档 - 《华为鸿蒙操作系统(OpenHarmony) v1.0 开发者文档》

推荐周立功代码规范。周立功标准代码编写规范 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)

延时代码

#include <STC8.H>
void Delay500ms()
{
    unsigned char i,j,k:
    i=29;
    j=14;
    k=54;
    do
    {
        do
        {
            while(--k);
        }while(--j);
    }while(--i);
}

void main()
{
  while(1)
  {
      P2=0xA5;
    Delay500ms();
    P2=0x5A;
    Delay500ms();
  }
}

单片机C语言

不建议看谭浩强,建议看比较新的、外文翻译过来的书,或直接看单片机相关的书,里面会直接讲C语言基础

有时可以把Delay500ms写成Delay_500ms

软延时:通过程序把它消耗在一个无意义的事情上,让其浪费掉这些时间

硬延时/定时:通过内部硬件电路结构实现

单片机资源非常有限,必须重点关注各种数据类型的位,防止浪费单片机的资源。

char 8位

0xFF (1111 1111) ,最高位为符号位,1负0正。

从1111 1111(-128)~0111 1111(+127),一共可代表255个数。

再进位到1000 0000就翻转变成-1出现错误了,因此使用char时必须注意不要超出其运行范围(-128~127

unsigned char 8位 0xFF,严格限制在0~255,否则255再+1变成0,再加1变成1
int 16位 0xFFFF
unsigned int 16位 0~65535
long 32位  
float   浮点,表示小数时使用

位数可以简单理解为线。如8位单片机内部有8根总线,可以一次性并行传输8个数。同理,32位单片机可以同时写32个数,以此类推。因此8位单片机尽量用8位的char。

而对ARM单片机最小就是32位,如果写8位,其他的相当于浪费了。因此在ARM里存在一个16位的short

花样点灯: 可以随便想如何点灯,然后编程实现,非常锻炼编程能力!

1、for

2、while

3、位操作(来源)

————————————————
版权声明:下文为CSDN博主「MoreWindows」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/morewindows/article/details/7354571

————————————————

1.只能用于整形数据,不可用于float和double

2.算数移位和逻辑移位不同

算术移位 左移<<操作低位补0 右移>>操作高位补符号位
逻辑移位 左移<<操作低位补0 右移>>操作高位补0
int a = -15, b = 15;
printf("%d %d\n", a >> 2, b >> 2);

15=0000 1111(二进制),右移二位,最高位由符号位填充将得到0000 0011即3

-15 = 1111 0001(二进制),右移二位,最高位由符号位填充将得到1111 1100即-4

常见用法

1.判断奇偶:最未位为0是偶数,为1是奇数

用if ((a & 1) == 0)判断a是不是偶数

eg.输出0到100之间的所有奇数

for (i = 0; i < 100; ++i)
    if (i & 1)
        printf("%d ", i);
    putchar('\n');

2.交换两数

void Swap(int &a, int &b)
{
    if (a != b)
    {
        a ^= b;
        b ^= a;
        a ^= b;
    }
}

3.变换符号(正变负,负变正)

取反后加1

#include <stdio.h>
int SignReversal(int a)
{
    return ~a + 1;
}
int main()
{int a = 7, b = -12345;
    printf("%d  %d\n", SignReversal(a), SignReversal(b));
    return 0;
}

4.求绝对值(负数按上面变换符号即可)

移位来取符号位,int i = a >> 31

int my_abs(int a)
{
    int i = a >> 31;
    return ((a ^ i) - i);
}

5.用位操作压缩素数表的空间占用

数组在内存上也是连续分配的一段空间,完全可以“认为”是一个很长的整数

//使用位操作压缩后的筛素数方法
#include <stdio.h>
#include <memory.h>
const int MAXN = 100;
int flag[MAXN / 32 + 1];
int primes[MAXN / 3 + 1], pi;
void GetPrime_1()
{
    int i, j;
    pi = 0;
    memset(flag, 0, sizeof(flag));
    for (i = 2; i < MAXN; i++)
        if (!((flag[i / 32] >> (i % 32)) & 1))
        {
            primes[pi++] = i;
            for (j = i; j < MAXN; j += i)
                flag[j / 32] |= (1 << (j % 32));
        }
}
void PrintfArray()
{
    for (int i = 0; i < pi; i++)
        printf("%d ", primes[i]);
    putchar('\n');
}
int main()
{
    GetPrime_1();
    PrintfArray();
    return 0;
}

6.高低位交换

二进制的数34520:10000110 11011000

高低位交换后是十进制的55430: 11011000 10000110

实现:右移时会执行逻辑右移即高位补0,因此x右移8位将得到00000000 10000110

x左移8位将得到11011000 00000000。可以发现只要将x>>8与x<<8这两个数相或就可以得到11011000 10000110

#include <stdio.h>
template <class T>
void PrintfBinary(T a)
{
    int i;
    for (i = sizeof(a) * 8 - 1; i >= 0; --i)
    {
        if ((a >> i) & 1)
            putchar('1');
        else 
            putchar('0');
        if (i == 8)
            putchar(' ');
    }
    putchar('\n');
}
int main()
{
    
    printf("交换前:    ");
    unsigned short a = 3344520;
    PrintfBinary(a);
 
    printf("交换后:    ");
    a = (a >> 8) | (a << 8);
    PrintfBinary(a);
    return 0;
}

7.二进制逆序

字符串的逆序,可以从字符串的首尾开始,依次交换两端的数据

用位操作的高低位交换来处理二进制逆序

类似于归并排序的分组处理

第一步:每2位为一组,组内高低位交换

10 00 01 10 11 01 10 00

-->01 00 10 01 11 10 01 00

第二步:每4位为一组,组内高低位交换

0100 1001 1110 0100

-->0001 0110 1011 0001

第三步:每8位为一组,组内高低位交换

00010110 10110001

-->01100001 00011011

第四步:每16位为一组,组内高低位交换

01100001 00011011

-->00011011 01100001

#include <stdio.h>
template <class T>
void PrintfBinary(T a)
{
    int i;
    for (i = sizeof(a) * 8 - 1; i >= 0; --i)
    {
        if ((a >> i) & 1)
            putchar('1');
        else 
            putchar('0');
        if (i == 8)
            putchar(' ');
    }
    putchar('\n');
}
int main()
{
    printf("二进制逆序\n");
 
    printf("逆序前:    ");
    unsigned short a = 34520;
    PrintfBinary(a);
 
    printf("逆序后:    ");    
    a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
    a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
    a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
    a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
    PrintfBinary(a);
}

8.二进制中1的个数

【1】直接移位再判断

【2】循环移位计数

【3】先打一个表再计算

【4】分组

第一步:每2位为一组,组内高低位相加

10 00 01 10 11 01 10 00

-->01 00 01 01 10 01 01 00

第二步:每4位为一组,组内高低位相加

0100 0101 1001 0100

-->0001 0010 0011 0001

第三步:每8位为一组,组内高低位相加

00010010 00110001

-->00000011 00000100

第四步:每16位为一组,组内高低位相加

00000011 00000100

-->00000000 00000111

#include <stdio.h>
template <class T>
void PrintfBinary(T a)
{
    int i;
    for (i = sizeof(a) * 8 - 1; i >= 0; --i)
    {
        if ((a >> i) & 1)
            putchar('1');
        else 
            putchar('0');
        if (i == 8)
            putchar(' ');
    }
    putchar('\n');
}
int main()
{
    printf("二进制中1的个数\n");
    
    unsigned short a = 34520;
    printf("原数    %6d的二进制为:  ", a);
    PrintfBinary(a);
    
    a = ((a & 0xAAAA) >> 1) + (a & 0x5555);
   a = ((a & 0xCCCC) >> 2) + (a & 0x3333);
   a = ((a & 0xF0F0) >> 4) + (a & 0x0F0F);
   a = ((a & 0xFF00) >> 8) + (a & 0x00FF);    
    printf("计算结果%6d的二进制为:  ", a);    
    PrintfBinary(a);
    return 0;
}

9.找到成对出现的数字中缺失的数字

有一个数只出现一次而且其它数字都出现了偶数次

可用异或运算的两个特性——1.自己与自己异或结果为0,2.异或满足交换律。

将这些数字全异或一遍,结果就一定是那个仅出现一个数。 

#include <stdio.h>
int main()
{
    printf("缺失的数字\n");
    
    const int MAXN = 15;
    int a[MAXN] = {1, 347, 6, 9, 13, 65, 889, 712, 889, 347, 1, 9, 65, 13, 712};
    int lostNum = 0;
    for (int i = 0; i < MAXN; i++)
        lostNum ^= a[i];
    printf("缺失的数字为:  %d\n", lostNum);    
    return 0;
}

练习
1.入门级
按键长按与短按实现不同功能
按键切换流水灯效果、LED灯亮度
2.刮目相看级
音乐方块游戏
呼吸灯
3.骨灰级
打地鼠

定时器

定时器的本质是“装桶”

进入中断,调用中断服务函数

自由定时器:不一定要装满,可以装完一定值之后读取有多少数并通过计算获得时间

 从左到右路过哪里就打开哪里!

  不要着急写程序,先把范例读一读,找到其中的不同,并判断这些不同会产生哪些区别,再动手去写。

(比如X位自动重载中不同位数,例程有什么变化)

#include <STC8.H>
unsigned int Counter=0;
void TIMERO(void) interrupt 1
Counter++;
if(Counter==1000)
P20=!P20:
}
Counter=0;
}
void TimerOInit(void)        //1000微秒@12.000Hz
{
AUXR|=0x80;        //定时器时钟1T模式
TMOD&=0xF0;    //设置定时器模式
TLO=0x20;        //设置定时初值
THO=0xD1;        //设置定时初值
TFO=0;            //清除TF0标志
TRO=1;            //定时器0开始计时
}
void main(void)
{
TimerOInit();
ET0=1;
EA=1;
while(1);
}

 主要来源:

2020单片机基础培训第一课(上)_哔哩哔哩_bilibili

2020单片机基础培训第一课(下)_哔哩哔哩_bilibili

posted @ 2023-07-24 10:30  asandstar  阅读(51)  评论(0编辑  收藏  举报