个人创建基于rt_thread的项目

在学习了中断函数后突发奇想想自己创建一个用中断函数来控制led灯的闪烁,说干就干,期望达到的效果如下:

按下key1后蜂鸣器发出声音同时led以绿,蓝,红的顺序进行闪烁;按下key2后蜂鸣器停止发出声音同时led灯熄灭。

在经过一番操作后代码如下:

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#define KEY0_PIN_NUM 0
#define BEEP_PIN_NUM 8

int jb,junbian;
/*完成对led的初始化*/
void led_start(){
  rt_pin_mode(16,PIN_MODE_OUTPUT);
  rt_pin_write(16,PIN_HIGH);
  rt_pin_mode(17,PIN_MODE_OUTPUT);
  rt_pin_write(17,PIN_HIGH);
  rt_pin_mode(21,PIN_MODE_OUTPUT);
  rt_pin_write(21,PIN_HIGH);	
}
/*完成对蜂鸣器的初始化*/
void jun_ready(){

  rt_pin_mode(8,PIN_MODE_OUTPUT);
  rt_pin_write(8,PIN_HIGH); 

}
/*完成led的闪烁控制*/
void jun_on(){
  
  rt_pin_write(16,PIN_LOW);
  rt_thread_delay(50);
  rt_pin_write(16,PIN_HIGH); 
  
  rt_pin_write(17,PIN_LOW);
  rt_thread_delay(50);
  rt_pin_write(17,PIN_HIGH);
  
  rt_pin_write(21,PIN_LOW);
  rt_thread_delay(50);
  rt_pin_write(21,PIN_HIGH);	
  
}
/*完成关闭led的控制*/
void jun_off(){
  rt_pin_write(8,PIN_LOW);
  rt_pin_write(16,PIN_HIGH);
  rt_pin_write(17,PIN_HIGH);
  rt_pin_write(21,PIN_HIGH);
}

void jun_start(){
  led_start();//调用led初始化函数
/*将key1和key2设置成上拉输入*/
  rt_pin_mode(0,PIN_MODE_INPUT_PULLUP );
  rt_pin_mode(45,PIN_MODE_INPUT_PULLUP );
  while(1){
  rt_pin_attach_irq(0,PIN_IRQ_MODE_FALLING,jun_ready,RT_NULL);//绑定引脚中断回调函数
  rt_pin_irq_enable(0,PIN_IRQ_ENABLE);//使能引脚中断
  
  jb=rt_pin_read(8);//读取蜂鸣器的电平
  
  if(jb==PIN_HIGH){
    while(jb==PIN_HIGH){
    jun_on();
  rt_pin_attach_irq(45,PIN_IRQ_MODE_FALLING,jun_off,RT_NULL);
  rt_pin_irq_enable(45,PIN_IRQ_ENABLE);
      junbian=rt_pin_read(8);
      if(junbian==PIN_LOW){
        break;
      }
      
    }
  }
}
}

MSH_CMD_EXPORT(jun_start,sat8 is the best)

现在说一下项目设计的思路,首先在完成对于引脚的初始化后我们首先要做的事就是实现闪烁功能,通过延时函数rt_thread_delay()我们可以轻易做到闪烁的功能。有关本项目所使用到的函数在这里我都写了注释:https://www.mdrfb.com/2019/09/03/examples-of-functions-in-rt_thread/

接下来就要解决用按钮控制的问题了,根据中断回调函数我们可以让程序在读到这一行的时候执行中断并作出相应的操作,

rt_pin_attach_irq(0,PIN_IRQ_MODE_FALLING,jun_ready,RT_NULL);//绑定引脚中断回调函数 
rt_pin_irq_enable(0,PIN_IRQ_ENABLE);//使能引脚中断

这两行实现了使用key1按钮来让蜂鸣器器响起来的功能因为蜂鸣器要和led灯一起闪烁所以我们需要时刻对蜂鸣器进行电平检测,当电平被检测为高时就代表我们的led就要开始闪起来了,

jb=rt_pin_read(8);//读取蜂鸣器的电平

这一行代码完成了对于蜂鸣器电平的检测,再根据if函数来执行检测到蜂鸣器电平为high时led开始闪烁的功能。

因为我们还要通过key2按钮来关闭led和蜂鸣器所以我们还要再打一行中断回调函数和使能引脚中断, 

 rt_pin_attach_irq(45,PIN_IRQ_MODE_FALLING,jun_off,RT_NULL);
 rt_pin_irq_enable(45,PIN_IRQ_ENABLE);

根据上文的思路,当蜂鸣器电平变低时led就要熄灭所以我们又添加了一条检测函数,

junbian=rt_pin_read(8);

再根据if判断语句和c语言中break指令完成对闪烁的中断,也就是跳出while循环,至此我们的操作目标已经都达成了。

在编写此类程序的时候有一点需要注意,有些人在编程时会忽略编译器对于读取程序的顺序,程序在运行时速度非常快所以有时候会发生这种情况,我的程序明明符合逻辑天衣无缝为什么下载到开发板上后功能却不完全或根本没有功能,为了达到随按随停,随按随开的功能我们要在主体函数外边加一个while(1)循环,也就是无限循环,否则就会发生按了按钮灯死活不亮的反应,因为你的程序早就被执行完毕了!

个人对于rt_thread中函数的理解和例子

//此文章会不间断进行更新,速度为作者学习的速度(捂脸)

1,rt_pin_mode()

此函数是用来完成对引脚的选择和控制输入还是输出模式。

示例:rt_pin_mode(16,PIN_MODE_OUTPUT)

2,rt_pin_write()

此函数是用来完成对引脚的输出高低电平的控制。

示例:rt_pin_write(16,PIN_LOW)

3,rt_mdelay()

此函数是用来将线程挂起来完成延时操作,延时单位为毫秒(ms)。

示例:rt_mdelay(500)

4,rt_delay()

此函数是用来线程挂起来完成延时操作,延时单位为系统滴答,一般我们设置系统时钟滴答的频率为100hz,也就是一个滴答为10ms。

示例:rt_delay(50)

5,rt_thread_create()

此函数是用来创建动态线程用的,具体方式如下:

rt_thread_t rt_thread_create(const char *name,
                             void (*entry)(void *parameter),
                             void       *parameter,
                             rt_uint32_t stack_size,
                             rt_uint8_t  priority,
                             rt_uint32_t tick)

创建一个动态线程分为这么几步:1,给出线程名字。2,设置入口函数。3,设置函数输入参数。4,定义线程栈的大小。5,设置线程优先级,数字越小优先级越高,0为最高优先级。6,配置线程时间片。

示例:

rt_threat jun=rt_thread_create("junbian",junbian,RT_NULL,512k,5,10)

在示例中假定我们创建的入口函数名称为junbian,RT_NULL所表示的意思为无输入参数。

6,rt_thread_starup()

此函数是用来启动线程的。

示例:rt_thread_starup(jun)

在这里jun假定为线程的句柄。

7,MSH_CMD_EXPORT()

这个严格意义上来说并不是函数而是命令,但是在编程时运用广泛所以也就把它和函数写一块了。可以通过这条命令来来将你所需要执行的线程或函数发送到MSH命令行,然后msh命令列表会通过串口进行输出。

示例:MSH_CMD_EXPORT(sat8,sat8 is the best)

在这里假定sat8为线程名字,逗号后面为用户自己定义的,你可以把注释或者标签写进去,在这里我们写的是sat8 is the best。

8,rt_pin_attach_irq()

此函数是用来绑定引脚中断的回调函数。什么是引脚中断回调函数?当一个引脚猝发某种情况时会将当前任务中断并完成用户指定的另一个任务。

rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
                             void (*hdr)(void *args), void  *args)

这是官方给出的格式,首先确定需要绑定的引脚,再选择触发中断的条件,接着选择触发条件时需要执行的函数,最后设置中断回调函数的参数(不需要时设置为RT_NULL)。

示例:rt_pin_attach_enable(0,PIN_IRQ_MODE_FALLING,jun_ready,RT_NULL)

9,rt_pin_irq_enable()

此函数是用来使能引脚中断的,当我们绑定完引脚中断的回调函数后需要使能引脚中断,也就是给出信号让引脚开始执行中断并执行中断函数。

rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)

这是官方给出的格式,首先设置引脚,再设置状态,状态有两种enable和disable。

示例: rt_pin_irq_enable(0,PIN_IRQ_ENABLE);

在rt_thread中用main函数来实现跑马灯的功能

跑马灯是大部分学习单片机的初学者编的第一个程序,在使用rt_thread嵌入式系统编程时完成对跑马灯的编程可谓是学习rt_thread的第一课,话不多说直接上代码:

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

int main(){
rt_pin_mode(16,PIN_MODE_OUTPUT);
  
  while(1){
    rt_pin_write(16,PIN_LOW);
    rt_thread_mdelay(500);
    rt_pin_write(16,PIN_HIGH);
    rt_thread_mdelay(500);
  }
return RT_EOK;
}

这里使用的开发板是野火f103,正常来讲使用rt_thread编程时要先创立线程但是今天我们先不创建,试试看直接用main函数能不能让跑马灯亮起来。

我们先从main函数的第一行看起:

rt_pin_mode(16,PIN_MODE_OUTPUT)这行的意思就是先确定我们要对哪个引脚进行操作然后确定它的输入输出模式,这里要解释一下括号中16是什么意思,首先让我们打开内核文件drv_gpio.c

static const struct pin_index pins[] = 
{
#if defined(GPIOA)
    __STM32_PIN(0 ,  A, 0 ),
    __STM32_PIN(1 ,  A, 1 ),
    __STM32_PIN(2 ,  A, 2 ),
    __STM32_PIN(3 ,  A, 3 ),
    __STM32_PIN(4 ,  A, 4 ),
    __STM32_PIN(5 ,  A, 5 ),
    __STM32_PIN(6 ,  A, 6 ),
    __STM32_PIN(7 ,  A, 7 ),
    __STM32_PIN(8 ,  A, 8 ),
    __STM32_PIN(9 ,  A, 9 ),
    __STM32_PIN(10,  A, 10),
    __STM32_PIN(11,  A, 11),
    __STM32_PIN(12,  A, 12),
    __STM32_PIN(13,  A, 13),
    __STM32_PIN(14,  A, 14),
    __STM32_PIN(15,  A, 15),
#if defined(GPIOB)
    __STM32_PIN(16,  B, 0),
    __STM32_PIN(17,  B, 1),
    __STM32_PIN(18,  B, 2),
    __STM32_PIN(19,  B, 3),
    __STM32_PIN(20,  B, 4),
    __STM32_PIN(21,  B, 5),
    __STM32_PIN(22,  B, 6),
    __STM32_PIN(23,  B, 7),
    __STM32_PIN(24,  B, 8),
    __STM32_PIN(25,  B, 9),
    __STM32_PIN(26,  B, 10),
    __STM32_PIN(27,  B, 11),
    __STM32_PIN(28,  B, 12),
    __STM32_PIN(29,  B, 13),
    __STM32_PIN(30,  B, 14),
    __STM32_PIN(31,  B, 15),
#if defined(GPIOC)
    __STM32_PIN(32,  C, 0),
    __STM32_PIN(33,  C, 1),
    __STM32_PIN(34,  C, 2),
    __STM32_PIN(35,  C, 3),
    __STM32_PIN(36,  C, 4),
    __STM32_PIN(37,  C, 5),
    __STM32_PIN(38,  C, 6),
    __STM32_PIN(39,  C, 7),
    __STM32_PIN(40,  C, 8),
    __STM32_PIN(41,  C, 9),
    __STM32_PIN(42,  C, 10),
    __STM32_PIN(43,  C, 11),
    __STM32_PIN(44,  C, 12),
    __STM32_PIN(45,  C, 13),
    __STM32_PIN(46,  C, 14),
    __STM32_PIN(47,  C, 15),
#if defined(GPIOD)
    __STM32_PIN(48,  D, 0),
    __STM32_PIN(49,  D, 1),
    __STM32_PIN(50,  D, 2),
    __STM32_PIN(51,  D, 3),
    __STM32_PIN(52,  D, 4),
    __STM32_PIN(53,  D, 5),
    __STM32_PIN(54,  D, 6),
    __STM32_PIN(55,  D, 7),
    __STM32_PIN(56,  D, 8),
    __STM32_PIN(57,  D, 9),
    __STM32_PIN(58,  D, 10),
    __STM32_PIN(59,  D, 11),
    __STM32_PIN(60,  D, 12),
    __STM32_PIN(61,  D, 13),
    __STM32_PIN(62,  D, 14),
    __STM32_PIN(63,  D, 15),
#if defined(GPIOE)
    __STM32_PIN(64,  E, 0),
    __STM32_PIN(65,  E, 1),
    __STM32_PIN(66,  E, 2),
    __STM32_PIN(67,  E, 3),
    __STM32_PIN(68,  E, 4),
    __STM32_PIN(69,  E, 5),
    __STM32_PIN(70,  E, 6),
    __STM32_PIN(71,  E, 7),
    __STM32_PIN(72,  E, 8),
    __STM32_PIN(73,  E, 9),
    __STM32_PIN(74,  E, 10),
    __STM32_PIN(75,  E, 11),
    __STM32_PIN(76,  E, 12),
    __STM32_PIN(77,  E, 13),
    __STM32_PIN(78,  E, 14),
    __STM32_PIN(79,  E, 15),
#if defined(GPIOF)
    __STM32_PIN(80,  F, 0),
    __STM32_PIN(81,  F, 1),
    __STM32_PIN(82,  F, 2),
    __STM32_PIN(83,  F, 3),
    __STM32_PIN(84,  F, 4),
    __STM32_PIN(85,  F, 5),
    __STM32_PIN(86,  F, 6),
    __STM32_PIN(87,  F, 7),
    __STM32_PIN(88,  F, 8),
    __STM32_PIN(89,  F, 9),
    __STM32_PIN(90,  F, 10),
    __STM32_PIN(91,  F, 11),
    __STM32_PIN(92,  F, 12),
    __STM32_PIN(93,  F, 13),
    __STM32_PIN(94,  F, 14),
    __STM32_PIN(95,  F, 15),
#if defined(GPIOG)
    __STM32_PIN(96,  G, 0),
    __STM32_PIN(97,  G, 1),
    __STM32_PIN(98,  G, 2),
    __STM32_PIN(99,  G, 3),
    __STM32_PIN(100, G, 4),
    __STM32_PIN(101, G, 5),
    __STM32_PIN(102, G, 6),
    __STM32_PIN(103, G, 7),
    __STM32_PIN(104, G, 8),
    __STM32_PIN(105, G, 9),
    __STM32_PIN(106, G, 10),
    __STM32_PIN(107, G, 11),
    __STM32_PIN(108, G, 12),
    __STM32_PIN(109, G, 13),
    __STM32_PIN(110, G, 14),
    __STM32_PIN(111, G, 15),
#if defined(GPIOH)
    __STM32_PIN(112, H, 0),
    __STM32_PIN(113, H, 1),
    __STM32_PIN(114, H, 2),
    __STM32_PIN(115, H, 3),
    __STM32_PIN(116, H, 4),
    __STM32_PIN(117, H, 5),
    __STM32_PIN(118, H, 6),
    __STM32_PIN(119, H, 7),
    __STM32_PIN(120, H, 8),
    __STM32_PIN(121, H, 9),
    __STM32_PIN(122, H, 10),
    __STM32_PIN(123, H, 11),
    __STM32_PIN(124, H, 12),
    __STM32_PIN(125, H, 13),
    __STM32_PIN(126, H, 14),
    __STM32_PIN(127, H, 15),
#if defined(GPIOI)
    __STM32_PIN(128, I, 0),
    __STM32_PIN(129, I, 1),
    __STM32_PIN(130, I, 2),
    __STM32_PIN(131, I, 3),
    __STM32_PIN(132, I, 4),
    __STM32_PIN(133, I, 5),
    __STM32_PIN(134, I, 6),
    __STM32_PIN(135, I, 7),
    __STM32_PIN(136, I, 8),
    __STM32_PIN(137, I, 9),
    __STM32_PIN(138, I, 10),
    __STM32_PIN(139, I, 11),
    __STM32_PIN(140, I, 12),
    __STM32_PIN(141, I, 13),
    __STM32_PIN(142, I, 14),
    __STM32_PIN(143, I, 15),
#if defined(GPIOJ)
    __STM32_PIN(144, J, 0),
    __STM32_PIN(145, J, 1),
    __STM32_PIN(146, J, 2),
    __STM32_PIN(147, J, 3),
    __STM32_PIN(148, J, 4),
    __STM32_PIN(149, J, 5),
    __STM32_PIN(150, J, 6),
    __STM32_PIN(151, J, 7),
    __STM32_PIN(152, J, 8),
    __STM32_PIN(153, J, 9),
    __STM32_PIN(154, J, 10),
    __STM32_PIN(155, J, 11),
    __STM32_PIN(156, J, 12),
    __STM32_PIN(157, J, 13),
    __STM32_PIN(158, J, 14),
    __STM32_PIN(159, J, 15),
#if defined(GPIOK)
    __STM32_PIN(160, K, 0),
    __STM32_PIN(161, K, 1),
    __STM32_PIN(162, K, 2),
    __STM32_PIN(163, K, 3),
    __STM32_PIN(164, K, 4),
    __STM32_PIN(165, K, 5),
    __STM32_PIN(166, K, 6),
    __STM32_PIN(167, K, 7),
    __STM32_PIN(168, K, 8),
    __STM32_PIN(169, K, 9),
    __STM32_PIN(170, K, 10),
    __STM32_PIN(171, K, 11),
    __STM32_PIN(172, K, 12),
    __STM32_PIN(173, K, 13),
    __STM32_PIN(174, K, 14),
    __STM32_PIN(175, K, 15),

在这里我们为每一个引脚都定义了一个宏,在开发板里led的绿灯所对应的引脚是PB0也就是gpioB第0号引脚(具体可以看开发板的原理图)

__STM32_PIN(16, B, 0),

这一行就是为PB0定义的宏,可以看到在括号内有3个数字,第一个数字就是这个引脚所对应的编号也就是16。

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

int main(){
rt_pin_mode(16,PIN_MODE_OUTPUT);
  
  while(1){
    rt_pin_write(16,PIN_LOW);
    rt_thread_mdelay(500);
    rt_pin_write(16,PIN_HIGH);
    rt_thread_mdelay(500);
  }
}

再让我们回到main函数中,括号里第二个参数应该很好理解,就是选择推挽输出模式同样再内核文件中也能看到关于输入输出的宏。

#define PIN_MODE_OUTPUT         0x00
#define PIN_MODE_INPUT          0x01
#define PIN_MODE_INPUT_PULLUP   0x02
#define PIN_MODE_INPUT_PULLDOWN 0x03
#define PIN_MODE_OUTPUT_OD      0x04

那么这一行是什么意思就一目了然了,选择PB0这个引脚并使用推挽输出模式。接下来让我们看while循环,(1)表示此循环无限运作下去(但通过对线程的操控可以让它关闭,具体的以后会讲)

rt_pin_write(16,PIN_LOW); 
rt_thread_mdelay(500); 
rt_pin_write(16,PIN_HIGH);
rt_thread_mdelay(500);

while循环中是我们用来点亮跑马灯并且让他闪烁的,第一句就是让PB0输出低电平,在rt_thread中所有引用的参数都是事先定义好的宏所以可以参照上文来找到它的原型,第二句是时钟函数就是延时50ms后读取下一行代码,rt_thread中时钟函数有很多种,如rt_thread_delay(使用系统滴答来完成延时操作,括号内参数应改成50,也就是50个系统滴答)。在搞清楚前两行代码是什么意思后整个while循环的意思就应该很清晰了,通过对引脚输出高低电平并适当的延时就能完成闪烁这个操作。

在rt_thread中用main函数来实现跑马灯的功能还是算蛮简单的,如果有学习过库函数编程应该会一看就懂,但是这种做法在现实中是几乎不会使用的,因为这样子编程和使用库函数编程没什么区别,也就是无法体现出tr_thread的特点。正常情况下是要创建线程后再进行用户函数的编写。

 

重大线索发现!

欢迎收看今天的“小狮子 带侦探”!我是最可爱的人形s.a.t.8。在后勤官格林娜小姐的指挥下指挥部所有的人形对整个指挥部区域进行了地毯式搜索,在指挥室中的电脑里我们又发现了一串可疑的代码:

;******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
;* File Name          : startup_stm32f10x_hd.s
;* Author             : MCD Application Team
;* Version            : V3.5.0
;* Date               : 11-March-2011
;* Description        : STM32F10x High Density Devices vector table for MDK-ARM 
;*                      toolchain. 
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == Reset_Handler
;*                      - Set the vector table entries with the exceptions ISR address
;*                      - Configure the clock system and also configure the external 
;*                        SRAM mounted on STM3210E-EVAL board to be used as data 
;*                        memory (optional, to be enabled by user)
;*                      - Branches to __main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the CortexM3 processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>   
;*******************************************************************************
; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
;*******************************************************************************

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
                                                  
; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                DCD     ADC3_IRQHandler            ; ADC3
                DCD     FSMC_IRQHandler            ; FSMC
                DCD     SDIO_IRQHandler            ; SDIO
                DCD     TIM5_IRQHandler            ; TIM5
                DCD     SPI3_IRQHandler            ; SPI3
                DCD     UART4_IRQHandler           ; UART4
                DCD     UART5_IRQHandler           ; UART5
                DCD     TIM6_IRQHandler            ; TIM6
                DCD     TIM7_IRQHandler            ; TIM7
                DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY
              
; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP
               
; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler          [WEAK]
                B       .
                ENDP
BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler           [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler         [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler                [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler           [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

Default_Handler PROC

                EXPORT  WWDG_IRQHandler            [WEAK]
                EXPORT  PVD_IRQHandler             [WEAK]
                EXPORT  TAMPER_IRQHandler          [WEAK]
                EXPORT  RTC_IRQHandler             [WEAK]
                EXPORT  FLASH_IRQHandler           [WEAK]
                EXPORT  RCC_IRQHandler             [WEAK]
                EXPORT  EXTI0_IRQHandler           [WEAK]
                EXPORT  EXTI1_IRQHandler           [WEAK]
                EXPORT  EXTI2_IRQHandler           [WEAK]
                EXPORT  EXTI3_IRQHandler           [WEAK]
                EXPORT  EXTI4_IRQHandler           [WEAK]
                EXPORT  DMA1_Channel1_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel2_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel3_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel4_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel5_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel6_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel7_IRQHandler   [WEAK]
                EXPORT  ADC1_2_IRQHandler          [WEAK]
                EXPORT  USB_HP_CAN1_TX_IRQHandler  [WEAK]
                EXPORT  USB_LP_CAN1_RX0_IRQHandler [WEAK]
                EXPORT  CAN1_RX1_IRQHandler        [WEAK]
                EXPORT  CAN1_SCE_IRQHandler        [WEAK]
                EXPORT  EXTI9_5_IRQHandler         [WEAK]
                EXPORT  TIM1_BRK_IRQHandler        [WEAK]
                EXPORT  TIM1_UP_IRQHandler         [WEAK]
                EXPORT  TIM1_TRG_COM_IRQHandler    [WEAK]
                EXPORT  TIM1_CC_IRQHandler         [WEAK]
                EXPORT  TIM2_IRQHandler            [WEAK]
                EXPORT  TIM3_IRQHandler            [WEAK]
                EXPORT  TIM4_IRQHandler            [WEAK]
                EXPORT  I2C1_EV_IRQHandler         [WEAK]
                EXPORT  I2C1_ER_IRQHandler         [WEAK]
                EXPORT  I2C2_EV_IRQHandler         [WEAK]
                EXPORT  I2C2_ER_IRQHandler         [WEAK]
                EXPORT  SPI1_IRQHandler            [WEAK]
                EXPORT  SPI2_IRQHandler            [WEAK]
                EXPORT  USART1_IRQHandler          [WEAK]
                EXPORT  USART2_IRQHandler          [WEAK]
                EXPORT  USART3_IRQHandler          [WEAK]
                EXPORT  EXTI15_10_IRQHandler       [WEAK]
                EXPORT  RTCAlarm_IRQHandler        [WEAK]
                EXPORT  USBWakeUp_IRQHandler       [WEAK]
                EXPORT  TIM8_BRK_IRQHandler        [WEAK]
                EXPORT  TIM8_UP_IRQHandler         [WEAK]
                EXPORT  TIM8_TRG_COM_IRQHandler    [WEAK]
                EXPORT  TIM8_CC_IRQHandler         [WEAK]
                EXPORT  ADC3_IRQHandler            [WEAK]
                EXPORT  FSMC_IRQHandler            [WEAK]
                EXPORT  SDIO_IRQHandler            [WEAK]
                EXPORT  TIM5_IRQHandler            [WEAK]
                EXPORT  SPI3_IRQHandler            [WEAK]
                EXPORT  UART4_IRQHandler           [WEAK]
                EXPORT  UART5_IRQHandler           [WEAK]
                EXPORT  TIM6_IRQHandler            [WEAK]
                EXPORT  TIM7_IRQHandler            [WEAK]
                EXPORT  DMA2_Channel1_IRQHandler   [WEAK]
                EXPORT  DMA2_Channel2_IRQHandler   [WEAK]
                EXPORT  DMA2_Channel3_IRQHandler   [WEAK]
                EXPORT  DMA2_Channel4_5_IRQHandler [WEAK]

WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler
                B       .

                ENDP

                ALIGN

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

;******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE*****

经过技术分析后我们得知这是前几次电子板的启动文件,上边的代码分别实行了一下功能:

首先,定义栈,何为栈?栈是堆栈中的一部分,而堆栈则是为了分配在运行函数时所需分配的内存空间,栈的目的就是将函数从第一行依次录入一个空间,这个空间就是栈;相应的,有栈就要有堆,所以第二段就是定义堆,堆的作用就是从栈中读取函数,同样也是依次读取并将读取的语句存入内存最后由cpu来执行功能。

然后声明中断函数,并根据4字节的长度来分配内存空间,接着编写中断函数的代码。

接下来就到了最重要的一步,将系统时钟函数放入r0寄存器运行完后清除,再将用户程序也就是main放入r0寄存器运行完后清楚,这里可能有人要问了为什么运行完后要清除?因为我们的内存空间是有限的不能无限制让一个已经运行完的程序当钉子户,否则会导致机器越用越卡越用越慢,所以以后在对内存进行操作的时候一定要时刻记得“用完就扔”的原则。

最后就是将用户自己编写的函数放到堆栈中并分配到内存,总共有4个寄存器被分配给了堆栈,r0被分配给了堆的起始地址,r2被分配给了堆的大小,r3被分配给了栈顶指针,r1被分配给了栈的大小。

Stack_Size EQU 0x00000400 
AREA STACK, NOINIT, READWRITE, ALIGN=3 
Stack_Mem SPACE Stack_Size __initial_sp

这三行代码诠释了栈的起始指针和栈的大小:首先将内存中0x00000400声明成了stack_size,再将stack_size(也就是0x00000400)作为内存地址分配给了stack_mem从而达到了设置栈顶指针的目的,再将两者相加就达到了栈的大小。

Heap_Size EQU 0x00000200 
AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base 
Heap_Mem SPACE Heap_Size __heap_limit

那么同理这三行代码诠释了堆的起始地址和堆的大小。

我们查询了这段代码发送的地址,发现是由一个叫均鞭的用户在英国赫里福基地发出的,为了查询事情的真相并找回失踪的指挥官,我!最可爱的人形小狮子将会第一时间赶赴现场为大家带来第一手资讯,请大家继续关注下一期的“小狮子 带侦探”!

 

 

 

如何通过RTT开启野火开发板上的sdcard,并且在msh中读取

野火开发板上有一个sdcard接口,通过RTT,我们可以非常方便的添加开发板相关的驱动,并且在RT-thread的Final sh中使用并读取sd卡的内容。

在开始本教程之前请先确保您已经安装RT-thread的ENV工具包。

首先,我们移动到RTT的bsp目录下,右键空白处,选择ConEmu Here

在Env控制台中输入menuconfig

进入menuconfig界面后进入到Hardware Drivers Config–>Onboard Peripheral Drivers

打开Enable SDCARD (sdio)

保存后在Env命令台中输入scons –target=mdk5

运行后,重新打开项目文件,即可发现项目中的Filesystem Group

重新烧录工程后,即可操作RTT上的文件操作系统

 \ | /
- RT -     Thread Operating System
 / | \     4.0.2 build Aug 22 2019
 2006 - 2019 Copyright by rt-thread team
msh />[I/SDIO] SD card capacity 15273984 KB.
found part[0], begin: 4194304, size: 14.576GB
[I/app.card] sd card mount to '/'
[D/at.dev] 

msh />ls
Directory /:
pic1.bmp            460854                   
pic2.bmp            115254                   
pic3.bmp            460854                   
pic4.bmp            460854                   
pic5.bmp            50678                    
pic6.bmp            44802

如上串口信息所示,可以调用sd卡中的文件

神秘的仪器,新的代码,背后的真相到底是。。。。

欢迎收看今天的“小狮子,带侦探”!上次我们在指挥官的卧室里发现了散发诡异光芒的小灯和一串神秘的代码。今天我们在指挥部后面的小花园里又发现了一个奇怪的装置,这个装置被遗弃在了灌木丛中,但是!我,最可爱的人型s.a.t.8依旧找到了这重要的线索!话不多说,让我们康康今天的带发现吧!

#include "stm32f10x.h"
#include "led.h"
void Delay(uint32_t jun){
  for(;jun!=0;--jun);
}
int main(void){
int a=1,b=0;
  led_gpio_sb();
  led_gpio_yw();
while(1){
  while(a==1){
    if(key_scan(K1_GPIO_PORT,LED_K1_GPIO_PIN)==KEY_ON||b==1){
      led_gpio_config();
      led_gpio_JUN();	
      led_gpio_BIAN();
      while(a==1){		
    LED_G(ON);	 
    LED_B(OFF);	
    LED_R(OFF);
    Delay(0xFFFFF);	
        if(key_scan(K2_GPIO_PORT,LED_K2_GPIO_PIN)==KEY_ON){
      a=0;		
        LED_G(OFF);	 
        LED_B(OFF);
        LED_R(OFF);
      break;}
        LED_G(OFF);	 
        LED_B(ON);
        LED_R(OFF);
        Delay(0xFFFFF);	
        if(key_scan(K2_GPIO_PORT,LED_K2_GPIO_PIN)==KEY_ON){
      a=0;		
        LED_G(OFF);	 
        LED_B(OFF);
        LED_R(OFF);
      break;}
    LED_G(OFF);	 
    LED_B(OFF);
    LED_R(ON);
    Delay(0xFFFFF);	
      if(key_scan(K2_GPIO_PORT,LED_K2_GPIO_PIN)==KEY_ON){
      a=0;		
        LED_G(OFF);	 
        LED_B(OFF);
        LED_R(OFF);
      break;
        }     
      }
    }	
if(a==0) break;
  
  }
    if(key_scan(K1_GPIO_PORT,LED_K1_GPIO_PIN)==KEY_ON){
    a=1;
      b=1;
    
  }
}
}
#include "led.h"
void led_gpio_config(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;

  RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin=LED_G_GPIO_PIN;
  
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    
  GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
  
}
void led_gpio_JUN(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;
  
  RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
    
  GPIO_InitStruct.GPIO_Pin=LED_B_GPIO_PIN ;
    
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  
  GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
  
}
void led_gpio_BIAN(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;
  
  RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin=LED_R_GPIO_PIN ;
  
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  
  GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
  
}

void led_gpio_sb(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;
  
  RCC_APB2PeriphClockCmd(K1_G_GPIO_CLK, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin=LED_K1_GPIO_PIN ;
  
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  
  GPIO_Init(K1_GPIO_PORT, &GPIO_InitStruct);
  
}

void led_gpio_yw(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;
  
  RCC_APB2PeriphClockCmd(K2_G_GPIO_CLK, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin=LED_K2_GPIO_PIN ;
  
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
    
  GPIO_Init(K2_GPIO_PORT, &GPIO_InitStruct);
  
}

uint8_t key_scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin){
  if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin)==KEY_ON){
    while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin)==KEY_ON);
    return KEY_ON;
  }
  else return KEY_OFF;
}
#ifndef led_h
#define led_h
#include "stm32f10x.h"
#define LED_G_GPIO_PIN     GPIO_Pin_0
#define LED_R_GPIO_PIN     GPIO_Pin_5 
#define LED_B_GPIO_PIN     GPIO_Pin_1 
#define LED_G_GPIO_PORT    GPIOB
#define K1_GPIO_PORT    GPIOA
#define K2_GPIO_PORT    GPIOC
#define LED_G_GPIO_CLK     RCC_APB2Periph_GPIOB
#define K1_G_GPIO_CLK     RCC_APB2Periph_GPIOA
#define K2_G_GPIO_CLK     RCC_APB2Periph_GPIOC
#define LED_K1_GPIO_PIN     GPIO_Pin_0
#define LED_K2_GPIO_PIN     GPIO_Pin_13
#define ON   1
#define OFF  0
#define LED_G(JUN) if(JUN)GPIO_ResetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);else GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);
#define LED_B(JUN) if(JUN)GPIO_ResetBits(LED_G_GPIO_PORT, LED_B_GPIO_PIN);else GPIO_SetBits(LED_G_GPIO_PORT, LED_B_GPIO_PIN);
#define LED_R(JUN) if(JUN)GPIO_ResetBits(LED_G_GPIO_PORT, LED_R_GPIO_PIN);else GPIO_SetBits(LED_G_GPIO_PORT, LED_R_GPIO_PIN);
void led_gpio_config(void);
void led_gpio_JUN(void);
void led_gpio_BIAN(void);
void led_gpio_sb(void);
void led_gpio_yw(void);
uint8_t key_scan(GPIO_TypeDef *GPIO,uint16_t GPIO_Pin);
#define KEY_ON 1
#define KEY_OFF 0
#define LED_G_TOGGLE {LED_G_GPIO_PORT->ODR ^=LED_G_GPIO_PIN;}
#define LED_R_TOGGLE {LED_G_GPIO_PORT->ODR ^=LED_R_GPIO_PIN;}
#define LED_B_TOGGLE {LED_G_GPIO_PORT->ODR ^=LED_B_GPIO_PIN;}
#endif

乍一看,这个装置没什么亮点,但是当我们打开开关摁下key1按钮后发现这个装置上的led灯竟然开始闪烁了,当我们再摁下key2按钮后发现led灯竟然熄灭了。这次的发现比上次的更加劲爆,很显然指挥官的失踪并不是无端发生的,这次的装置和上次的装置一定是指挥官留下的线索。后勤官格林娜知道后第一时间来到了现场,在看过代码后,格林娜小姐说这次的代码和上次的有明显的不同,因为上次只有输出而这次却有了输入,通过对输入检测的编写可以让设备接受到信号并作出预先设计好的反应。

格林娜小姐在分析完决定调用格里芬指挥部的力量进行地毯式搜索,想要知道下一次的线索吗?那就继续关注“小狮子,带侦探”吧!我是最可爱的人形s.a.t.8,期待与您分享关于指挥官失踪的第一手资讯。

JQ8900的RTT[RT-thread]简易测试代码

-以下为示例代码

可以通过开发板上的按钮来对JQ8900进行操作,达到切换曲目的效果

并且可以自定义修改代码进行更多操作,优化了串口发送信息的方式。

/*
通过RTT例子修改
用于测试YSV0.7是否可用
*/

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>


#define LED0_PIN    GET_PIN(B, 1)    //定位到野火开发板上两个RGB相关的寄存器上
#define LED0_PINE   GET_PIN(B, 0)
#ifndef KEY0_PIN_NUM
    #define KEY0_PIN_NUM      GET_PIN(A, 0) /* PA0 */
#endif
#ifndef KEY1_PIN_NUM
    #define KEY1_PIN_NUM      GET_PIN(C, 13) /* PC13 */
#endif
#define SAMPLE_UART_NAME       "uart2"

static rt_device_t serial;              /* 串口设备句柄 */
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 配置参数 */


/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;




/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static void Re_music(void *args)
{
    static char buffer1[4]={0xAA,0x05,0x00,0xAF};
    rt_uint32_t tx_length;
    tx_length=4;
    int i = 0;
    while(i < 4){
    rt_device_write(serial,0,&buffer1[i],tx_length);
    i++;}
    rt_kprintf("上一条\n");
}
static void next_music(void *args)
{
    static char buffer1[4]={0xAA,0x06,0x00,0xB0};
    rt_uint32_t tx_length;
    tx_length=4;
    int i = 0;
    while(i < 4){
    rt_device_write(serial,0,&buffer1[i],tx_length);
    i++;}
    rt_kprintf("下一条\n");

}

static void serial_thread_entry(void *parameter)
{

  //  char ch;
    int jun = 1;
    while (1)
      
    {		
             /*发送缓冲区是固定为5个字节长度的*/
        static char uart2_tx_buffer[5]={0xAA,0x02,0x00,0xAC,0x05};
        rt_uint32_t tx_length;
        tx_length=5;
    rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, Re_music, RT_NULL);
    rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);


    rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, next_music, RT_NULL);
    rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);
        if(jun){
        rt_device_write(serial,0,&uart2_tx_buffer[0],tx_length);
        rt_device_write(serial,0,&uart2_tx_buffer[1],tx_length);
        rt_device_write(serial,0,&uart2_tx_buffer[2],tx_length);
        rt_device_write(serial,0,&uart2_tx_buffer[3],tx_length);
        rt_kprintf("测试成功!");}
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */					
             jun = 0;
    }
}

static int mdr1(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    char uart_name[RT_NAME_MAX];

    if (argc == 2)
    {
        rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
    }
    else
    {
        rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
    }

    /* 查找系统中的串口设备 */
    serial = rt_device_find(uart_name);

    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    if (!serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
    config.baud_rate = BAUD_RATE_9600;    //配置串口通信为9600
    config.data_bits = DATA_BITS_8;
    config.stop_bits = STOP_BITS_1;
    config.parity = PARITY_NONE;
    /* 打开设备后才可修改串口配置参数 */
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_input);
    /* 发送字符串 */
//    rt_device_write(serial, 0, str, (sizeof(str) - 1));

    /* 创建 serial 线程 */
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        ret = RT_ERROR;
    }

    return ret;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mdr1, uart JUN);

可以自行添加到RT-thread的BSP中,并烧录到开发板中。

JQ8900模块的基础调试(直接串口调试)

将JQ8900模块按照以下方式进行连接(Rx,Tx,GND与DC5V暂时悬空)

触发输入口笔者暂时只接了I01于I02,仅做测试用

具体接线方式如下:

一般接线方式

接完线之后,将串口相关的线暂时悬空,并接上micro-USB线,并连接到电脑上。

电脑会立即识别到flash映射的可移动磁盘,打开磁盘,下载测试语音包,并将语音包解压复制到可移动磁盘中。

提取码:csat

最终效果如图所示

将micro-USB线拔下,拿出usb转TTL模块,将刚刚悬空的串口相关线接上ttl模块,注意TX与RX的反接

参考接线方式

讲usb转ttl模块接上电脑,测试悬空的I01与I02两条线,将两条线分别接地,看看是否有声音发出,有声音发出说明模块测试ok

 

接着测试串口能否控制模块,打开sscom,如下设置

输入AA 02 00 AC,并点击发送,可以听到喇叭的声音。

至此测试完成,下一篇将会介绍JQ8900与开发板的通信。

新的线索!不为人知的秘密竟然在这儿被发现。。。。。。。

欢迎来到今天的“小狮子 带侦探”,我是最可爱的战术人形s.a.t.8!今天我们在指挥官的卧室里找到一块奇怪的电路板,上面的led灯在闪烁着诡异的光芒。在把电路板送给后勤官格林娜做了简单的分析后发现了如下代码:

#include "stm32f10x.h"
#include "led.h"
void Delay(uint32_t jun){
  for(;jun!=0;--jun);
}
int main(void){
   led_gpio_config();
   led_gpio_JUN();
   led_gpio_BIAN();
  while (1)
  {
   LED_G(ON);	 
    LED_B(OFF);
    
    LED_R(OFF);
   Delay(0xFFFFF);	
   LED_G(OFF);	 
    LED_B(ON);
    LED_R(OFF);
    Delay(0xFFFFF);	
    LED_G(OFF);	 
    LED_B(OFF);
    LED_R(ON);
    Delay(0xFFFFF);	
    LED_G(ON);	 
    LED_B(ON);
    LED_R(OFF);
    Delay(0xFFFFF);	
    LED_G(ON);	 
    LED_B(OFF);
    LED_R(ON);
    Delay(0xFFFFF);	
    LED_G(ON);	 
    LED_B(OFF);
    LED_R(ON);
    Delay(0xFFFFF);	
    LED_G(OFF);	 
    LED_B(ON);
    LED_R(ON);
    Delay(0xFFFFF);	
    LED_G(ON);	 
    LED_B(ON);
    LED_R(ON);
    Delay(0xFFFFF);	
  }
}
#include "led.h"
void led_gpio_config(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;

  
  RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin=LED_G_GPIO_PIN;
  
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  
  
  GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
  
}
void led_gpio_JUN(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;
  
  RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
    
  GPIO_InitStruct.GPIO_Pin=LED_B_GPIO_PIN ;
    
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  
  GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
  
}
void led_gpio_BIAN(void){
  
  GPIO_InitTypeDef       GPIO_InitStruct;
  
  RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
  
  GPIO_InitStruct.GPIO_Pin=LED_R_GPIO_PIN ;
  
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  
  GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
  
}
#ifndef led_h
#define led_h
#include "stm32f10x.h"
#define LED_G_GPIO_PIN     GPIO_Pin_0
#define LED_R_GPIO_PIN     GPIO_Pin_5 
#define LED_B_GPIO_PIN     GPIO_Pin_1 
#define LED_G_GPIO_PORT    GPIOB
#define LED_G_GPIO_CLK     RCC_APB2Periph_GPIOB
#define ON   1
#define OFF  0
#define LED_G(JUN) if(JUN)GPIO_ResetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);else GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);
#define LED_B(JUN) if(JUN)GPIO_ResetBits(LED_G_GPIO_PORT, LED_B_GPIO_PIN);else GPIO_SetBits(LED_G_GPIO_PORT, LED_B_GPIO_PIN);
#define LED_R(JUN) if(JUN)GPIO_ResetBits(LED_G_GPIO_PORT, LED_R_GPIO_PIN);else GPIO_SetBits(LED_G_GPIO_PORT, LED_R_GPIO_PIN);
void led_gpio_config(void);
void led_gpio_JUN(void);
void led_gpio_BIAN(void);
#endif

后勤官格林娜表示她曾经有在军校时有学过代码,这里面的意思好像是先对官方固件库中的变量进行声明,再自己写一个函数并将刚才的声明过的变量写进去,最后再将写好的函数放到main函数中,以此来完成开关电路板芯片中的时钟,决定gpio输入输出模式和速度以及哪个引脚需要输出高低点平。最后通过while函数和delay进行闪烁。

在指挥官卧室发现的电路板的工作原理知道了,但指挥官为什么要干这件事呢?这里的代码和上次在指挥官办公室发现的又有什么关系呢?想知道真相的观众们敬请期待下一次的“小狮子 带侦探”!

ILI9341 LCD显示屏 HAL库驱动代码

LCD-BSP.c 文件

#include "lcd/bsp_lcd.h"
#include "lcd/ascii.h"

/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
__IO uint32_t lcd_id=0;                // 保存当前检查到的液晶模块ID
SRAM_HandleTypeDef hlcd;               // SRAM外设句柄
static int FSMC_LCD_Initialized = 0;   // FSMC初始化标志位:0:未初始化;1:已完成初始化
static int FSMC_LCD_DeInitialized = 0; // FSMC反初始化标志位:0:未反初始化;1:已完成反初始化
uint16_t LCD_X_LENGTH = ILI9341_LESS_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9341_MORE_PIXEL;
uint8_t LCD_SCAN_MODE = 6;


/**
  * @brief  向ILI9341写入命令
  * @param  usCmd :要写入的命令(表寄存器地址)
  * @retval 无
  */	
//__inline void LCD_WRITE_CMD ( uint16_t usCmd )
//{
//	* ( __IO uint16_t * ) ( FSMC_LCD_CMD ) = usCmd;
//	
//}


///**
//  * @brief  向ILI9341写入数据
//  * @param  usData :要写入的数据
//  * @retval 无
//  */	
//__inline void LCD_WRITE_DATA ( uint16_t usData )
//{
//	* ( __IO uint16_t * ) ( FSMC_LCD_DATA ) = usData;
//	
//}


///**
//  * @brief  从ILI9341读取数据
//  * @param  无
//  * @retval 读取到的数据
//  */	
//__inline uint16_t LCD_READ_DATA ( void )
//{
//	return ( * ( __IO uint16_t * ) ( FSMC_LCD_DATA) );
//	
//}
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: 初始化LCD的IO引脚
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:LCD控制器ILI9488相当于一个外部SRAM操作
  *           该函数被HAL_SRAM_MspInit函数调用
  */
static void HAL_FSMC_LCD_MspInit(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;	
uint16_t LCD_X_LENGTH = ILI9341_LESS_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9341_MORE_PIXEL;

//液晶屏扫描模式,本变量主要用于方便选择触摸屏的计算参数
//参数可选值为0-7
//调用ILI9341_GramScan函数设置方向时会自动更改
//LCD刚初始化完成时会使用本默认值
uint8_t LCD_SCAN_MODE = 6;



  /* 如果已经完成初始化就无需初始化第二遍 */
  if(FSMC_LCD_Initialized)
  {
    return;
  }
  FSMC_LCD_Initialized = 1;
  
  /* 使能相关端口时钟 */
  FSMC_LCD_CS_GPIO_ClK_ENABLE();
  FSMC_LCD_DC_GPIO_ClK_ENABLE();
  FSMC_LCD_BK_GPIO_ClK_ENABLE();  
//  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  
  /* 使能FSMC外设时钟 */
  __HAL_RCC_FSMC_CLK_ENABLE();


  GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10 
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14 
                          |GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14 
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4 
                          |GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = ILI9341_RD_PIN; 
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init (ILI9341_RD_PORT,  &GPIO_InitStruct );
  
  GPIO_InitStruct.Pin = ILI9341_WR_PIN; 
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(ILI9341_WR_PORT, &GPIO_InitStruct );
  
    GPIO_InitStruct.Pin = FSMC_LCD_CS_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(FSMC_LCD_CS_PORT, &GPIO_InitStruct);
  
    GPIO_InitStruct.Pin = FSMC_LCD_DC_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(FSMC_LCD_DC_PORT, &GPIO_InitStruct);



  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.Pin = ILI9341_RST_PIN; 
  HAL_GPIO_Init ( ILI9341_RST_PORT, &GPIO_InitStruct );

  /* 输出低电平:背光不亮 */
  HAL_GPIO_WritePin(FSMC_LCD_BK_PORT, FSMC_LCD_BK_PIN, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(ILI9341_RST_PORT, ILI9341_RST_PIN, GPIO_PIN_SET);
  /* 液晶背光控制引脚初始化 */
  GPIO_InitStruct.Pin = FSMC_LCD_BK_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(FSMC_LCD_BK_PORT, &GPIO_InitStruct);
}

/**
  * 函数功能: 初始化FSMC的IO引脚
  * 输入参数: hsram:SRAM外设句柄指针
  * 返 回 值: 无
  * 说    明:该函数被HAL库内部函数调用
  */	
void HAL_SRAM_MspInit(SRAM_HandleTypeDef* hsram)
{
  HAL_FSMC_LCD_MspInit();
}

/**
  * 函数功能: 反初始化LCD的IO引脚
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:LCD控制器ILI9488相当于一个外部SRAM操作
  *           该函数被HAL_SRAM_MspDeInit函数调用
  */
static void HAL_FSMC_LCD_MspDeInit(void)
{
  /* 如果已经完成反初始化就无需初始化第二遍 */
  if(FSMC_LCD_DeInitialized)
  {
    return;
  }
  FSMC_LCD_DeInitialized = 1;
  
  /* 禁用FSMC外设时钟 */
  __HAL_RCC_FSMC_CLK_DISABLE();
  
  /** FSMC GPIO Configuration  
  PF0   ------> FSMC_A0
  PE7   ------> FSMC_D4
  PE8   ------> FSMC_D5
  PE9   ------> FSMC_D6
  PE10   ------> FSMC_D7
  PE11   ------> FSMC_D8
  PE12   ------> FSMC_D9
  PE13   ------> FSMC_D10
  PE14   ------> FSMC_D11
  PE15   ------> FSMC_D12
  PD8   ------> FSMC_D13
  PD9   ------> FSMC_D14
  PD10   ------> FSMC_D15
  PD14   ------> FSMC_D0
  PD15   ------> FSMC_D1
  PD0   ------> FSMC_D2
  PD1   ------> FSMC_D3
  PD4   ------> FSMC_NOE
  PD5   ------> FSMC_NWE
  PG12   ------> FSMC_NE4
  */
  HAL_GPIO_DeInit(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10 
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14 
                          |GPIO_PIN_15);

  HAL_GPIO_DeInit(GPIOD, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14 
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4 
                          |GPIO_PIN_5);
  HAL_GPIO_DeInit(FSMC_LCD_DC_PORT, FSMC_LCD_DC_PIN);
  HAL_GPIO_DeInit(FSMC_LCD_CS_PORT, FSMC_LCD_CS_PIN);
}

/**
  * 函数功能: 反初始化FSMC的IO引脚
  * 输入参数: hsram:SRAM外设句柄指针
  * 返 回 值: 无
  * 说    明:该函数被HAL库内部函数调用
  */	
void HAL_SRAM_MspDeInit(SRAM_HandleTypeDef* hsram)
{
  HAL_FSMC_LCD_MspDeInit();
}


/**
  * 函数功能: LCD  FSMC 模式配置
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:读写使用相同时间配置
  */
void MX_FSMC_Init(void)
{
  FSMC_NORSRAM_TimingTypeDef Timing;

  /* 配置FSMC参数 */
  hlcd.Instance = FSMC_NORSRAM_DEVICE;
  hlcd.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;

  hlcd.Init.NSBank = FSMC_LCD_BANKx;
  hlcd.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
  hlcd.Init.MemoryType = FSMC_MEMORY_TYPE_NOR;
  hlcd.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hlcd.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hlcd.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hlcd.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hlcd.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
  hlcd.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  hlcd.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hlcd.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
//  hlcd.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hlcd.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;

  Timing.AddressSetupTime      = 0x01; //地址建立时间
  Timing.AddressHoldTime       = 0x00; //地址保持时间
  Timing.DataSetupTime         = 0x04; //数据建立时间
  Timing.BusTurnAroundDuration = 0x00;
  Timing.CLKDivision           = 0x00;
  Timing.DataLatency           = 0x00;
  Timing.AccessMode = FSMC_ACCESS_MODE_B;
  HAL_SRAM_Init(&hlcd, &Timing, &Timing);



  /* Disconnect NADV */
  __HAL_AFIO_FSMCNADV_DISCONNECTED();
}

static void LCD_Delay ( __IO uint32_t nCount )
{
  for ( ; nCount != 0; nCount -- );
  
}
/**
  * 函数功能: 初始化LCD寄存器
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:需要配置哪些寄存器,需要设置什么值与液晶厂家生产环境密切相关,
  *           所以这些参数由厂家提供,不同厂家可能不同。也可以根据ILI9341芯片
  *           手册内容参考修改。
  */
static void ILI9488_REG_Config ( void )
{

LCD_SetDirection(LCD_DIRECTION);

  DEBUG_DELAY  ();
  LCD_WRITE_CMD ( 0xCF  );
  LCD_WRITE_DATA ( 0x00  );
  LCD_WRITE_DATA ( 0x81  );
  LCD_WRITE_DATA ( 0x30  );
  
  /*  Power on sequence control (EDh) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xED );
  LCD_WRITE_DATA ( 0x64 );
  LCD_WRITE_DATA ( 0x03 );
  LCD_WRITE_DATA ( 0x12 );
  LCD_WRITE_DATA ( 0x81 );
  
  /*  Driver timing control A (E8h) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xE8 );
  LCD_WRITE_DATA ( 0x85 );
  LCD_WRITE_DATA ( 0x10 );
  LCD_WRITE_DATA ( 0x78 );
  
  /*  Power control A (CBh) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xCB );
  LCD_WRITE_DATA ( 0x39 );
  LCD_WRITE_DATA ( 0x2C );
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x34 );
  LCD_WRITE_DATA ( 0x02 );
  
  /* Pump ratio control (F7h) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xF7 );
  LCD_WRITE_DATA ( 0x20 );
  
  /* Driver timing control B */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xEA );
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x00 );
  
  /* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xB1 );
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x1B );
  
  /*  Display Function Control (B6h) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xB6 );
  LCD_WRITE_DATA ( 0x0A );
  LCD_WRITE_DATA ( 0xA2 );
  
  /* Power Control 1 (C0h) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xC0 );
  LCD_WRITE_DATA ( 0x35 );
  
  /* Power Control 2 (C1h) */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0xC1 );
  LCD_WRITE_DATA ( 0x11 );
  
  /* VCOM Control 1 (C5h) */
  LCD_WRITE_CMD ( 0xC5 );
  LCD_WRITE_DATA ( 0x45 );
  LCD_WRITE_DATA ( 0x45 );
  
  /*  VCOM Control 2 (C7h)  */
  LCD_WRITE_CMD ( 0xC7 );
  LCD_WRITE_DATA ( 0xA2 );
  
  /* Enable 3G (F2h) */
  LCD_WRITE_CMD ( 0xF2 );
  LCD_WRITE_DATA ( 0x00 );
  
  /* Gamma Set (26h) */
  LCD_WRITE_CMD ( 0x26 );
  LCD_WRITE_DATA ( 0x01 );
  DEBUG_DELAY ();
  
  /* Positive Gamma Correction */
  LCD_WRITE_CMD ( 0xE0 ); //Set Gamma
  LCD_WRITE_DATA ( 0x0F );
  LCD_WRITE_DATA ( 0x26 );
  LCD_WRITE_DATA ( 0x24 );
  LCD_WRITE_DATA ( 0x0B );
  LCD_WRITE_DATA ( 0x0E );
  LCD_WRITE_DATA ( 0x09 );
  LCD_WRITE_DATA ( 0x54 );
  LCD_WRITE_DATA ( 0xA8 );
  LCD_WRITE_DATA ( 0x46 );
  LCD_WRITE_DATA ( 0x0C );
  LCD_WRITE_DATA ( 0x17 );
  LCD_WRITE_DATA ( 0x09 );
  LCD_WRITE_DATA ( 0x0F );
  LCD_WRITE_DATA ( 0x07 );
  LCD_WRITE_DATA ( 0x00 );
  
  /* Negative Gamma Correction (E1h) */
  LCD_WRITE_CMD ( 0XE1 ); //Set Gamma
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x19 );
  LCD_WRITE_DATA ( 0x1B );
  LCD_WRITE_DATA ( 0x04 );
  LCD_WRITE_DATA ( 0x10 );
  LCD_WRITE_DATA ( 0x07 );
  LCD_WRITE_DATA ( 0x2A );
  LCD_WRITE_DATA ( 0x47 );
  LCD_WRITE_DATA ( 0x39 );
  LCD_WRITE_DATA ( 0x03 );
  LCD_WRITE_DATA ( 0x06 );
  LCD_WRITE_DATA ( 0x06 );
  LCD_WRITE_DATA ( 0x30 );
  LCD_WRITE_DATA ( 0x38 );
  LCD_WRITE_DATA ( 0x0F );
  
  /* memory access control set */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0x36 ); 	
  LCD_WRITE_DATA ( 0xC8 );    /*竖屏  左上角到 (起点)到右下角 (终点)扫描方式*/
  DEBUG_DELAY ();
  
  /* column address control set */
  LCD_WRITE_CMD ( CMD_SetCoordinateX ); 
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0xEF );
  
  /* page address control set */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( CMD_SetCoordinateY ); 
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x00 );
  LCD_WRITE_DATA ( 0x01 );
  LCD_WRITE_DATA ( 0x3F );
  
  /*  Pixel Format Set (3Ah)  */
  DEBUG_DELAY ();
  LCD_WRITE_CMD ( 0x3a ); 
  LCD_WRITE_DATA ( 0x55 );
  
  /* Sleep Out (11h)  */
  LCD_WRITE_CMD ( 0x11 );	
  LCD_Delay ( 0xAFFf<<2 );
  DEBUG_DELAY ();
  
  /* Display ON (29h) */
  LCD_WRITE_CMD ( 0x29 ); 
  
}

/**
  * 函数功能: 读取液晶模组ID
  * 输入参数: 无
  * 返 回 值: 液晶模组的ID
  * 说    明:这是通过读取04H寄存器获取得到液晶模组ID,该ID值有液晶厂家编程,不同液晶
  *           厂家的液晶模组得到的ID值可能不同。这也可以分辨不同型号的液晶模组。
  */
static uint32_t LCD_ReadID(void)
{
  uint16_t buf[4];

  LCD_WRITE_CMD(0xD3);  
  buf[0] = LCD_READ_DATA();        // 第一个读取数据无效
  buf[1] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
  buf[2] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
  buf[3] = LCD_READ_DATA()&0x00ff; // 只有低8位数据有效
  return (buf[1] << 16) + (buf[2] << 8) + buf[3];  
}

/**
  * 函数功能: 液晶模组初始化
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
uint32_t BSP_LCD_Init(void)
{
  HAL_FSMC_LCD_MspInit();
  MX_FSMC_Init();
  
  lcd_id=LCD_ReadID();

  ILI9488_REG_Config();
  
  LCD_Clear(0,0,LCD_DEFAULT_WIDTH,LCD_DEFAULT_HEIGTH,BLACK);
  HAL_Delay(20);
  
  return lcd_id;
}

/**
  * 函数功能: 设置LCD的GRAM的扫描方向 
  * 输入参数: ucOption :选择GRAM的扫描方向 
  *           可选值:1 :原点在屏幕左上角 X*Y=320*480
  *                   2 :原点在屏幕右上角 X*Y=480*320
  *                   3 :原点在屏幕右下角 X*Y=320*480
  *                   4 :原点在屏幕左下角 X*Y=480*320
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_SetDirection( uint8_t ucOption )
{	
  if(ucOption >7 )
    return;
  
  //根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
  LCD_SCAN_MODE = ucOption;
  
  //根据模式更新XY方向的像素宽度
  if(ucOption%2 == 0)	
  {
    //0 2 4 6模式下X方向像素宽度为240,Y方向为320
    LCD_X_LENGTH = ILI9341_LESS_PIXEL;
    LCD_Y_LENGTH =	ILI9341_MORE_PIXEL;
  }
  else				
  {
    //1 3 5 7模式下X方向像素宽度为320,Y方向为240
    LCD_X_LENGTH = ILI9341_MORE_PIXEL;
    LCD_Y_LENGTH =	ILI9341_LESS_PIXEL; 
  }

  //0x36命令参数的高3位可用于设置GRAM扫描方向	
  LCD_WRITE_CMD ( 0x36 ); 
  LCD_WRITE_DATA ( 0x08 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
  LCD_WRITE_CMD ( CMD_SetCoordinateX ); 
  LCD_WRITE_DATA ( 0x00 );		/* x 起始坐标高8位 */
  LCD_WRITE_DATA ( 0x00 );		/* x 起始坐标低8位 */
  LCD_WRITE_DATA ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 结束坐标高8位 */	
  LCD_WRITE_DATA ( (LCD_X_LENGTH-1)&0xFF );				/* x 结束坐标低8位 */

  LCD_WRITE_CMD ( CMD_SetCoordinateY ); 
  LCD_WRITE_DATA ( 0x00 );		/* y 起始坐标高8位 */
  LCD_WRITE_DATA ( 0x00 );		/* y 起始坐标低8位 */
  LCD_WRITE_DATA ( ((LCD_Y_LENGTH-1)>>8)&0xFF );	/* y 结束坐标高8位 */	 
  LCD_WRITE_DATA ( (LCD_Y_LENGTH-1)&0xFF );				/* y 结束坐标低8位 */

  /* write gram start */
  LCD_WRITE_CMD ( CMD_SetPixel );	
}

/**
  * 函数功能: 在LCD显示器上开辟一个窗口
  * 输入参数: usX :在特定扫描方向下窗口的起点X坐标
  *           usY :在特定扫描方向下窗口的起点Y坐标
  *           usWidth :窗口的宽度
  *           usHeight :窗口的高度
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{	
  LCD_WRITE_CMD(CMD_SetCoordinateX ); 				       /* 设置X坐标 */
  LCD_WRITE_DATA(usX>>8);	             /* 设置起始点:先高8位 */
  LCD_WRITE_DATA(usX&0xff);	           /* 然后低8位 */
  LCD_WRITE_DATA((usX+usWidth-1)>>8);  /* 设置结束点:先高8位 */
  LCD_WRITE_DATA((usX+usWidth-1)&0xff);/* 然后低8位 */

  LCD_WRITE_CMD(CMD_SetCoordinateY); 			           /* 设置Y坐标*/
  LCD_WRITE_DATA(usY>>8);              /* 设置起始点:先高8位 */
  LCD_WRITE_DATA(usY&0xff);            /* 然后低8位 */
  LCD_WRITE_DATA((usY+usHeight-1)>>8); /* 设置结束点:先高8位 */
  LCD_WRITE_DATA((usY+usHeight-1)&0xff);/* 然后低8位 */
}

/**
  * 函数功能: 设定LCD的光标坐标
  * 输入参数: usX :在特定扫描方向下窗口的起点X坐标
  *           usY :在特定扫描方向下窗口的起点Y坐标
  * 返 回 值: 无
  * 说    明:无
  */
static void LCD_SetCursor(uint16_t usX,uint16_t usY)	
{
  LCD_OpenWindow(usX,usY,1,1);
}

/**
  * 函数功能: 在LCD显示器上以某一颜色填充像素点
  * 输入参数: ulAmout_Point :要填充颜色的像素点的总数目
  *           usColor :颜色
  * 返 回 值: 无
  * 说    明:无
  */
#if defined ( __CC_ARM )  // 使用Keil编译环境
static __inline void LCD_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
{
  uint32_t i = 0;	
  
  /* 开始向GRAM写入数据 */
  LCD_WRITE_CMD (  CMD_SetPixel);	
  
  for ( i = 0; i < ulAmout_Point; i ++ )
    LCD_WRITE_DATA ( usColor );	
}
#elif defined ( __ICCARM__ ) // 使用IAR编译环境
#pragma inline
static void LCD_FillColor ( uint32_t ulAmout_Point, uint16_t usColor )
{
  uint32_t i = 0;	
  
  /* 开始向GRAM写入数据 */
  LCD_WRITE_CMD ( 0x2C );	
  
  for ( i = 0; i < ulAmout_Point; i ++ )
    LCD_WRITE_DATA ( usColor );	
}
#endif

/**
  * 函数功能: 对LCD显示器的某一窗口以某种颜色进行清屏
  * 输入参数: usX :在特定扫描方向下窗口的起点X坐标
  *           usY :在特定扫描方向下窗口的起点Y坐标
  *           usWidth :窗口的宽度
  *           usHeight :窗口的高度
  *           usColor :颜色
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_Clear(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight,uint16_t usColor)
{	 
#if 0   /* 优化代码执行速度 */
  uint32_t i;
  uint32_t n,m;
  /* 在LCD显示器上开辟一个窗口 */
  LCD_OpenWindow(usX,usY,usWidth,usHeight); 
  /* 开始向GRAM写入数据 */
  LCD_WRITE_CMD(0x2C);
  
  m=usWidth * usHeight;
  n=m/8;
  m=m-8*n;
  for(i=0;i<n;i++)
  {
    LCD_WRITE_DATA(usColor);	
    LCD_WRITE_DATA(usColor);	
    LCD_WRITE_DATA(usColor);	
    LCD_WRITE_DATA(usColor);	
    
    LCD_WRITE_DATA(usColor);	
    LCD_WRITE_DATA(usColor);	
    LCD_WRITE_DATA(usColor);	
    LCD_WRITE_DATA(usColor);	
  }
  for(i=0;i<m;i++)
  {
    LCD_WRITE_DATA(usColor);	
  }
#else
  /* 在LCD显示器上开辟一个窗口 */
  LCD_OpenWindow(usX,usY,usWidth,usHeight);
  /* 在LCD显示器上以某一颜色填充像素点 */
  LCD_FillColor(usWidth*usHeight,usColor);	
#endif	
}

/**
  * 函数功能: 对LCD显示器的某一点以某种颜色进行填充
  * 输入参数: usX :在特定扫描方向下窗口的起点X坐标
  *           usY :在特定扫描方向下窗口的起点Y坐标
  *           usColor :颜色
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_SetPointPixel(uint16_t usX,uint16_t usY,uint16_t usColor)	
{	
  if((usX<LCD_DEFAULT_WIDTH)&&(usY<LCD_DEFAULT_HEIGTH))
  {
    LCD_OpenWindow(usX,usY,1,1);
    LCD_FillColor(1,usColor);
  }
}

/**
  * 函数功能: 对LCD显示器的某一点以某种颜色进行填充
  * 输入参数: 无
  * 返 回 值: uint16_t:像素数据RGB565
  * 说    明:无
  */
static uint16_t LCD_Read_PixelData ( void )	
{	
  uint16_t usR=0, usG=0, usB=0 ;
  
  LCD_WRITE_CMD ( 0x2E );   /* 读数据 */
  usR = LCD_READ_DATA (); 	/*FIRST READ OUT DUMMY DATA*/
  
  usR = LCD_READ_DATA ();  	/*READ OUT RED DATA  */
  usB = LCD_READ_DATA ();  	/*READ OUT BLUE DATA*/
  usG = LCD_READ_DATA ();  	/*READ OUT GREEN DATA*/	
  
  return (((usR>>11)<<11) | ((usG>>10)<<5) | (usB>>11));
  
}

/**
  * 函数功能: 获取 LCD 显示器上某一个坐标点的像素数据
  * 输入参数: usX :在特定扫描方向下窗口的起点X坐标
  *           usY :在特定扫描方向下窗口的起点Y坐标
  * 返 回 值: uint16_t:像素数据RGB565
  * 说    明:无
  */
uint16_t LCD_GetPointPixel ( uint16_t usX, uint16_t usY )
{ 
  uint16_t usPixelData;
  
  LCD_SetCursor ( usX, usY );
  
  usPixelData = LCD_Read_PixelData ();
  
  return usPixelData;
  
}

/**
  * 函数功能: 在 LCD 显示器上使用 Bresenham 算法画线段
  * 输入参数: usX1 :在特定扫描方向下窗口的起点X坐标
  *           usY1 :在特定扫描方向下窗口的起点Y坐标
  *           usX2 :在特定扫描方向下线段的另一个端点X坐标
  *           usY2 :在特定扫描方向下线段的另一个端点Y坐标
  *           usColor :线段的颜色
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_DrawLine(uint16_t usX1,uint16_t usY1,uint16_t usX2,uint16_t usY2,uint16_t usColor)
{
  uint16_t us; 
  uint16_t usX_Current, usY_Current;
  int32_t lError_X=0,lError_Y=0,lDelta_X,lDelta_Y,lDistance; 
  int32_t lIncrease_X, lIncrease_Y;
  
  lDelta_X=usX2-usX1; //计算坐标增量 
  lDelta_Y=usY2-usY1; 
  usX_Current = usX1; 
  usY_Current = usY1; 
  
  if(lDelta_X>0)
  {
    lIncrease_X=1; //设置单步方向 
  }
  else if(lDelta_X==0)
  {
    lIncrease_X=0;//垂直线 
  }
  else 
  { 
    lIncrease_X=-1;
    lDelta_X=-lDelta_X;
  }
  
  if(lDelta_Y>0)
  {
    lIncrease_Y=1;
  }
  else if(lDelta_Y==0)
  {
    lIncrease_Y=0;//水平线 
  }
  else
  {
    lIncrease_Y=-1;
    lDelta_Y=-lDelta_Y;
  }
  
  if(lDelta_X>lDelta_Y)
  {
    lDistance=lDelta_X; //选取基本增量坐标轴 
  }
  else
  {
    lDistance=lDelta_Y; 
  }
  
  for(us=0;us<=lDistance+1;us++)//画线输出 
  {
    LCD_SetPointPixel(usX_Current,usY_Current,usColor);//画点 
    lError_X+=lDelta_X; 
    lError_Y+=lDelta_Y;
    if(lError_X>lDistance)
    { 
      lError_X-=lDistance; 
      usX_Current+=lIncrease_X; 
    }
    if(lError_Y>lDistance) 
    { 
      lError_Y-=lDistance; 
      usY_Current+=lIncrease_Y; 
    }		
  }
}

/**
  * 函数功能: 在LCD显示器上画一个矩形
  * 输入参数: usX_Start :在特定扫描方向下窗口的起点X坐标
  *           usY_Start :在特定扫描方向下窗口的起点Y坐标
  *           usWidth:矩形的宽度(单位:像素)
  *           usHeight:矩形的高度(单位:像素)
  *           usColor :矩形的颜色
  *           ucFilled :选择是否填充该矩形
  *             可选值:0:空心矩形
  *                     1:实心矩形
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint16_t usColor, uint8_t ucFilled )
{
  if(ucFilled)
  {
    LCD_Clear ( usX_Start, usY_Start, usWidth, usHeight, usColor);
  }
  else
  {
    LCD_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start, usColor );
    LCD_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1, usColor );
    LCD_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1, usColor );
    LCD_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1, usColor );		
  }
}

/**
  * 函数功能: 在 LCD 显示器上使用 Bresenham 算法画圆
  * 输入参数: usX_Center :在特定扫描方向下圆心的X坐标
  *           usY_Center :在特定扫描方向下圆心的Y坐标
  *           usRadius:圆的半径(单位:像素)
  *           usColor :圆的颜色
  *           ucFilled :选择是否填充该圆
  *             可选值:0:空心圆
  *                     1:实心圆
  * 返 回 值: 无
  * 说    明:无
  */
void LCD_DrawCircle(uint16_t usX_Center,uint16_t usY_Center,uint16_t usRadius,uint16_t usColor,uint8_t ucFilled)
{
  int16_t sCurrentX, sCurrentY;
  int16_t sError;
  
  sCurrentX=0;
  sCurrentY=usRadius;	
  sError=3-(usRadius<<1);   //判断下个点位置的标志
  
  while(sCurrentX<=sCurrentY)
  {
    int16_t sCountY;		
    if(ucFilled)
    {			
      for(sCountY=sCurrentX;sCountY<=sCurrentY;sCountY++)
      {                      
        LCD_SetPointPixel(usX_Center+sCurrentX,usY_Center+sCountY,usColor);          //1,研究对象 
        LCD_SetPointPixel(usX_Center-sCurrentX,usY_Center+sCountY,usColor);           //2       
        LCD_SetPointPixel(usX_Center-sCountY,  usY_Center+sCurrentX,usColor);           //3
        LCD_SetPointPixel(usX_Center-sCountY,  usY_Center-sCurrentX,usColor);           //4
        LCD_SetPointPixel(usX_Center-sCurrentX,usY_Center-sCountY,usColor);           //5    
        LCD_SetPointPixel(usX_Center+sCurrentX,usY_Center-sCountY,usColor);           //6
        LCD_SetPointPixel(usX_Center+sCountY,  usY_Center-sCurrentX,usColor);           //7 	
        LCD_SetPointPixel(usX_Center+sCountY,  usY_Center+sCurrentX,usColor);           //0				
      }
    }		
    else
    {          
      LCD_SetPointPixel(usX_Center+sCurrentX,usY_Center+sCurrentY,usColor);             //1,研究对象
      LCD_SetPointPixel(usX_Center-sCurrentX,usY_Center+sCurrentY,usColor);             //2      
      LCD_SetPointPixel(usX_Center-sCurrentY,usY_Center+sCurrentX,usColor);             //3
      LCD_SetPointPixel(usX_Center-sCurrentY,usY_Center-sCurrentX,usColor);             //4
      LCD_SetPointPixel(usX_Center-sCurrentX,usY_Center-sCurrentY,usColor);             //5       
      LCD_SetPointPixel(usX_Center+sCurrentX,usY_Center-sCurrentY,usColor);             //6
      LCD_SetPointPixel(usX_Center+sCurrentY,usY_Center-sCurrentX,usColor);             //7 
      LCD_SetPointPixel(usX_Center+sCurrentY,usY_Center+sCurrentX,usColor);             //0
    }			
    sCurrentX ++;		
    if(sError<0) 
    {
      sError+=(4*sCurrentX+6);	  
    }
    else
    {
      sError +=(10+4*(sCurrentX-sCurrentY));   
      sCurrentY--;
    } 
  }
}

/**
  * 函数功能: 在 LCD 显示器上显示一个英文字符
  * 输入参数: usX:在特定扫描方向下字符的起始X坐标
  *           usY :在特定扫描方向下该点的起始Y坐标
  *           cChar :要显示的英文字符
  *           usColor_Background :选择英文字符的背景色
  *           usColor_Foreground :选择英文字符的前景色
  *           font:字体选择
  *             参数:USE_FONT_16 :16号字体
  *                   USE_FONT_24 :24号字体 
  * 返 回 值: 无
  * 说    明:该函数必须与ascii.h内容对应使用
  */
void LCD_DispChar_EN( uint16_t usX, uint16_t usY, const char cChar, uint16_t usColor_Background, uint16_t usColor_Foreground,USE_FONT_Typdef font)
{
  uint8_t ucTemp, ucRelativePositon, ucPage, ucColumn;
  
  /* 检查输入参数是否合法 */
  assert_param(IS_USE_FONT(font));
  
  ucRelativePositon = cChar - ' ';
  
  if(font==USE_FONT_16)
  {
    LCD_OpenWindow(usX,usY,8,16);
    LCD_WRITE_CMD(0x2C);
    
    for(ucPage=0;ucPage<16;ucPage++)
    {
      ucTemp=ucAscii_1608[ucRelativePositon][ucPage];		
      for(ucColumn=0;ucColumn<8;ucColumn++)
      {
        if(ucTemp&0x01)
          LCD_WRITE_DATA(usColor_Foreground);			
        else
          LCD_WRITE_DATA(usColor_Background);								
        ucTemp >>= 1;					
      }
    }    
  }
  else
  {
    LCD_OpenWindow(usX,usY,12,24);
    LCD_WRITE_CMD(0x2C);
    
    for(ucPage=0;ucPage<48;ucPage++)
    {
      ucTemp=ucAscii_2412[ucRelativePositon][ucPage];		
      for(ucColumn=0;ucColumn<8;ucColumn++)
      {
        if(ucTemp&0x01)
          LCD_WRITE_DATA(usColor_Foreground);			
        else
          LCD_WRITE_DATA(usColor_Background);								
        ucTemp >>= 1;					
      }	
      ucPage++;
      ucTemp=ucAscii_2412[ucRelativePositon][ucPage];
      /* 只显示前面4个位,与上面8位总共12位 */
      for(ucColumn=0;ucColumn<4;ucColumn++)
      {
        if(ucTemp&0x01)
          LCD_WRITE_DATA(usColor_Foreground);			
        else
          LCD_WRITE_DATA(usColor_Background);								
        ucTemp >>= 1;					
      }	
    }
  }	
}

/**
  * 函数功能: 在 LCD 显示器上显示英文字符串
  * 输入参数: usX:在特定扫描方向下字符的起始X坐标
  *           usY :在特定扫描方向下该点的起始Y坐标
  *           pStr :要显示的英文字符串的首地址
  *           usColor_Background :选择英文字符的背景色
  *           usColor_Foreground :选择英文字符的前景色
  *           font:字体选择
  *             参数:USE_FONT_16 :16号字体
  *                   USE_FONT_24 :24号字体 
  * 返 回 值: 无
  * 说    明:该函数必须与ascii.h内容对应使用
 
void LCD_DispString_EN ( uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,USE_FONT_Typdef font)
{
  /* 检查输入参数是否合法 */
  assert_param(IS_USE_FONT(font));
  
  while ( * pStr != '\0' )
  {
    if(font==USE_FONT_16)
    {
      if ( ( usX +  8 ) > LCD_DEFAULT_WIDTH )
      {
        usX = 0;
        usY += 16;
      }      
      if ( ( usY +  16 ) > LCD_DEFAULT_HEIGTH )
      {
        usX = 0;
        usY = 0;
      }      
      LCD_DispChar_EN ( usX, usY, * pStr, usColor_Background, usColor_Foreground,font);
      pStr ++;      
      usX += 8;
    }
    else
    {
      if ( ( usX +  12 ) > LCD_DEFAULT_WIDTH )
      {
        usX = 0;
        usY += 24;
      }      
      if ( ( usY +  24 ) > LCD_DEFAULT_HEIGTH )
      {
        usX = 0;
        usY = 0;
      }      
      LCD_DispChar_EN ( usX, usY, * pStr, usColor_Background, usColor_Foreground,font);
      pStr ++;      
      usX += 12;
    }
  }
}
 */

LCD-BSP.h

#ifndef __LCD_BSP_H__
#define	__LCD_BSP_H__

/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* 类型定义 ------------------------------------------------------------------*/
typedef enum
{
  USE_FONT_16=16,
  USE_FONT_24=24,
}USE_FONT_Typdef;
#define IS_USE_FONT(FONT)           (((FONT) == USE_FONT_16) || ((FONT) == USE_FONT_24))
#define      DEBUG_DELAY()  
#define      CMD_SetCoordinateX		 		    0x2A	     //设置X坐标
#define      CMD_SetCoordinateY		 		    0x2B	     //设置Y坐标
#define      CMD_SetPixel		 		          0x2C	     //填充像素
/* 宏定义 --------------------------------------------------------------------*/
/******************************************************************************
2^26 =0X0400 0000 = 64MB,每个 BANK 有4*64MB = 256MB
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

*******************************************************************************/
/******************************* ILI9341 显示屏的 FSMC 参数定义 ***************/
#define FSMC_LCD_CMD            ( ( uint32_t ) 0x60000000 )                       //FSMC_Bank1_NORSRAM1用于LCD命令操作的地址
#define FSMC_LCD_DATA           ( ( uint32_t ) 0x60020000 )                       //FSMC_Bank1_NORSRAM1用于LCD数据操作的地址      
#define LCD_WRITE_CMD(x)               *(__IO uint16_t *)FSMC_LCD_CMD  = x 
#define LCD_WRITE_DATA(x)              *(__IO uint16_t *)FSMC_LCD_DATA = x
#define LCD_READ_DATA()                *(__IO uint16_t *)FSMC_LCD_DATA

#define FSMC_LCD_BANKx                 FSMC_NORSRAM_BANK1

/************************* ILI9341 显示屏8080通讯引脚定义 *********************/
#define FSMC_LCD_CS_GPIO_ClK_ENABLE()  __HAL_RCC_GPIOD_CLK_ENABLE() 
#define FSMC_LCD_CS_PORT               GPIOD
#define FSMC_LCD_CS_PIN                GPIO_PIN_7

#define FSMC_LCD_DC_GPIO_ClK_ENABLE()  __HAL_RCC_GPIOD_CLK_ENABLE()
#define FSMC_LCD_DC_PORT               GPIOD
#define FSMC_LCD_DC_PIN                GPIO_PIN_11

#define FSMC_LCD_BK_GPIO_ClK_ENABLE()  __HAL_RCC_GPIOD_CLK_ENABLE()   
#define FSMC_LCD_BK_PORT               GPIOD
#define FSMC_LCD_BK_PIN                GPIO_PIN_12

//写使能
#define      ILI9341_WR_CLK               __HAL_RCC_GPIOD_CLK_ENABLE() 
#define      ILI9341_WR_PORT               GPIOD
#define      ILI9341_WR_PIN                GPIO_PIN_5

//读使能
#define      ILI9341_RD_CLK              __HAL_RCC_GPIOD_CLK_ENABLE() 
#define      ILI9341_RD_PORT               GPIOD
#define      ILI9341_RD_PIN                GPIO_PIN_4

//复位引脚
#define      ILI9341_RST_CLK              __HAL_RCC_GPIOD_CLK_ENABLE() 
#define      ILI9341_RST_PORT              GPIOE
#define      ILI9341_RST_PIN               GPIO_PIN_1

 
#define LCD_BK_ON()                    HAL_GPIO_WritePin(FSMC_LCD_BK_PORT, FSMC_LCD_BK_PIN, GPIO_PIN_SET);
#define LCD_BK_OFF()                   HAL_GPIO_WritePin(FSMC_LCD_BK_PORT, FSMC_LCD_BK_PIN, GPIO_PIN_RESET);

/**************** 显示方向选择,可选(1,2,3,4)四个方向 *************************/
#define LCD_DIRECTION                  6  // 原点在屏幕左上角 X*Y=320*480
//#define LCD_DIRECTION                  2  // 原点在屏幕右上角 X*Y=480*320
//#define LCD_DIRECTION                  3  // 原点在屏幕右下角 X*Y=320*480
//#define LCD_DIRECTION                  4  // 原点在屏幕左下角 X*Y=480*320

/******** ILI934 显示屏全屏默认(扫描方向为1时)最大宽度和最大高度*************/
#if (LCD_DIRECTION==1)||(LCD_DIRECTION==3)

 #define LCD_DEFAULT_WIDTH		         240  // X轴长度
 #define LCD_DEFAULT_HEIGTH         	 320  // Y轴长度
 
#else

 #define LCD_DEFAULT_WIDTH		         240  // X轴长度
 #define LCD_DEFAULT_HEIGTH         	 320  // Y轴长度
 
#endif
#define 			ILI9341_LESS_PIXEL	  							240			//液晶屏较短方向的像素宽度
#define 			ILI9341_MORE_PIXEL	 								320			//液晶屏较长方向的像素宽度
/******************************* 定义 ILI9488 显示屏常用颜色 ********************************/
#define BACKGROUND		                 WHITE     //默认背景颜色
extern uint8_t LCD_SCAN_MODE;


#define      WHITE		 		                 0xFFFF	   //白色
#define      BLACK                         0x0000	   //黑色 
#define      GREY                          0xF7DE	   //灰色 
#define      BLUE                          0x001F	   //蓝色 
#define      BLUE2                         0x051F	   //浅蓝色 
#define      RED                           0xF800	   //红色 
#define      MAGENTA                       0xF81F	   //红紫色,洋红色 
#define      GREEN                         0x07E0	   //绿色 
#define      CYAN                          0x7FFF	   //蓝绿色,青色 
#define      YELLOW                        0xFFE0	   //黄色 
#define      BRED                          0xF81F
#define      GRED                          0xFFE0
#define      GBLUE                         0x07FF

/* 扩展变量 ------------------------------------------------------------------*/
extern SRAM_HandleTypeDef hlcd;
/* 函数声明 ------------------------------------------------------------------*/
uint32_t BSP_LCD_Init(void);
void LCD_SetDirection(uint8_t ucOtion);
void LCD_OpenWindow(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight);
void LCD_Clear(uint16_t usX,uint16_t usY,uint16_t usWidth,uint16_t usHeight,uint16_t usColor);
void LCD_SetPointPixel(uint16_t usX,uint16_t usY,uint16_t usColor);
uint16_t LCD_GetPointPixel(uint16_t usX,uint16_t usY);
void LCD_DrawLine(uint16_t usX1,uint16_t usY1,uint16_t usX2,uint16_t usY2,uint16_t usColor);
void LCD_DrawRectangle(uint16_t usX_Start,uint16_t usY_Start, uint16_t usWidth,uint16_t usHeight,uint16_t usColor,uint8_t ucFilled);
void LCD_DrawCircle(uint16_t usX_Center,uint16_t usY_Center,uint16_t usRadius,uint16_t usColor,uint8_t ucFilled);
void LCD_DispChar_EN(uint16_t usX,uint16_t usY,const char cChar,uint16_t usColor_Background,uint16_t usColor_Foreground,USE_FONT_Typdef font);
void LCD_DispString_EN(uint16_t usX,uint16_t usY,const char *pStr,uint16_t usColor_Background,uint16_t usColor_Foreground,USE_FONT_Typdef font);


/********************************** 声明 ILI934 函数 ***************************************/
void                     ILI9341_Init                    ( void );
void                     ILI9341_Rst                     ( void );
void                     ILI9341_BackLed_Control         ( FunctionalState enumState );
void                     ILI9341_GramScan                ( uint8_t ucOtion );
void                     ILI9341_OpenWindow              ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9341_Clear                   ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9341_SetPointPixel           ( uint16_t usX, uint16_t usY );
uint16_t                 ILI9341_GetPointPixel           ( uint16_t usX , uint16_t usY );
void                     ILI9341_DrawLine                ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 );
void                     ILI9341_DrawRectangle           ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight,uint8_t ucFilled );
void                     ILI9341_DrawCircle              ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled );
void                     ILI9341_DispChar_EN             ( uint16_t usX, uint16_t usY, const char cChar );
void                     ILI9341_DispStringLine_EN      ( uint16_t line, char * pStr );
void                     ILI9341_DispString_EN      			( uint16_t usX, uint16_t usY, char * pStr );
void 											ILI9341_DispString_EN_YDir 		(   uint16_t usX,uint16_t usY ,  char * pStr );


void 											LCD_ClearLine										(uint16_t Line);
void 											LCD_SetBackColor								(uint16_t Color);
void 											LCD_SetTextColor								(uint16_t Color)	;
void 											LCD_SetColors										(uint16_t TextColor, uint16_t BackColor);
void 											LCD_GetColors										(uint16_t *TextColor, uint16_t *BackColor);
#endif /* __LCD_BSP_H__ */

main.c

#include "stm32f1xx_hal.h"
#include "usart/bsp_debug_usart.h"
#include "lcd/bsp_lcd.h"
#include "stdlib.h"

/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: 系统时钟配置
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;  // 外部晶振,8MHz
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;  // 9倍频,得到72MHz主时钟
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;       // 系统时钟:72MHz
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;              // AHB时钟:72MHz
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;               // APB1时钟:36MHz
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;               // APB2时钟:72MHz
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

 	// HAL_RCC_GetHCLKFreq()/1000    1ms中断一次
  // HAL_RCC_GetHCLKFreq()/100000	 10us中断一次
  // HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);  // 配置并启动系统滴答定时器
  /* 系统滴答定时器时钟源 */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
  /* 系统滴答定时器中断优先级配置 */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/**
  * 函数功能: 主函数.
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
int main(void)
{
  uint32_t lcdid;
  uint16_t color;
  
  /* 复位所有外设,初始化Flash接口和系统滴答定时器 */
 // HAL_Init();
  /* 配置系统时钟 */
  SystemClock_Config();
  
  /* 初始化3.5寸TFT液晶模组,一般优先于调试串口初始化 */
  lcdid=BSP_LCD_Init(); 
  
  
 /* 调用格式化输出函数打印输出数据 */
//  printf("LCD ID=0x%08X\n",lcdid);
  
  LCD_Clear(0,0,LCD_DEFAULT_WIDTH,LCD_DEFAULT_HEIGTH,BLUE);
  
  /* 开背光 */
 // LCD_BK_OFF();
  
  /* 初始化随机种子 */
  srand(0xffff);
  
  /* 无限循环 */
  while (1)
  {
    HAL_Delay(1000);
    /* 获取随机数 */
     color=rand();
     LCD_Clear(0,0,LCD_DEFAULT_WIDTH,LCD_DEFAULT_HEIGTH,color);
  }
}

用于驱动ILI9341屏幕的HAL库驱动,由标准库ILI9341驱动修改而来,可用于驱动使用FSMC接口8080时序的ILI9341屏幕,以及包含相关的屏幕操作函数,可以方便的在main入口函数中调用,达到显示的效果。