搜档网
当前位置:搜档网 › STM32学习笔记(6):LCD的显示

STM32学习笔记(6):LCD的显示

STM32学习笔记(6):LCD的显示
STM32学习笔记(6):LCD的显示

STM32学习笔记(6):LCD的显示

2011年4月14日LCD显示1.LCD/LCM的基本概念

液晶显示器(Liquid Crystal Display: LCD)的构造是在两片平行的玻璃当中放置液态的晶体,两片玻璃中间有许多垂直和水平的细小电线,透过通电与否来控制杆状水晶分子改变方向,将光线折射出来产生画面。

LCM(LCD Module)即LCD显示模组、液晶模块,是指将液晶显示器件,连接件,控制与驱动等外围电路,PCB电路板,背光源,结构件等装配在一起的组件。

在平时的学习开发中,我们一般使用的是LCM,带有驱动IC和LCD屏幕等多个模块。

2.FSMC的基本概念

在STM32上开发LCD显示,可以有两种方式来对LCD进行操作,一种是通过普通的IO口,连接LCM的相应引脚来进行操作,第2种是通过FSMC来进行操作。

可变静态存储控制器(Flexible Static Memory Controller: FSMC) 是STM32系列中内部集成256 KB以上FlaSh,后缀为xC、xD和xE的高存储密度微控制器特有的存储控制机制。之所以称为“可变”,是由于通过对特殊功能寄存器的设置,FSMC能够根据不同的外部存储器类型,发出相应的数据/地址/控制信号类型以匹配信号的速度,从而使得STM32系列微控制器不仅能够应用各种不同类型、不同速度的外部静态存储器,而且能够在不增加外部器件的情况下同时扩展多种不同类型的静态存储器,满足系统设计对存储容量、产品体积以及成本的综合要求。

FSMC有很多优点:

1.支持多种静态存储器类型。STM32通过FSMC可以与SRAM、ROM、PSRAM、NOR Flash和NANDFlash存储器的引

脚直接相连。

2.支持丰富的存储操作方法。FSMC不仅支持多种数据宽度的异步读/写操作,而且支持对NOR、PSRAM、NAND存储

器的同步突发访问方式。

3.支持同时扩展多种存储器。FSMC的映射地址空间中,不同的BANK是独立的,可用于扩展不同类型的存储器。当系

统中扩展和使用多个外部存储器时,FSMC会通过总线悬空延迟时间参数的设置,防止各存储器对总线的访问冲突。

4.支持更为广泛的存储器型号。通过对FSMC的时间参数设置,扩大了系统中可用存储器的速度范围,为用户提供了灵

活的存储芯片选择空间。

5.支持代码从FSMC扩展的外部存储器中直接运行,而不需要首先调入内部SRAM。

FSMC包含两类控制器:

1.1个NOR闪存/SRAM控制器,可以与NOR闪存、SRAM和PSRAM存储器接口。

2.1个NAND闪存/PC卡控制器,可以与NAND闪存、PC卡,CF卡和CF+存储器接口。

控制器产生所有驱动这些存储器的信号时序:

1.16位数据线,用于连接8位或16位的存储器;

2.26位地址线,最多可连续64MB的存储器(这里不包括片选线);

3.5位独立的片选信号线;

4.1组适合不同类型存储器的控制信号线:

-控制读/写操作

-与存储器通信,提供就绪/繁忙信号和中断信号

-与所用配置的PC卡接口:PC存储卡、PC I/O卡和真正的IDE接口

从FSMC的角度看,可以把外部存储器划分为固定大小为256MB的4个存储块

·存储块1用于访问最多4个NOR闪存或者PSRAM存储设备。这个存储区被划分为4个NOR/PSRAM区,并有4个专用的片选。

·存储块2和3用于访问NAND闪存设备,每个存储块连接一个NAND闪存。

·存储块4用于访问PC卡设备

每一个存储块上的存储器类型是由用户在配置寄存器中定义的。

注意:FSMC只是提供了一个控制器,并不提供相应的存储设备,至于外设接的是什么设备,完全是由用户自己选择,只要能用于FSMC控制,就可以,像本次实验中,我们接的就是LCM。

3.本例中FSMC的使用

由于本例只是利用FSMC对LCM进行操作,因此不用完全懂得FSMC的所有功能,而是懂得一部分相应的操作即可。

1.FSMC包括哪几个部分

FSMC包含以下4个模块:

·AHB接口(包含FSMC配置寄存器)

·NOR闪存和PSRAM控制器

·NAND闪存和PC卡控制器

·外部设备接口

需要注意的是,FSMC可以请求AHB进行数据宽度操作。如果AHB操作的数据宽度大于外部设备(NOR或NAND或LCD)的宽度,此时FSMC将AHB操作分割成几个连续的较小的数据宽度,以适应外部设备的数据宽度。

2.FSMC对外部设备的地址映像

FSMC对外部设备的地址映像从0x6000 0000开始,到0x9FFF FFFF结束,一共4个地址块,每个地址块256MB,而每个地址块又分成4个分地址块,大小为64MB。对于NOR的地址映像来说,我们可以通过选择HADDR[27:26] 来确定当前使用的是哪个64M的分地址块。而这四个分存储块的片选,则使用NE[4:1]来选择。数据线/地址线/控制线是共享的。

这里的HADDR 是需要转换到外部设备的内部AHB地址线,每个地址对应一个字节单元。因此,若外部设备的地址宽度是8位的,则HADDR[25:0]与STM32的CPU引脚FSMC_A[25:0]一一对应,最大可以访问64M字节的空间。若外部设备的地

址宽度是16位的,则是HADDR[25:1]与STM32的CPU引脚FSMC_A[24:0]一一对应。在应用的时候,可以将FSMC_A总线连接到存储器或其他外设的地址总线引脚上。

4.ILI9325

由于我们使用的是奋斗STM32 V3开发板,其内部自带的是一个LCM,产品的编号是:QD024CPS25-36AV0,其中的详细规格参数可以参考QD024CPS25-36AV0规格书中的记载。而LCM中的驱动IC就是采用的ILI9325。

ILI9325的功能很多,在此无法一一说明,但是参考ILI9325的Datasheet我们发现有几个引脚还是非常重要的,而只要操作好了这几个引脚,基本上就可以实现简单的对LCM的控制了。

nCS: IC的片选信号。如果是低电平,则ILI9325是被选中,并且可以进行操作,如果是高电平,这不被选中。

RS: 寄存器选择信号。如果是低电平,则选择的是索引或者状态寄存器,如果是高电平,则选择控制寄存器。

nWR/SCL: 写使能信号,低电平有效。

nRD: 读使能信号,低电平有效。

以上内容是从ILI9325的Datasheet里面找到的,但是根据我的实际操作发现,似乎高电平也是有效的。而且,不管是高电平还是低电平,都可以成功驱动LCD,如果有了解情况的可以讨论一下。

ILI9325的寄存器非常多,详细的各个寄存器的功能请参考ILI9325的Datasheet。在对ILI9325进行操作时,应该先写地址,然后再写数据,设置好各个寄存器之后,ILI9325就可以开始工作了。

5.电路设计

1.信号线的连接

STM32F10x FSMC有4个不同的banks,每一个64MB,可支持NOR以及其他类似的存储器。这些外部设备的地址线、数据线和控制线是共享的。每个设备的访问时通过片选信号来决定的,而每次只能访问一个设备。我们的LCM就是连接在NOR的bank 上面。

FSMC_D[15:0]:16bit的数据总线,连接ILI9325的数据线;

FSMC_NEx:分配给NOR的256MB的地址空间还可以分为4个banks,每一个区用来分配一个外设,这4个外设分别就是NE1-NE4;FSMC_NOE:输出使能,连接ILI9325的nRD引脚;

FSMC_NWE:写使能,连接ILI9325的nWR引脚;

FSMC_Ax:用在LCD显示RAM和寄存器之间进行选择的地址线,这个和ILI9325的RS引脚相连。该线可用任意一根地址线,范围是FSMC_A[25:0]。当RS=0时,表示读写寄存器,RS=1时,表示读写数据RAM。

其实关于RS的表述也并不完全准确,应该这么理解,RS=0的时候,向这个地址写的数表示了选择什么寄存器进行操作,然而要对寄存器进行什么操作,则要看当RS=1时,送入的数据了。

关于地址的计算,如果我们选择NOR的第一个存储区,并且使用FSMC_A16来控制ILI9325的RS引脚,则如果要访问寄存器地址(RS=0),那么地址是0x6000 0000(起始地址),如果要访问数据区(RS=1),那么基地址应该是0x6002 0000。

有人会问,为什么不是0x6001 0000呢?因为FSMC_A16=1。因为在前文中已经说过,若外部设备的地址宽度是16位的,则是HADDR[25:1]与STM32的CPU引脚FSMC_A[24:0]一一对应。也就是说,内部产生的地址应该要左移一位,FSMC_A16=1,

代表着第17位为1,而不是第16位为1。如果外部设备的地址宽度是8位的话,则不会出现这个问题。

再举一个例子,如果选择NOR的第4个存储区,使用FSMC_A0来控制RS引脚,则访问数据区的地址为0x6000 0002,访问LCD寄存器的地址为:0x6000 0000。

2.时序问题

一般使用模式2来做LCD的接口控制,不使用外扩模式。并且读写操作的时序一样。此种情况下,我们需要使用3个参数:ADDSET、DATAST、ADDHOLD。时序的计算需要根据NOR闪存存储器的特性和STM32F10x的时钟HCLK来计算这些参数。

写或读访问时序是存储器片选信号的下降沿与上升沿之间的时间,这个时间可以由FSMC时序参数的函数计算得到:

写/读访问时间= ((ADDSET + 1) + (DATAST + 1)) ×HCLK

在写操作中,DATAST用于衡量写信号的下降沿与上升沿之间的时间参数:

写使能信号从低变高的时间= t WP = DATAST ×HCLK

为了得到正确的FSMC时序配置,下列时序应予以考虑:

最大的读/写访问时间、不同的FSMC内部延迟、不同的存储器内部延迟

因此得到:

((ADDSET + 1) + (DATAST + 1)) × HCLK = max (t WC , t RC )

DATAST × HCLK = tWP

DATAST必须满足:

DATAST = (tAVQV+ tsu(Data_NE) + tv(A_NE) )/HCLK – ADDSET – 4 由于我没有找到ILI9325的这些时序的参数,所以就参考了一些以前别人写的程序里面的时序配置:当HCLK 的频率是72MHZ,使用模式B,则有如下时序:

地址建立时间:0x1

地址保持时间:0x0

数据建立时间:0x5

6.程序编写步骤

对于程序的编写,一般步骤是:

1.初始化RCC;

2.初始化GPIO;

3.初始化FSMC;

4.初始化LCD;

5.往GRAM里面写入显示数据。

其中RCC、GPIO、FSMC的初始化函数在STM32的固件库中已经有相应的函数,在此就不一一赘述了,如果有不懂的,可以参考以前我写的学习笔记。FSMC的初始化参数很多,而且基本上可以通用,因此在此也不对每一个参数具体有什么用进行解释了,一般来说,用通用参数就足够普通的开发了。

而对LCD的初始化,则需要自己编写相应的代码。基本原则是,首先向寄存器地址写入需要操作的寄存器地址(代码),然后再根据Datasheet,向数据区地址写入相应的数据,以实现某些操作。具体的操作在ILI9325的Datasheet 第8节Register Descriptions中,有详细的解释。而LCD的初始化只要按照Datasheet里面的,把每一个寄存器都给配置好了,就没有问题了。而这些寄存器的配置,大部分都是通用的,只是有一些屏幕方向选择,坐标系等会略有差别。

LCD配置好之后,就可以往GRAM里面写入图像数据了,在这里推荐一个软件“Image2LCD”,这个软件能读取图像,然后生成C代码的数据,只要将这些生成的代码直接写入GRAM中,就可以显示出图像了。不过要记住,在图像转换的时候,输出数据类型选择“C语言数组”,扫描模式选择“水平扫描”,输出灰度“16位真彩色”,最大宽度和高度“320”“240”勾选“高位在前(MSB First)”。这些配置都是和ILI9325的寄存器配置相对应的,如果说ILI9325的配置和本文中的不一样,则需要相应的选择其他的选项。

7. 程序源代码

main.c文件中的代码:

#include "stm32f10x_lib.h"

#include "stm32f10x_lcd.h"

extern unsigned char LCD_Image_BIT[];

extern unsigned char LCD_Image_HIT[];

void RCC_cfg();

void FSMC_cfg();

void LCD_cfg();

void GPIO_cfg();

void LCD_Show(unsigned char * LCD_Image);

int main()

{

RCC_cfg();

GPIO_cfg();

FSMC_cfg();

LCD_cfg();

while(1)

{

LCD_Show(LCD_Image_HIT);

Delay(100000000);

LCD_Show(LCD_Image_BIT);

Delay(100000000);

}

}

//RCC时钟配置

void RCC_cfg()

{

//定义错误状态变量

ErrorStatus HSEStartUpStatus;

//将RCC寄存器重新设置为默认值

RCC_DeInit();

//打开外部高速时钟晶振

RCC_HSEConfig(RCC_HSE_ON);

//等待外部高速时钟晶振工作

HSEStartUpStatus = RCC_WaitForHSEStartUp();

if(HSEStartUpStatus == SUCCESS)

{

//设置AHB时钟(HCLK)为系统时钟

RCC_HCLKConfig(RCC_SYSCLK_Div1);

//设置高速AHB时钟(APB2)为HCLK时钟

RCC_PCLK2Config(RCC_HCLK_Div1);

//设置低速AHB时钟(APB1)为HCLK的2分频

RCC_PCLK1Config(RCC_HCLK_Div2);

//设置FLASH代码延时

FLASH_SetLatency(FLASH_Latency_2);

//使能预取指缓存

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

//设置PLL时钟,为HSE的9倍频8MHz * 9 = 72MHz

RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);

//使能PLL

RCC_PLLCmd(ENABLE);

//等待PLL准备就绪

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

//设置PLL为系统时钟源

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

//判断PLL是否是系统时钟

while(RCC_GetSYSCLKSource() != 0x08);

}

//打开GPIO时钟,复用功能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);

//打开FSMC时钟

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);

}

//FSMC配置

void FSMC_cfg()

{

FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;

FSMC_NORSRAMTimingInitTypeDef p;

//设置地址建立时间

p.FSMC_AddressSetupTime = 0x02;

//设置地址保持时间

p.FSMC_AddressHoldTime = 0x00;

//设置数据建立时间

p.FSMC_DataSetupTime = 0x05;

//总线返转时间

p.FSMC_BusTurnAroundDuration = 0x00;

//时钟分频

p.FSMC_CLKDivision = 0x00;

//数据保持时间

p.FSMC_DataLatency = 0x00;

//设置FSMC访问模式

p.FSMC_AccessMode = FSMC_AccessMode_B;

//选择设置的BANK以及片选信号(BANK1中的第一个block)

FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;

//设置是否数据地址总线时分复用(No)

FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;

//设置存储器类型(NOR)

FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR;

//设置数据宽度(16bit)

FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;

//设置是否使用迸发访问模式(连续读写模式)(No)

FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;

//设置WAIT信号的有效电平(低电平有效)

FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;

//设置是否使用还回模式(No)

FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;

//设置WAIT信号有效时机(在wait状态之前)

FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;

//设置是否使能写操作(Yes)

FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;

//设置是否使用WAIT信号(No)

FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;

//设置是否使用扩展模式(读写时序相互独立)(No)

FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;

//设置是否使用异步等待信号(No)

FSMC_NORSRAMInitStructure.FSMC_AsyncWait = FSMC_AsyncWait_Disable;

//设置是否使用迸发写模式(No)

FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;

//设定读写时序

FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;

//设定写时序

FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;

FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);

//使能Bank1中的block1

FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);

}

//GPIO配置

void GPIO_cfg()

{

GPIO_InitTypeDef GPIO_InitStructure;

//背光控制

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOD, &GPIO_InitStructure);

//LCD复位

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;

GPIO_Init(GPIOE, &GPIO_InitStructure);

//打开FSMC的数据端口D[15:0]

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(GPIOD, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_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_Init(GPIOE, &GPIO_InitStructure);

//打开FSMC功能端口,PD.4=RD(nOE);PD.5=WR(nWE)

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;

GPIO_Init(GPIOD, &GPIO_InitStructure);

//打开NE1设置

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

GPIO_Init(GPIOD, &GPIO_InitStructure);

//打开RS设置

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 ;

GPIO_Init(GPIOD, &GPIO_InitStructure);

//NE1=1

GPIO_SetBits(GPIOD, GPIO_Pin_7);

//LCD_RESET=0

GPIO_ResetBits(GPIOE, GPIO_Pin_1);

//LCD_RD=1(nOE)

GPIO_SetBits(GPIOD, GPIO_Pin_4);

//LCD_WR=1(nWE)

GPIO_SetBits(GPIOD, GPIO_Pin_5);

//背光LIGHT=1

GPIO_SetBits(GPIOD, GPIO_Pin_13);

}

//LCD初始化

void LCD_cfg()

{

//复位LCD

LCD_rst();

//LCD初始化

LCD_Init();

}

//LCD显示图片

//根据LCD_Init里面的配置,LCD的原点在左下角,终点在右上角;先纵向增长,再横向增长

void LCD_Show(unsigned char * LCD_Image)

{

u32 n = 0;

u16 temp = 0;

//设置进入模式

//AM=1:地址在水平写入方向上更新

//I/D[1:0]=01:水平方向递增,垂直方向递减

//BGR=1:RGB数据转换为BGR数据

//TRI=0;DFM=0;

//详细信息参考ILI9325 8.2.5 Entry Mode

LCD_WR_CMD(0x0003, 0x1018);

//GRAM的水平地址

//8.2.18 GRAM Horizontal/Vertical Address Set

LCD_WR_CMD(0x0020, 0x0000);

//GRAM的垂直地址

LCD_WR_CMD(0x0021, 0x013F);

//水平方向开始地址

LCD_WR_CMD(0x0050, 0x0000);

//水平方向结束地址(0-239)

LCD_WR_CMD(0x0051, 0x00EF);

//垂直方向开始地址

LCD_WR_CMD(0x0052, 0x0000);

//垂直方向结束地址(0-319)

LCD_WR_CMD(0x0053, 0x013F);

//写数据地址

//因为是16bits一起写入,而图像数据数组中是每个数据8bits,

//所以是2个8bits的数据合并成一个16bits的数据,再写入GRAM LCD_WR_ADD(0x0022);

while(n<153600)

{

temp = (u16)(LCD_Image[n]<<8) + LCD_Image[n+1];

LCD_WR_DATA(temp);

n += 2;

}

}

stm32f10x_lcd.c中的代码

#include "stm32f10x_lcd.h"

//数据区地址

#define Bank1_LCD_Data ((u32)0x60020000)

//寄存器区地址

#define Bank1_LCD_Reg ((u32)0x60000000)

u32 color1 = 0;

//延时函数

void Delay(u32 nCount)

{

for(; nCount != 0; nCount--);

}

//LCD复位

void LCD_rst()

{

//PE.1连接LCD的reset引脚

GPIO_ResetBits(GPIOE, GPIO_Pin_1);

Delay(0xAFFFFf);

GPIO_SetBits(GPIOE, GPIO_Pin_1 );

Delay(0xAFFFFf);

}

//LCD写寄存器地址函数

void LCD_WR_ADD(u16 index)

{

*(vu16 *)(Bank1_LCD_Reg) = index;

}

//LCD写数据函数

void LCD_WR_DATA(u16 val)

{

*(vu16 *)(Bank1_LCD_Data) = val;

}

//LCD写寄存器命令函数,先将命令地址写到Reg中,然后再将命令的数值写到Data中//具体地址和配置参照ILI9325的Datasheet

void LCD_WR_CMD(u16 index, u16 val)

{

*(vu16 *)(Bank1_LCD_Reg) = index;

*(vu16 *)(Bank1_LCD_Data) = val;

}

void LCD_Init()

{

//设置内部时钟

LCD_WR_CMD(0x00E3, 0x3008);

LCD_WR_CMD(0x00E7, 0x0012);

LCD_WR_CMD(0x00EF, 0x1231);

//启动振荡,ILI9325可以不要这一句

LCD_WR_CMD(0x0000, 0x0001);

//设置驱动器输出控制,SS=1,SM=0

//当SS=0时,源输出信号从S1开始至S720结束;

//当SS=1时,源输出信号从S720开始至S1结束。

//SM和GS搭配使用,具体查看ILI9325 8.2.3 Driver Output Contorl

LCD_WR_CMD(0x0001, 0x0100);

//LCD波形控制

//B/C=1:行反转;

//EOR=1和B/C=1:设置行反转

//8.2.4 LCD Driving Wave Control

LCD_WR_CMD(0x0002, 0x0700);

//设置进入模式

//AM=1:地址在水平写入方向上更新

//I/D[1:0]=01:水平方向递增,垂直方向递减

//BGR=1:RGB数据转换为BGR数据

//TRI=0;DFM=0;

//详细信息参考ILI9325 8.2.5 Entry Mode LCD_WR_CMD(0x0003, 0x1018);

//重新调整控制寄存器大小

//8.2.6 Resizing Control Register

LCD_WR_CMD(0x0004, 0x0000);

//显示器控制2

//FP[3:0]=0010;

//BP[3:0]=0010;

//详细信息参考ILI9325 8.2.8 Display Control 2 LCD_WR_CMD(0x0008, 0x0202);

//显示器控制3

//设置非显示区域刷新

//8.2.9 Display Control 3

LCD_WR_CMD(0x0009, 0x0000);

//显示器控制4

//FMARK信号设置

//8.2.10 Display Control 4

LCD_WR_CMD(0x000A, 0x0000);

//RGB显示接口控制1

//8.2.11 RGB Display Interface Control 1

LCD_WR_CMD(0x000C, 0x0000);

//帧标记位置

//8.2.12 Frame Marker Position

LCD_WR_CMD(0x000D, 0x0000);

//RGB显示接口控制2

//8.2.13 RGB Display Interface Control 2

LCD_WR_CMD(0x000F, 0x0000);

//功率控制1

//8.2.14 Power Control 1

LCD_WR_CMD(0x0010, 0x0000);

//功率控制2

//8.2.15 Power Control 2

//VC[2:0]=111:参考电压为Vci

LCD_WR_CMD(0x0011, 0x0007);

//功率控制3

//8.2.16 Power Control 3

LCD_WR_CMD(0x0012, 0x0000);

//功率控制4

//8.2.17 Power Control 4

LCD_WR_CMD(0x0013, 0x0000);

//延时,放电

Delay(200);

//功率控制1

//SAP=1:源驱动程序被启动

//BT[2:0]=110:

//APE=1:开始供应电力

//AP[2:0]=001:伽马驱动放大和源驱动放大

LCD_WR_CMD(0x0010, 0x1690);

//功率控制2

//DC1[2:0]=010:选择升压电路2工作频率Fosc/16 //DC0[2:0]=010:选择升压电路1工作频率Fosc/4 //VC[2:0]=111:参考电压为Vci

LCD_WR_CMD(0x0011, 0x0227);

//延时

Delay(50);

//功率控制3

//PON=1:控制线路3(VGL)开启

//VRH[3:0]=1100:设置外部参考电压

LCD_WR_CMD(0x0012, 0x001C);

//延时

Delay(50);

//功率控制4

//VDV[4:0]=11000:设置Vcom的电压振幅交替LCD_WR_CMD(0x0013, 0x1800);

//功率控制7

//8.2.21 Power Control 7

//VCM[5:0]=011100:设置内部VcomH电压

LCD_WR_CMD(0x0029, 0x001C);

//帧速率和色彩控制

//8.2.22 Frame Rate and Color Control

//FRS[3:0]:1101:帧率128

LCD_WR_CMD(0x002B, 0x000D);

//延时

Delay(50);

//GRAM的水平地址

//8.2.18 GRAM Horizontal/Vertical Address Set

LCD_WR_CMD(0x0020, 0x0000);

//GRAM的垂直地址

LCD_WR_CMD(0x0021, 0x0000);

//伽马控制

//8.2.23 Gamma Control

LCD_WR_CMD(0x0030, 0x0007);

LCD_WR_CMD(0x0031, 0x0302);

LCD_WR_CMD(0x0032, 0x0105);

LCD_WR_CMD(0x0035, 0x0206);

LCD_WR_CMD(0x0036, 0x0808);

LCD_WR_CMD(0x0037, 0x0206);

LCD_WR_CMD(0x0038, 0x0504);

LCD_WR_CMD(0x0039, 0x0007);

LCD_WR_CMD(0x003C, 0x0105);

LCD_WR_CMD(0x003D, 0x0808);

//水平和垂直位置的RAM地址

//8.2.24 Horizontal and Vertical RAM Address Position

//水平方向开始地址

LCD_WR_CMD(0x0050, 0x0000);

//水平方向结束地址(0-239)

LCD_WR_CMD(0x0051, 0x00EF);

//垂直方向开始地址

LCD_WR_CMD(0x0052, 0x0000);

//垂直方向结束地址(0-319)

LCD_WR_CMD(0x0053, 0x013F);

//门扫描控制

//8.2.25 Gate Scan Control

//GS=1:扫描方向是从G320到G1

//NL[5:0]=100111

LCD_WR_CMD(0x0060, 0xA700);

//NDL=0:在非显示区域设置源驱动器的输出极

//VLE=0:垂直滚动显示不可用

//REV=1:图像灰度反转

LCD_WR_CMD(0x0061, 0x0001);

//VL[8:0]=0

LCD_WR_CMD(0x006A, 0x0000);

//局部影像1显示位置

//8.2.26 Partial Image 1 Display Position

LCD_WR_CMD(0x0080, 0x0000);

//局部影像1RAM开始/结束地址

//8.2.27 Partial Image 1 RAM Start/End Address LCD_WR_CMD(0x0081, 0x0000);

LCD_WR_CMD(0x0082, 0x0000);

//局部影像2显示位置

//8.2.28. Partial Image 2 Display Position

LCD_WR_CMD(0x0083, 0x0000);

//局部影像2RAM开始/结束地址

//8.2.29 Partial Image 2 RAM Start/End Address LCD_WR_CMD(0x0084, 0x0000);

LCD_WR_CMD(0x0085, 0x0000);

//平板接口控制1

//8.2.30 Panel Interface Control 1

//RTNI[4:0]=10000:设置内部时钟运行模式中1线时钟的数目:16个

LCD_WR_CMD(0x0090, 0x0010);

//平板接口控制2

//8.2.31 Panel Interface Control 2

LCD_WR_CMD(0x0092, 0x0000);

LCD_WR_CMD(0x0093, 0x0003);

//平板接口控制4

//8.2.32 Panel Interface Control 4

LCD_WR_CMD(0x0095, 0x0110);

LCD_WR_CMD(0x0097, 0x0000);

LCD_WR_CMD(0x0098, 0x0000);

//显示控制1

//8.2.7 Display Control 1

//BASEE=1:显示基本图像

//GON=1 DTE=1:正常显示

//D[1:0]=11:打开显示面板

LCD_WR_CMD(0x0007, 0x0133);

//GRAM写入数据,用黑色清屏

LCD_WR_ADD(0x0022);

for(color1=0;color1<320*240;color1++)

{

LCD_WR_DATA(0x0000); //

}

color1=0;

}

stm32f10x_lcd.h中的代码

#include "stm32f10x_lib.h"

//LCD复位函数

void LCD_rst();

//LCD初始化函数

void LCD_Init();

//延时函数

void Delay(u32 nCount);

//LCD写寄存器地址函数

void LCD_WR_ADD(u16 index);

//LCD写数据函数

void LCD_WR_DATA(u16 val);

pic_bit.c中的代码和pic_hit.c中的代码由于太长了,所以并没有贴出来,其实就是根据某一

幅图片用Image2LCD生成的,其数组名分别叫const unsigned char LCD_Image_BIT[153600] const unsigned char LCD_Image_HIT[153600]

STM32 IIC 学习笔记总结

STM32系列IIC学习笔记经验总结一、各寄存器内容与组织:控制、地址匹配、数据、状态、时钟控制、上升沿控制

二、IIC协议及STM32的master实现 EVENT后的第一个符号表示事件发生后对应的标志位的状态,着重看7位地址的通信;

三、基础知识(主要讨论起主机模式,从机模式的配置与使用可类比) 1.默认工作在从机模式,产生起始信号后自动转为主机模式,产生终止信号或仲裁失权后自动转为从机模式;起止信号由主 机模式下的软件实现,地址也只能由主机发送,响应信号由接收器发出(软件实现),要注意区别主机、从机、发送机、接收机; 2.数据通信的直接通道,SDA LineShift RegisterDRMemory(数据寄存器与存储器直接的数据交换发生在DMA模式, 另外若从机在SDA接收到的是地址则直接会与地址寄存器比较,而不会送入数据寄存器) 3.主机产生时钟信号,一串数据总是以起始于start信号,终止于stop信号,一旦SDA线上产生start位信号,主机模 式便被选中;9个寄存器的功能分配简单明了:I2C_CR2主要配置时钟与模块中断及DMA使能位,I2C_CR1则主要产生Start等控制信号,I2C_SR2主要是MSL、TRA和BUSY标志位,I2C_SR1则是其他事件的标志位,接下来就是存储数据的I2C_DR,时钟设置的I2C_CC4R和I2C_TRISE,地址匹配的I2C_OAR1和I2C_OAR2; 4.主机模式必要操作序列:外围时钟输入最少2M(标准模式)、4M(快速模式) 1)配置I2C_CR2寄存器以产生正确时序; 2)配置时钟控制寄存器I2C_CCR; 3)配置上升时间寄存器I2C_TRISE; 4)配置I2C_CR1寄存器以使能接口电路; 5)配置I2C_CR1寄存器,置位START位以产生起始信号; 5.时序具体解析 1)Start信号,置位I2C_CR1的START位以产生起始信号(在总线空闲时,即I2C_SR2的BUSY清零),使转为主机模式(置位I2C_SR2的MSL);在主机模式下,置位START位会在当前字节传输完成后产生一个重启ReStart信号;一旦Start信号送出,I2C_SR1的SB位会由硬件置位并产生中断(前提是ITEVFEN位被置位,貌似文档有误,我认为应是IC2_SR2的ITEVTEN位),然后需要读SR1和写DR以清零SB(这也符合操作时序); 2)从机地址发送,7位模式下,地址字节一旦送出,I2C_SR1的ADDR位会由硬件置位并产生中断(前提是ITEVFEN 置位),然后主机等待读取SR1和SR2以清零ADDR(稍微符合,读SR2貌似饶了一步);7位模式下,地址字节最低位若是0则说明主机要进入发送模式,若是1则是接收模式;I2C_SR2的TRA表示主机在发送模式还是接收模式; 3)主机发送模式,地址送出且ADDR清零后,主机会将DR中数据发送到SDA line(当然经过Shift Register),主机会等到第一个数据写入DR(EV8_1阶段),若收到响应脉冲,SR1中的TxE位会置位(前提是ITEVFEN和ITBUFEN已置位);在最后一个字节传输结束前的传输过程中,若TxE置位且某数据字节没有写入DR,BTF会置位直到(硬件清零)该数据字节被写入到DR,这个过程中SCL会一直被拉低; 4)主机发送模式关闭通信,最后一个字节被写入DR,CR1的STOP位要由软件置位而产生停止信号,接口自动转为从机模式(MSL清零);置位Stop位即对应于EV8_2事件; 5)主机接收模式,地址送出且ADDR清零后,主机会进入接收模式,接口会从SDA line中读数据到DR中(同样经过Shift Register);每个字节接收后的操作序列为,产生应答信号(前提是CR1的ACK位置位),RxNE位置位并产生中断(前提是SR2的ITEVFEN和ITBUFEN置位);在最后一个字节传输结束前的传输过程中,若RxNE 置位且某数据未从DR中读取,BTF会置位直到(硬件清零)该数据字节被读出,这个过程SCL会一直被拉低; 6)主机接收模式关闭通信,收到最后一个字节后会发送NACK信号给从机,从机收到NACK会释放总线(SDA和SCL),此时主机便可发送一个Stop或Restart信号;在读完倒数第二个字节后(RxNE中断后),要清零ACK 位以产生NACK应答,要置位STOP/START位以产生Stop/Restart信号;在单字节数据接收状况,NACK 要在ADDR清零前(EV6)设置,STOP信号要在ADDR清零后配置;Stop信号产生后,主机自动进入从机模式(SR2的MSL清零); 7)最后一字节数据接收的ACK响应前若RxNE清零(ACK清零与Stop请求)没有完成,则建议采取以下步骤以确保ACK位在最后一字节数据接收前被清零,STOP位在最后一字节数据接受完后(没有附加数据)被置位: (1)2字节的数据接收:等到ADDR=1;清零ACK,置位POS;清零ADDR;等到BTF=1(数据1在DR,

STM32学习笔记

STM32学习笔记整理 端口复用配置过程 引脚具体可以复用为啥功能,参考芯片手册STM32F103ZET6.Pdf 具体每个引脚配置成什么模式,参考STM32中文参考手册,第八章,通用IO和复用。NVIC中断

假定设置中断优先级组为2,然后设置 中断3(RTC中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。 那么这3个中断的优先级顺序为:中断7>中断3>中断6 特别说明: 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。 首先,系统运行后先设置中断优先级分组。调用函数: void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); 整个系统执行过程中,只设置一次中断分组。 然后,中断初始化函数 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器 结构体内容NVIC_InitTypeDef typedef struct {

STM32学习笔记

输入模式初始化GPIOE2,3,4 ①IO口初始化:GPIO_InitTypeDef GPIO_InitStructure; ②使能PORTA,PORTE时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE); ③PE.2.3.4端口配置:GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; ④设置成(上拉)输入:GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; ⑤GPIO_Init(GPIOE, &GPIO_InitStructure); 输出模式初始化 ①IO口初始化:GPIO_InitTypeDef GPIO_InitStructure; ②使能PB,PE端口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); ③3LED0-->PB.5 端口配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; ④设置(推挽)输出模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; ⑤设置IO口速度为50MHz GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ⑥说明初始化哪个端口GPIO_Init(GPIOB, &GPIO_InitStructure); 在LED灯试验中初始为高电平灭GPIO_SetBits(GPIOB,GPIO_Pin_5); 再初始化相同发输出模式时③④⑤可省略例如(经实验初始化恰好为不同IO口相同IO序号③可省略,应该不规范吧) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 端口配置, 推挽输出GPIO_Init(GPIOE, &GPIO_InitStructure); //推挽输出,IO口速度为50MHz GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高 1,头文件可以定义所用的函数列表,方便查阅你可以调用的函数; 2,头文件可以定义很多宏定义,就是一些全局静态变量的定义,在这样的情况下,只要修改头文件的内容,程序就可以做相应的修改,不用亲自跑到繁琐的代码内去搜索。 3,头文件只是声明,不占内存空间,要知道其执行过程,要看你头文件所申明的函数是在哪个.c文件里定义的,才知道。 4,他并不是C自带的,可以不用。 5,调用了头文件,就等于赋予了调用某些函数的权限,如果你要算一个数的N次方,就要调用Pow()函数,而这个函数是定义在math.c里面的,要用这个函数,就必需调用math.h 这个头文件。

stm32的GPIO学习笔记讲课教案

s t m32的G P I O学习 笔记

I/O口工作模式: 1.高阻输入 输入模式的结构比较简单,就是一个带有施密特触发输入(Schmitt-triggered input)的三态缓冲器(U1),并具有很高的阻抗。施密特触发输入的作用是能将缓慢变化的或者是畸变的输入脉冲信号整形成比较理想的矩形脉冲信号。 执行 GPIO管脚读操作时,在读脉冲(Read Pulse)的作用下会把管脚(Pin)的当前电平状态读到内部总线上(Internal Bus)。 2.推挽输出 推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高. 在推挽输出模式下,GPIO还具有回读功能,实现回读功能的是一个简单的三态门 U2。注意:执行回读功能时,读到的是管脚的输出锁存状态,而不是外部管脚 Pin的状态。 3.开漏输出 开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平,比如加上上拉电阻就可以提供TTL/CMOS电平输出等。

开漏输出和推挽输出相比结构基本相同,但只有下拉晶体管 T1而没有上拉晶体管。同样,T1实际上也是多组可编程选择的晶体管。开漏输出的实际作用就是一个开关,输出“1”时断开、输出“0”时连接到 GND(有一定内阻) 开漏输出和推挽输出相比结构基本相同,但只有下拉晶体管 T1而没有上拉晶体管。同样,T1实际上也是多组可编程选择的晶体管。开漏输出的实际作用就是一个开关,输出“1”时断开、输出“0”时连接到 GND(有一定内阻). 4.钳位二级管 其作用是防止从外部管脚 Pin输入的电压过高或者过低。 提高输出电压一种简单的做法:是先在 GPIO管脚上串联一只二极管(如 1N4148),然后再接上拉电阻。 ///////////////////////////////////////////////////////////////////////////////////////////////////////// STM32的GPIO管脚深入分析: 概述:STM23的每个GPIO引脚都可以由软件配置成输出(推挽或开漏),输入(带或不带上拉或下拉)或复用的外设功能端口。多数GPIO引脚与数字或模拟的复用外设共用;除了具有模拟输入(ADC)功能的管脚之外,其他的GPIO引脚都有大电流通过能力。 tip:每个IO口可以自由编程,单IO口寄存器必须要按32位bit被访问。 STM32的每个IO端口都有7个寄存器来控制 一.具体如下8种模式:

STM32各模块学习笔记

STM32 中断优先级和开关总中断 一,中断优先级: STM32(Cortex-M3) 中的优先级概念 STM32(Cortex-M3) 中有两个优先级的概念 —— 抢占式优先级和响应优先级,有人把响应优 先级称作 '亚优先级 '或 '副优先级 ',每个中断源都需要被指定这两种优先级。 具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应, 即中断嵌 套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。 当两个中断源的抢占式优先级相同时, 这两个中断将没有嵌套关系, 当一个中断到来后, 如 果正在处理另一个中断, 这个后到来的中断就要等到前一个中断处理完之后才能被处理。 如 果这两个中断同时到达, 则中断控制器根据他们的响应优先级高低来决定先处理哪一个; 如 果他们的抢占式优先级和响应优先级都相等, 则根据他们在中断表中的排位顺序决定先处理 哪一个。 既然每个中断源都需要被指定这两种优先级, 就需要有相应的寄存器位记录每个中断的优先 级;在 Cortex-M3 中定义了 8 个比特位用于设置中断源的优先级,这 8 个比特位可以有 8 种分配方式,如下: 这就是优先级分组的概念。 Cortex-M3 允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此 STM32 把指定中断优先级的寄存器位减少到 4 位,这 4个寄存器位的分组方式如下: 第 0 组:所有 4 位用于指定响应优先级 第 1 组:最高 1 位用于指定抢占式优先级,最低 第 2 组:最高 2 位用于指定抢占式优先级,最低 第 3 组:最高 3 位用于指定抢占式优先级,最低 第 4 组:所有 4 位用于指定抢占式优先级 所有 8 位用于指定响应优先级 最 高 1 位用于指定抢占式优先级, 最高 2 位用于指定抢占式优先级, 最高 3 位用于指定抢占式优先级, 最高 4 位用于指定抢占式优先级, 最高 5 位用于指定抢占式优先级, 最高 6 位用于指定抢占式优先级, 最高 7 位用于指定最低 7 位用于指定响应优先级 最低 6 位用于指定响应优先级 最低 5 位用于指定响应优先级 最低 4 位用于指定响应优先级 最低 3 位用于指定响应优先级 最低 2 位用于指定响应优先级 最低 1 位用于指定响应优先级 3 位用于指定响应优先 级 2 位用于指定响应优先 级

stm32pwm输入捕捉模式学习笔记

stm32 pwm输入捕捉模式学习笔记 (本文来自:android_chunhui的博客) PWM输入是输入捕获的一个特殊应用,输入捕获就是当连接到定时器的引脚上产生电平变化时对应的捕获装置会立即将当前计数值复制到另一个寄存器中。你可以开启捕获中断然后在中断处理函数中读出保存的计数值。主要用于读取pwm的频率和占空比。 与输入捕获不同的是PWM输入模式时,用到两个通道(一般用TIMx_CH1或TIMx_CH2),只给其中一个通道分配gpio时钟即可,另一个在内部使用。给一个通道分配gpio时钟后,需要设置另一个为从机且复位模式。(例如使用ch2,ch1就得设置成从机模式)。当一个输入信号(TI1或TI2)来临时,主通道捕获上升沿,从机捕获下降沿。 假设pwm从低电平开始触发,当上升沿来临时,两个通道TIM_CNT均复位开始计数,下一个下降沿来临,从机读取TIM_CNT中的值,记为CCR1,下一个上升沿来临,主通道读取TIM_CNT的值,记为CCR2。所以CCR2/f,为pwm周期,倒数即频率。CCR1/CCR2就是占空比。 下面是pwm捕获模式下的配置: void Tim2_PWMIC_Init(void) { TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //***通道选择,通道一为从机TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿触发 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //管脚与寄存器对应关系TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //输入预分频。意思是控制在多少个输入周期做一次捕获,如果 //输入的信号频率没有变,测得的周期也不会变。比如选择4分频,则每四个输入周期才做一次捕获,这样在输入信号变化不频繁的情况下, //可以减少软件被不断中断的次数。 TIM_ICInitStructure.TIM_ICFilter = 0x0; //滤波设置,经历几个周期跳变认定波形稳定0x0~0xF TIM_PWMIConfig(TIM2, &TIM_ICInitStructure); //根据参数配置TIM外设信息TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //选择IC2为始终触发源 TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);//TIM从模式:触发信号的上升沿重新初始化计数器和触发寄存器的更新事件 TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //启动定时器的被动触发 TIM_Cmd(TIM2,ENABLE); //启动TIM2 TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE); //打开中断 } //中断服务函数

STM32学习笔记之二_中断

STM32中中断的理解 一、什么是中断 中断是指在计算机执行程序的过程中,当出现异常情况或者特殊请求时,计算机停止现行的程序的运行,转而对这些异常处理或者特殊请求的处理,处理结束后再返回到现行程序的中断处,继续执行原程序。 中断处理过程: (1)保护被中断进程现场。为了在中断处理结束后能够使进程准确地返回到中断点,系统必须保存当前处理机程序状态字PSW和程序计数器PC等的值。 (2)分析中断原因,转去执行相应的中断处理程序。在多个中断请求同时发生时,处理优先级最高的中断源发出的中断请求。 (3)恢复被中断进程的现场,CPU继续执行原来被中断的进程。 二、什么是中断服务程序 处理中断事件的程序被称为中断服务程序。 三、什么是中断向量 中断向量就是中断服务程序的入口地址。 四、什么是中断向量号 中断号也叫中断类型号,或者中断请求号。 中断是指在CPU运行期间,被CPU内部或外部事件所打断、暂停当前程序的执行而转去执行一段特定的处理内部或外部时间程序的过程。外部设备进行I/O操作时,会随机产生中断请求信号。这个信号中会有特定的标志,使计算机能够判断是哪个设备提出中断请求,这个信号就叫做中断号。 五、什么是中断向量地址 中断向量地址就是内存中存放中断服务程序入口地址的地址。 六、什么是中断向量表 CPU是根据中断向量号获取中断向量值,即对应中断服务程序的入口地址值。因此为了让CPU由中断向量号查找到对应的中断向量,就需要在内存中建立一张查询表,即中断向量表。 七、STM32中中断发生时系统找到对应中断服务执行的过程 (1)根据中断设发生备确定对应的中断向量号。

(3)执行中断服务程序。 以ALIENTEK Mini STM32开发板范例代码中的定时器中断实验为例来说明。 (1)根据中断设发生备确定对应的中断向量号。 在main.c中: TIM3_Int_Init(4999,7199); 在timer.c中: void TIM3_Int_Init(u16 arr,u16 psc) { . . . NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断号 . . . } 在stm32f10x.h中: typedef enum IRQn { . . . TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ . . . } 根据以上三个文件可以确定,定时器TIM3对应的中断向量号为TIM3_IRQn,而TIM3_IRQn = 29,所以,定时器TIM3对应的中断向量号为29。

详细的STM32单片机学习笔记

详细的STM32单片机学习笔记 STM32单片机学习笔记 1、AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中21,意思是APB2接高速设备 2、Stm32f10x.h相当于reg52.h(里面有基本的位操作定义),另一个为stm32f10x_conf.h 专门控制外围器件的配置,也就是开关头文件的作用 3、HSE Osc(High Speed External Oscillator)高速外部晶振,一般为8MHz,HSI RC(High Speed InternalRC)高速内部RC,8MHz 4、LSE Osc(Low Speed External Oscillator)低速外部晶振,一般为32.768KHz,LSI RC (Low Speed InternalRC)低速内部晶振,大概为40KHz左右,提供看门狗时钟和自动唤醒单元时钟源 5、SYSCLK时钟源有三个来源:HSI RC、HSE OSC、PLL 6、MCO[2:0]可以提供4源不同的时钟同步信号,PA8 7、GPIO口貌似有两个反向串联的二极管用作钳位二极管。 8、总线矩阵采用轮换算法对系统总线和DMA进行仲裁 9、ICode总线,DCode总线、系统总线、DMA总线、总线矩阵、AHB/APB桥 10、在使用一个外设之前,必须设置寄存器RCC_AHBENR来打开该外设的时钟 11、数据字节以小端存储形式保存在存储器中 12、内存映射区分为8个大块,每个块为512MB 13、FLASH的一页为1K(小容量和中容量),大容量是2K。 14、系统存储区(SystemMemory)为ST公司出厂配置锁死,用户无法编辑,用于对FLASH 区域进行重新编程。所以我们烧写程序务必选择BOOT1 = 0,这样通过内嵌的自举程序对

STM32学习心得笔记

STM32学习心得笔记 时钟篇 在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。 ①、HSI是高速内部时钟,RC振荡器,频率为8MHz。 ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz~16MHz。 ③、LSI是低速内部时钟,RC振荡器,频率为40kHz。 ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。 ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍, 但是其输出频率最大不得超过72MHz。 其中40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。另外, 实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。RTC的时钟源通过RTCSEL[1:0]来选择。 STM32中有一个全速功能的USB 模块,其串行接口引擎需要一个频率为48MHz的时

钟源。该时钟源只能 从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL 必须使能, 并且时钟频率配置为48MHz或72MHz。 另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。 系统时钟SYSCLK,它是供STM32中绝大部分部件工作的时钟源。系统时钟可选择为PLL 输出、HSI或者HSE。系统时钟最 大频率为72MHz,它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分 频。其中AHB分频器输出的时钟送给5大模块使用: ①、送给AHB 总线、内核、内存和DMA使用的HCLK时钟。 ②、通过8分频后送给Cortex的系统定时器时钟。 ③、直接送给Cortex的空闲运行时钟FCLK。 ④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz), 另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。

STM32自学笔记

一、原子位操作: 原子位操作定义在文件中。令人感到奇怪的是位操作函数是对普通的内存地址进行操作的。原子位操作在多数情况下是对一个字长的内存访问,因而位号该位于0-31之间(在64位机器上是0-63之间),但是对位号的范围没有限制。 原子操作中的位操作部分函数如下: void set_bit(int nr, void *addr)原子设置addr所指的第nr位 void clear_bit(int nr, void *addr)原子的清空所指对象的第nr位 void change_bit(nr, void *addr)原子的翻转addr所指的第nr位int test_bit(nr, void *addr)原子的返回addr位所指对象nr位int test_and_set_bit(nr, void *addr)原子设置addr所指对象的第nr位,并返回原先的值 int test_and_clear_bit(nr, void *addr)原子清空addr所指对象的第nr位,并返回原先的值 int test_and_change_bit(nr, void *addr)原子翻转addr所指对象的第nr位,并返回原先的值 unsigned long word = 0; set_bit(0, &word); /*第0位被设置*/ set_bit(1, &word); /*第1位被设置*/ clear_bit(1, &word); /*第1位被清空*/ change_bit(0, &word); /*翻转第0位*/ 二、STM32的GPIO锁定: 三、中断挂起: 因为某种原因,中断不能马上执行,所以“挂起”等待。比如有高、低级别的中断同时发生,就挂起低级别中断,等高级别中断程序执行完,在执行低级别中断。四、固文件: 固件(Firmware)就是写入EROM(可擦写只读存储器)或EEPROM(电可擦可编程只读存储器)中的程序。 五、固件库:包含各个外设或者内核的驱动头文件和C文件。 六、TIx的输入捕获滤波器(消抖): 采样频率fSAMPLING,采样次数N,如果以采样频率对一脉冲进行采样时,如果在N个采样方波里该脉宽不变,则视为一次有效的脉冲,否则视为无效的脉冲。 七、高级定时器的PWM互补输出: 常用于X相电机驱动,其中的互补输出则防止电机的死区出现。

stm32学习笔记

MDK不会让你直接在入口参数处设置一个简单的字,因为这样代码可读性太差 MDK一般把取值范围的宏定义放在判断有效性语句的上方,这样是为了方便大家查找 可以通过|(或)的方式同时初始化多个IO口。这样操作的前提是,他们的Mode和Speed 参数相同,因为Mode和Speed参数并不能一次定义多种 在stm32f10x.h看那些外设是挂载在那个总线之下 GPIO操作步骤为: 1)使能IO口时钟。调用函数为RCC_APB2PeriphClockCmd()。 2)初始化IO参数。调用函数GPIO_Init(); 3)操作IO。 按键一端接高电平时另一端接I/O口则I/O口设置为下拉输入,因为按键按下时I/O口变成高电平;反之接低电平时I/O口设置为上拉输入因为按键按下时I/O口状态为低电平 Config意为配置 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);这个函数的作用是对中断的优 先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改 当接收到从电脑发过来的数据,把接收到的数据保存在USART_RX_BUF中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(回车的表示 由2个字节组成:0X0D和0X0A)的第一个字节0X0D时,计数器将不再增加,等待0X0A 的到来,而如果0X0A没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接 收到0X0A,则标记USART_RX_STA的第15位,这样完成一次接收,并等待该位被其他 程序清除,从而开始下一次的接收,而如果迟迟没有收到0X0D,那么在接收数据超过USART_REC_LEN的时候,则会丢弃前面的数据,重新接收。 USART初始化需要设置的参数为:波特率,字长,停止位,奇偶校验位,硬件数据流控制, 模式(收,发)。 ①串口时钟使能,GPIO时钟使能 ②串口复位 ③GPIO端口模式设置 ④串口参数初始化 ⑤初始化NVIC并且开启中断 ⑥使能串口 SysTick是系统滴答计时器 在EXTI_GetITStatus函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而

STM32学习笔记(7):USART串口的使用

1.串口的基本概念 在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,也支持LIN(局部互联网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。还可以使用DMA方式,实现高速数据通信。 USART通过3个引脚与其他设备连接在一起,任何USART双向通信至少需要2个引脚:接受数据输入(RX)和发送数据输出(TX)。 RX: 接受数据串行输入。通过过采样技术来区别数据和噪音,从而恢复数据。 TX: 发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。 2.串口的如何工作的 一般有两种方式:查询和中断。 (1)查询:串口程序不断地循环查询,看看当前有没有数据要它传送。如果有,就帮助传送(可以从PC到STM32板子,也可以从STM32板子到PC)。 (2)中断:平时串口只要打开中断即可。如果发现有一个中断来,则意味着要它帮助传输数据——它就马上进行数据的传送。同样,可以从 PC到STM3板子,也可以从STM32板子到PC。 3.串口的硬件连接 我用的奋斗STM32 V3开发板拥有二路RS-232 接口,CPU 的PA9-US1-TX(P68)、PA10-US1-RX(P69)、PA9-US2-TX(P25)、PA10-US2-RX(P26)通过MAX3232 实现两路RS-232 接口,分别连接在XS5 和XS17 接口上。 USART1 在系统存储区启动模式下,将通过该口通过PC对板上的CPU进行ISP,该口也可作为普通串口功能使用,JP3,JP4 的短路冒拔去,将断开第二路的RS232通信,仅作为TTL 通信通道。 4.编程实例 我们要对串口进行操作,首先要将STM32的串口和CPU进行连接。在Windows操作系统中,有一个自带的系统软件叫“超级终端”。VISTA以上的操作系统去掉了这个软件,不过可以从XP的系统中,复制“hypertrm.dll”和“hypertrm.exe”到“windows/system32”文件夹下,然后双击运行hypertrm.exe,就可以看见超级终端的运行界面了。 运行超级终端以后,会弹出“连接描述”,输入名称和选择图标,这个地方随便写个什么名称都可以。然后弹出“连接到”设置,在“连接时使用”选择你自己PC和STM32连

单片机STM32学习笔记

推挽输出与开漏输出的区别 推挽输出:可以输出高,低电平,连接数字器件; 开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内). 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止. 要实现“线与”需要用OC(open collector)门电路.是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。输出既可以向负载灌电流,也可以从负载抽取电流。 问题: 很多芯片的供电电压不一样,有3.3v和5.0v,需要把几种IC的不同口连接在一起,是不是直接连接就可以了?实际上系统是应用在I2C上面。 简答: 1、部分3.3V器件有5V兼容性,可以利用这种容性直接连接 2、应用电压转换器件,如TPS76733就是5V输入,转换成3.3V、1A输出。 开漏电路特点及应用 在电路设计时我们常常遇到开漏(open drain)和开集(open collector)的概念。所谓开漏电路概念中提到的“漏”就是指MOSFET的漏极。同理,开集电路中的“集”就是指三极管的集电极。开漏电路就是指以MOSFET的漏极为输出的电路。一般的用法是会在漏极外部的电路添加上拉电阻。完整的开漏电路应该由开漏器件和开漏上拉电阻组成。如图1所示:

组成开漏形式的电路有以下几个特点: 1. 利用外部电路的驱动能力,减少IC内部的驱动。当IC内部MOSFET导通时,驱动电流是从外部的VCC流经R pull-up ,MOSFET到GND。IC内部仅需很下的栅极驱动电流。如图1。 2. 可以将多个开漏输出的Pin,连接到一条线上。形成“与逻辑” 关系。如图1,当PIN_A、PIN_B、PIN_C任意一个变低后,开漏线上的逻辑就为0了。这也是I2C,SMBus等总线判断总线占用状态的原理。 3. 可以利用改变上拉电源的电压,改变传输电平。如图2, IC的逻辑电平由电源Vcc1决定,而输出高电平则由Vcc2决定。这样我们就可以用低电平逻辑控制输出高电平逻辑了。 4. 开漏Pin不连接外部的上拉电阻,则只能输出低电平(因此对于经典的51单片机的P0口而言,要想做输入输出功能必须加外部上拉电阻,否则无法输出高电平逻辑)。 5. 标准的开漏脚一般只有输出的能力。添加其它的判断电路,才能具备双向输入、输出的能力。

STM32-串口实验学习笔记

STM32-串口实验学习笔记 USART1_IRQHandler(void)函数:当串口1 发生了相应的中断,就会跳到改函数执行。这里设计了一个小小的接收协议(系统并未定义):通过这个函数,配合一个数组USART_RX_BUF[ ],一个接收状态寄存器USART_RX_STA 实现对串口的数据的接收管理。USART_RX_BUF[ ]最大值为64,也就是一次接收的数据最大不能超过64 字节。USART_RX_STA 是一个接收状态寄存器,其各位的定义如表所示: (注意:这个是作者设计的协议,怎样判断串口接收一组数据完毕?由于每次接收的数据长度不一样,少的就3 个8 位数据,多的时候有十多个,这个数据个数是不定的,且没规律的数据,有什么好的方法让它接收完整? 协议的设计思路如下: 当接收到从电脑发过来的数据,把接收到的数据保存在USART_RX_BUF 中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(0X0D,0X0A)的第一个字节0X0D 时,标志位即第六位置1,计数器将不再增加,等待0X0A 的到来,而如果0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接收到0X0A,则标记USART_RX_STA 的第七位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,而如果迟迟没有收到0X0D,那么在接收数据超过64 个了,则会丢弃前面的数据,重新接收。由于这个寄存器是作者定义,我们理解时可能容易迷糊,其实这个跟普通寄存器类似,只不过是作者定义了一个8 位数,规定了它每位的定义,前两位是两个个标志位,后六位用来计数,

STM32学习笔记(初学者快速入门

STM32学习笔记(初学者快速入门 STM32 学习笔记 从51 开始单片机玩了很长时间了有51PICAVR 等等早就想跟潮 流玩玩ARM 但一直没有开始原因-----不知道玩了ARM 可以做什么对我自 己而言如果为学习而学习肯定学不好然后cortex-m3 出来了据说这 东西可以替代单片机于是马上开始关注也在第一时间开始学习可惜一开始 就有点站错了队选错了型仍是对我自己而言我希望这种芯片应该是满大 街都是随便哪里都可以买得到但我选的第一种显然做不到为此大概浪费

了一年多时间吧现在回到对我来说是正确的道路上来啦边学边写点东西 这里写的是我的学习的过程显然很多时候会是不全面的不系统的感 悟式的甚至有时会是错误的有些做法会是不专业的那么为什么我还要写 呢这是一个有趣的问题它甚至涉及到博客为什么要存在的问题显然博客 里面的写的东西其正确性权威性大多没法和书比可为什么博客会存在呢 理由很多我非专家只说我的感慨 我们读武侠小说总会有一些创出独门功夫的宗师功夫极高然后他的弟 子则基本上无法超越他我在想这位宗师在创造他自己的独门功

夫时必然会 有很多的次的曲折弯路甚至失败会浪费他的很多时间而他教给弟子时 则已去掉了这些曲折和弯路当然更不会把失败教给弟子按理说效率应该更 高可是没用弟子大都不如师为什么呢也许知识本身并不是最重要的获 取知识的过程才是最重要的也许所谓的知识并不仅仅是一条条的结论而是 附带着很多说不清道不明的东西如植物的根一条主根上必带有大量的小小的 触须 闲话多了些就权当前言了下面准备开始 一条件的准备

我的习惯第一步是先搭建一个学习的平台原来学51PICAVR 时都 是想方设法自己做些工具实验板之类现在人懒了直接购买成品了 硬件电路板火牛板 软件有keil 和iar 可供选择网上的口水仗不少我选keil理由很简单 这个我熟目前要学的知识中软硬件我都不熟所以找一个我有点熟的东西 就很重要在我相当熟练之前肯定不会用到IAR如果真的有一天不得不用I AR 相信学起来也很容易因为这个时候硬件部分我肯定很熟了再加上有ke il 的基础所以应该很容易学会了

超详细的STM32单片机学习笔记汇总

超详细的STM32单片机学习笔记汇总 1、AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中2>1,意思是APB2接高速设备 2、Stm32f10x.h相当于reg52.h(里面有基本的位操作定义),另一个为stm32f10x_conf.h 专门控制外围器件的配置,也就是开关头文件的作用 3、HSE Osc(High Speed External Oscillator)高速外部晶振,一般为8MHz,HSI RC(High Speed InternalRC)高速内部RC,8MHz 4、LSE Osc(Low Speed External Oscillator)低速外部晶振,一般为32.768KHz,LSI RC (Low Speed InternalRC)低速内部晶振,大概为40KHz左右,提供看门狗时钟和自动唤醒单元时钟源 5、SYSCLK时钟源有三个来源:HSI RC、HSE OSC、PLL 6、MCO[2:0]可以提供4源不同的时钟同步信号,PA8 7、GPIO口貌似有两个反向串联的二极管用作钳位二极管。 8、总线矩阵采用轮换算法对系统总线和DMA进行仲裁 9、ICode总线,DCode总线、系统总线、DMA总线、总线矩阵、AHB/APB桥 10、在使用一个外设之前,必须设置寄存器RCC_AHBENR来打开该外设的时钟 11、数据字节以小端存储形式保存在存储器中 12、内存映射区分为8个大块,每个块为512MB 13、 FLASH的一页为1K(小容量和中容量),大容量是2K。 14、系统存储区(SystemMemory)为ST公司出厂配置锁死,用户无法编辑,用于对FLASH 区域进行重新编程。所以我们烧写程序务必选择BOOT1 = 0,这样通过内嵌的自举程序对FLASH进行烧写,比如中断向量表和代码 15、STM32核心电压为1.8V

STM32学习笔记(关于时钟)

STM32学习----时钟(转载) 在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。 ①、HSI是高速内部时钟,RC振荡器,频率为8MHz。 ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。 HSE/LSE时钟源 ③、LSI是低速内部时钟,RC振荡器,频率为40kHz。 ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。 ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。 其中40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。另外,实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。RTC的时钟源通过RTCSEL[1:0]来选择。 STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz 的时钟源。该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。 另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。 系统时钟SYSCLK,它是供STM32中绝大部分部件工作的时钟源。系统时钟可选择为PLL输出、HSI或者HSE。系统时钟最大频率为72MHz,它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用: ①、送给AHB总线、内核、内存和DMA使用的HCLK时钟。 ②、通过8分频后送给Cortex的系统定时器时钟。 ③、直接送给Cortex的空闲运行时钟FCLK。 ④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。

相关主题