01
摘要
你点亮过多少板子的LED灯呢?有很多小伙伴留言说讲一下STM32、FPGA、Liunx他们之间有什么不同。不同点很多,口说无凭。
今天就来点亮一下STM32、FPGA和Liunx板子的LED灯,大家大致看一下点灯流程和点灯环境以及点灯流程,就能大概的了解一下三者的区别,可以有选择的去学习!
![d5f20d6ab209701a3d2bd54219b08ee3.gif](https://i-blog.csdnimg.cn/blog_migrate/649d0c5ee087477e9eed25f717dc53dc.gif)
02
使用STM32点亮LED灯
STM32从字面上来理解ST是意法半导体,M是Microelectronics的缩写,32 表示32位,合起来理解,STM32就是指ST公司开发的32位微控制器。
在如今的32 位控制器当中,STM32可以说是最璀璨的新星,它受宠若娇,大受工程师和市场的青睐,无芯能出其右。首先使用STM32电亮一个led灯,大家现在回过头来看是不是非常的简单。
STM32初始化流程
1、使能指定GPIO的时钟。
2、初始化GPIO,比如输出功能、上拉、速度等等。
3、STM32有的IO可以作为其它外设引脚,也就是IO复用,如果要将IO作为其它外设引脚使用的话就需要设置 IO 的复用功能。
4、最后设置GPIO输出高电平或者低电平。
1、新建工程
![fe9cba6f4d6bce0f2ac32f588c0687b3.png](https://i-blog.csdnimg.cn/blog_migrate/5a2559c64276c78ec45df9979c2d84fd.png)
2、代码编写
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭
}
3、编译代码
![2957ef8478d8cc53588217e39a660c5e.png](https://i-blog.csdnimg.cn/blog_migrate/4978d1e0924f94c05559e6d18e150be5.png)
4、配置下载器
![99f6631e21b2b10460a1e7b00d29df2a.png](https://i-blog.csdnimg.cn/blog_migrate/bcaf2e673207b7effd9d5fb2297aabb5.png)
烧录代码
03
使用FPGA点亮LED灯
FPGA(Field Programmable Gate Array,简称 FPGA),译文:现场可编程门阵列,一种主要以数字电路为主的集成芯片,于1985年由Xilinx创始人之一 Ross Freeman发明,属于可编程逻辑器件PLD(Programmable Logic Device)的一种。
真正意义上的第一颗FPGA芯片XC2064为Xilinx所发明,这个时间差不多比著名的摩尔定律晚20年左右,但是FPGA一经发明,后续的发展速度之快,超出大多数人的想象。
计数器是在FPGA设计中最常用的一种时序逻辑电路,根据计数器的计数值我们可以精确的计算出FPGA内部各种信号之间的时间关系,每个信号何时拉高、何时拉低、拉高多久、拉低多久都可以由计数器实现精确的控制。
而让计数器计数的是由外部晶振产生的时钟,所以可以比较精准的控制具体需要计数的时间。计数器一般都是从0开始计数,计数到我们需要的值或者计数满溢出后清零,并可以进行不断的循环。
本例我们让计数器计数1s时间间隔,来实现led灯每隔1s闪烁一次的效果。
![]() | ![]() |
1、模块框图
![]() | ![]() |
2、RTL代码的编写
开始RTL代码的编写,RTL代码编写出的模块叫RTL模块(后文中也称功能模块、可综合模块)。
之所以叫RTL代码是因为用Verilog HDL在Resistances Transistors Logic(寄存器传输级逻辑)来描述硬件电路,RTL代码能够综合出真实的电路以实现我们设计的功能,区别于不可综合的仿真代码。
`timescale 1ns/1ns
//带标志信号的计数器
module counter
#(
parameter CNT_MAX = 25'd24_999_999
)
(
input wire sys_clk , //系统时钟50Mhz
input wire sys_rst_n , //全局复位
output reg led_out //输出控制led灯
);
reg [24:0] cnt; //经计算得需要25位宽的寄存器才够500ms
reg cnt_flag;
//cnt:计数器计数,当计数到CNT_MAX的值时清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt < CNT_MAX)
cnt <= cnt + 1'b1;
else
cnt <= 25'b0;
//cnt_flag:计数到最大值产生的标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == CNT_MAX - 1'b1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
//led_out:输出控制一个LED灯,每当计数满标志信号有效时取反
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if(cnt_flag == 1'b1)
led_out <= ~led_out;
endmodule
3、代码的分析和综合
![d8943711008d47f4364f7d077762524d.png](https://i-blog.csdnimg.cn/blog_migrate/fa3ceacef7fdc43448f732fc208ffb3b.png)
4、 查看RTL视图
![3669416d7439baf717cae001082e1386.png](https://i-blog.csdnimg.cn/blog_migrate/8245ef2c72794711c1bca6a66437416e.png)
5、Testbench代码的编写
`timescale 1ns/1ns
module tb_counter();
//wire define
wire led_out ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
//初始化系统时钟、全局复位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
initial begin
$timeformat(-9, 0, "ns", 6);
$monitor("@time %t: led_out=%b", $time, led_out);
end
//------------- counter_inst --------------
counter
#(
.CNT_MAX (25'd24 )
)
counter_inst
(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.led_out (led_out ) //output led_out
);
endmodule
6、ModelSim仿真波形
![c6fdb506145e8ed4e90b88f74d93bdf5.png](https://i-blog.csdnimg.cn/blog_migrate/da4518f6b59ae6f8ce809e3c5b6b9433.png)
![5b85f0d60f2853a1b59b22bbc00e4c61.png](https://i-blog.csdnimg.cn/blog_migrate/b42f64b263ad78bb783cd4c04f3b2654.png)
7、上板验证
![39d3b39ab5df3d849a5bbe45cb198be1.png](https://i-blog.csdnimg.cn/blog_migrate/a8cb9f897420af289210b19c9b0fee5c.png)
![db2ef42b66c4384d25099a184c919a07.png](https://i-blog.csdnimg.cn/blog_migrate/f8930cc543e6a6f7ab76aa5126b04d28.png)
程序下载完毕后,会看到板卡LED0不断闪烁,时间间隔为1秒。
04
使用I.MX6ULL IO点亮LED
嵌入式linux学习者大体可以分为两类,一类是进阶用户,主要指已经有大量mcu工作经验的开发者, 他们希望进阶到更有难度,薪资更高的mpu开发中去。另一类则是学生用户,主要是刚开始接触嵌入式开发的大学生群体。
I.MX应用处理器包括I.MX8、I.MX7、I.MX6及I.MX28系列,被广泛应用于工业控制、汽车电子领域,久经市场考验。而且它的产品线非常丰富,用户熟悉其中一款产品后就能非常方便地迁移至不同的平台。
一般拿到一款全新的芯片,第一个要做的事情的就是驱动其GPIO,控制其GPIO输出高低电平,我们学习I.MX6U也一样的,先来学习一下I.MX6U的GPIO。
在学习I.MX6U的GPIO之前,我们可以对比一下STM32的GPIO初始化(如果没有学过 STM32 就不用回顾了),我们以最常见的STM32F103为例来看一下STM32的GPIO初始化,示例代码如下:
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能 PB 端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PB5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化 GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}
STM32初始化流程
1、使能指定GPIO的时钟。
2、初始化 GPIO,比如输出功能、上拉、速度等等。
3、STM32 有的 IO 可以作为其它外设引脚,也就是 IO 复用,如果要将 IO 作为其它外设引脚使用的话就需要设置 IO 的复用功能。
4、最后设置GPIO输出高电平或者低电平。
I.MX6U的GPIO一共有5组:GPIO1、GPIO2、GPIO3、GPIO4和GPIO5
,其中GPIO1有32个IO,GPIO2有22个IO,GPIO3有29个IO、GPIO4有29个IO,GPIO5最少,只有12个IO,这样一共有124个GPIO
。
I.MX6ULL IO初始化流程
1、使能时钟,CCGR0—CCGR6
这7个寄存器控制着6ULL所有外设时钟的使能。为了简单,设置CCGR0~CCGR6这7
个寄存器全部为0XFFFFFFFF
,相当于使能所有外设时钟。
2、IO复用,将寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
的bit3~0
设置为0101=5
,这样GPIO1_IO03
就复用为GPIO
。
3、寄存器IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
是设置GPIO1_IO03
的电气属性。包括压摆率、速度、驱动能力、开漏、上下拉等。
4、配置GPIO功
能,设置输入输出。设置GPIO1_DR
寄存器bit3为1,也就是设置为输出模式。设置GPIO1_DR
寄存器的bit3
,为1表示输出高电平,为0表示输出低电平。
汇编由一条一条指令构成,指令就涉及到汇编指令。
Int a,b;
a=b;
假设a地址为0X20,b地址为0x30
LDR R0, =0X30
LDR R1, [R0]
LDR R0, =0X20
STR R1, [R0]
我们在使用汇编编写驱动的时候最常用的就是LDR
和STR
这两个指令。
1、新建工程
新建工程文件夹
![3ed72578a7d5d6063663ea306364d33a.png](https://i-blog.csdnimg.cn/blog_migrate/706e57180ffd14205139408cbdee6cdb.png)
![d8c5ed1a6e944689361dd818353e5cad.png](https://i-blog.csdnimg.cn/blog_migrate/d11a119f5b1fc18ca8839a9d207cdf32.png)
2、在VSCode中编写代码
ubuntu中我们使用的是VScode编辑器来写代码,跟在windows中新建项目一样,新建项目、保存工作区,然后编写代码。
![adf07623d1e0c3f1acabdeeeb28791a8.png](https://i-blog.csdnimg.cn/blog_migrate/0156e85ed9783afb9003a523922d6bf1.png)
3、编写代码
.global _start /* 全局标号 */
_start:
/* 1、使能所有时钟 ldf如果用大写就全部用大写,如果小写就全部用小写*/
ldr r0, =0X020C4068 //将寄存器CCGR0地址0X020C4068 存放到 寄存器R0 中
ldr r1, =0XFFFFFFFF //把寄存器x地址0Xffffffff存放到 寄存器r1 中
str r1, [r0]//把寄存器r1中的值(0XFFFFFFFF) 写入到寄存器r0里面的值作为地址的内存里面
ldr r0, =0X020C406C/*将寄存器CCGR1地址(0X020C4068) 存放到 寄存器R0 中*/
str r1, [r0]
ldr r0, =0X020C4070 /* CCGR2 */
str r1, [r0]
ldr r0, =0X020C4074 /* CCGR3 */
str r1, [r0]
ldr r0, =0X020C4078 /* CCGR4 */
str r1, [r0]
ldr r0, =0X020C407C /* CCGR5 */
str r1, [r0]
ldr r0, =0X020C4080 /* CCGR6 */
str r1, [r0]
/* 2、设置GPIO1_IO03复用为GPIO1_IO03 */
ldr r0, =0X020E0068 /* 将寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */
ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */
str r1,[r0]
/* 3、配置GPIO1_IO03的IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */
ldr r1, =0X10B0
str r1,[r0]
/* 4、设置GPIO1_IO03为输出 */
ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR */
ldr r1, =0X0000008
str r1,[r0]
/* 5、打开LED0
* 设置GPIO1_IO03输出低电平
*/
ldr r0, =0X0209C000 /*寄存器GPIO1_DR */
ldr r1, =0
str r1,[r0]
/*
* 描述:loop死循环
*/
loop:
b loop
.global _start @全局标号
/**/
4、编译代码
使用如下三条命令来编译代码
arm-linux-gnueabihf-gcc -g -c leds.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
![f68705d0f08d96df129243bacfdb6f5e.png](https://i-blog.csdnimg.cn/blog_migrate/d205bf5fa15d94864363b16201dfa8fa.png)
最终生成了led.o
led.elf
led.bin
三个文件
5、烧写代码
STM32中代码烧写到内部FLASH。IMX6ULL支持SD卡、EMMC、NAND、nor、SPI flash等启动。裸机例程选择烧写到SD卡里面。在ubuntu下向SD卡烧写裸机bin文件。烧写不是将bin文件拷贝到SD卡中,而是将bin文件烧写到SD卡绝对地址上。
而且对于I.MX而言,不能直接烧写bin文件,比如先在bin文件前面添加头部。完成这个工作,需要使用正点原子提供的imxdownload软件。
![5985c8d93bb69e49df812898938c7fd3.png](https://i-blog.csdnimg.cn/blog_migrate/f1a14ba7b6bda7b0053d26d6fce07d2b.png)
烧写的三个命令
ls /dev/sd* -l
chmod 777 imxdownload
./imxdownload led.bin /dev/sdb
Imxdownload使用方法,确定要烧写的SD卡文件,需要使用ls /dev/sd* -l
命令来检测SD是哪一个文件,我的是/dev/sdb
。
![cbe44039f19733d40d5793984f3d9161.png](https://i-blog.csdnimg.cn/blog_migrate/43d699ce4221fb4b090220e60ec595be.png)
![191d496056156b6a80f58bf7089c6da9.png](https://i-blog.csdnimg.cn/blog_migrate/05f4e2d3ef81b733628655c14f4ba249.png)
给予imxdownload可执行权限:Chmod 777 imxdownload
烧写:./imxdownload led.bin /dev/sdb
![8f5c8482bd65d5114d7902550827815d.png](https://i-blog.csdnimg.cn/blog_migrate/fdc748e240f88f029fc730bf5efd295b.png)
Imxdownlaod
会向led.bin
添加一个头部,生成新的load.imx
文件,这个load.imx
文件就是最终烧写到SD卡里面去的。
![1772dce1abe53c3cd008f862530089f3.png](https://i-blog.csdnimg.cn/blog_migrate/288870b8c4318ebc262c74122787f0ea.png)
这里要注意的是如果烧写的速度在几十MB/S左右的话,那么可能意味着烧写失败了。而且是因为SD卡没找到而导致烧写失败,这个问题只能重启 ubuntu解决。
![03ccaf769ceeec8721f4daac6b3ccd0d.png](https://i-blog.csdnimg.cn/blog_migrate/08fe093312694e086d19fbace03d2476.png)
之后就可以从读卡器中把SD拔下来,然后插入到开发板中,将拨码开关拔止SD卡模式,供电之后,蓝色LED亮,红色LED灭,两秒钟之后红色LED亮。
![107c61d0063fdc2583e39474ba9d6381.png](https://i-blog.csdnimg.cn/blog_migrate/97a2da9438f8fa9feb00b9d5645ea6f6.png)
最后你觉得哪个板子点灯最简单呢?
-END-
往期推荐:点击图片即可跳转阅读