一、八位流水灯设计
1. 任务需求
电路如图所示,GPIOA管脚低8位连接至LD。请循环依次点亮LD0~LD7,每个LD亮灭时间间隔200ms。(代码中无需初始化GPIOA,请直接写出流水亮灭逻辑伪代码,可直接给GPIOA赋值操作)
2.问题分析
由电路图和任务需求可知,八个LED灯采用共阴极接法分别,LED 0-7 的阳极分别与MCU的GPIO引脚的A0-A7相连,通过以此使能GPIOA的0-7八个引脚依次输出高低电平(GPIO_SetBits,GPIO_ResetBits)实现流水灯效果。
3.流水灯伪代码书写(主函数部分)
1 | /* |
4.工程实现
笔者在Keil上利用stm32F103库函数模板实现流水灯的工程文件编写,主要任务是对Led.c及其头文件的编写实现对stm32 GPIOA引脚的初始化。工程目录如下:
led.h头文件
1 |
|
led.c文件
1 |
|
5.实验搭建及效果
笔者手头上有一块stm32F103RCT6开发板以及LED小灯若干,所以索性搭建了流水灯实现的硬件电路,其中RCT6需要借助USB转TTL将hex文件烧录到板子中,根据实验原理在面包板上搭建电路,成功实现了LED流水灯效果
二、SN74HC595 驱动led设计
1.任务需求
电路如图所示,一片SN74HC595DR串并转换芯片连接至8段数码管。请驱动LED,循环显示09数字,显示间隔时间1s。其中74HC595的三个GPIO选择PB0PB3。
2. 问题分析
本题主要是利用74HC595带锁存移位寄存器串入并处芯片实现将3个GPIO引脚的输入转化为8个并行输出来驱动数码管显示数字。
3. 知识回顾
- 74HC595工作原理
74HC595芯片管脚的实物图和接线图如图所示,左右各有8个引脚共有16个引脚,其中Q0-Q8用作并行输出,在本题中将连接数码管的八个引脚。一个电源引脚Vcc和一个接地GND。另外,在本题中将MR和OE分别连接电源和地。
下面的动图更加形象地说明595芯片的工作过程
- 八段数码管介绍
八段数码管的基本原理为:将八个LED灯采用共阳极(或共阴极)的接法,然后通过控制每个LED小灯另一端电平的高或低来实现每个灯的亮与灭进而实现显示不同的数字或者字母。
数码管的字母对应表:以共阴极数码管为例,下面介绍一下0-9数字的字母对应表,根据自己的数码管实际情况,若为共阳极连接方式,则将0与1分别替换即可。
4.工程实现
A.基于Stm32+keil实现
在流水灯的实验基础之上,在App目录下新建一个smg.c文件用于实现数码管的显示,smg.c的编写与led.c相似,基本上就是GPIO引脚的初始化。
smg.h头文件
1 |
|
smg.c文件
1 | /* |
编写主函的时候,首先定义一个10*9 的二维数组用于存放0-9 十个数字。在主循环体中按照一下步骤将数据逐位存入74HC595芯片
- 判断输入的为1 or 0
- 若为1 则将B2拉高,为0将B2拉低
- 将B1拉高,将B2的输入存入寄存器
- 再将B1拉低,等待下一次存入
main.c 文件内容如下:
1 | /* |
整个工程目录如下图所示:
将工程生成的hex文件烧录到stm32F103RCT6中,笔者手头上现有一个八段数码管和一篇595芯片,根据上述接线方法连接电路,其中stm32的B0、B1、B2分别连接595芯片的数据发送端口Ds管脚,寄存器输入开关SHCP管脚以及并行输出开关STCP管脚,正常运行后可以看到如下显示效果:
B.基于树莓派RPI.GPIO实现
树莓派的GPIO使用起来非常方便,直接通过编写一个py文件调用RPI.GPIO库和时间库就可以实现本实验需求,基本原理再次不做赘述,smg.py文件编写如下:
1 | # encoding: utf-8 |
编写完程序后,在文件目录下指令执行smg.py文件
笔者利用手上的树莓派4B的三个GPIO引脚连接595的DS、SHCP、STCP三个引脚,可以观察到如下实验现象:
三、多线程设计
1.任务需求
多线程设计,每个线程各自打印一个字母,可以交替打印A, B, C字母。
2.解决思路
- 让三个线程对应三个标志flag:0,1,2。同时使得三个线程共享一个锁。
- 通过对状态标志进行判断,如果没有轮到当前线程打印时则通过wait()函数来阻塞当前线程,如果轮到当前状态标志,则执行打印操作。
- 执行完成后将状态标志flag修改为下一状对应的态标志并将打印次数加1,最后执行notify_all()唤醒当前所有阻塞进程。
- 三个线程打印次数达到设定值,三个子线程执行结束,跟随主线程一块儿退出
3.程序实现
笔者用C++编写程序如下:运行结果如下(循环打印5次)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using namespace std;
mutex mtx; //互斥信号量
condition_variable cv; //阻塞等待条件变量
int flag = 0; //状态标志
void PrintString_1()
{
unique_lock<mutex> lk(mtx); //获得一个锁
int count = 0; //计数器
while (count < 10) //循环打印次数10
{
while (flag != 0) //判断打印标志,不为零,则使得线程等待,处于阻塞等待,为0,跳出阻塞,执行打印操作
cv.wait(lk);
cout << this_thread::get_id() << " : " << "A" << std::endl;
flag = 1; //打印完成后,状态标志变为1
count++; //循环次数加一
cv.notify_all(); //唤醒当前所有的线程,因为此时其他两个线程在wait,而且下一步就要去执行其中一个
}
}
void PrintString_2()
{
unique_lock<mutex> lk(mtx);
int count = 0;
while (count < 10)
{
while (flag != 1)
cv.wait(lk);
cout << this_thread::get_id() << " : " << "B" << std::endl;
flag = 2;
count++;
cv.notify_all();
}
}
void PrintString_3()
{
unique_lock<mutex> lk(mtx);
int count = 0;
while (count < 10)
{
while (flag != 2)
cv.wait(lk);
cout << this_thread::get_id() << " : " << "C" << std::endl;
flag = 0;
count++;
cv.notify_all();
}
}
int main()
{
//创建三个线程,分别执行打印ABC
std::thread t1(PrintString_1);
std::thread t2(PrintString_2);
std::thread t3(PrintString_3);
t3.join();
t1.join();
t2.join();
return 0;
}