TFT并口写时序图:
图中,当RS为0时,表示写入的是寄存器地址,RS为1的时候,表示写入的是数据(寄存器值/GRAM数据)
并口读时序图:
RS为0时,表示读取的是状态寄存器值,RS为1时,表示读取的是像素数据。
TFT并口写时序图:
图中,当RS为0时,表示写入的是寄存器地址,RS为1的时候,表示写入的是数据(寄存器值/GRAM数据)
并口读时序图:
RS为0时,表示读取的是状态寄存器值,RS为1时,表示读取的是像素数据。
在ARM7内核芯片中,在临界区不关闭快速中断,可以避免丢失重要的中断信号。STM32要实现类似功能,需要用到BASEPRI寄存器。
比如一个产品需要外接维根读头,STM32利用外部中断解码。如果在临界区关闭所有中断,那么在刷卡时,有可能丢掉部分外部中断信号,导致解码错误。
系统简化中断设置,只设置抢占式优先级,不要子优先级。抢占式优先级(0-15),子优先级都为0.
UCOS的SYSTICK中断及PENDSV中断设定在最低优先级
#define ConfigKERNEL_INTERRUPT_PRIORITY_PENSV (15)
#define ConfigKERNEL_INTERRUPT_PRIORITY_SYSTICK (14)
Pendsvde 的优先级在OS_CPU_A.S的OSStartHighRdy中设置,优先级最低为15.
Systick在core_cm3.h的systick_config()中设置,pendsv的优先级寄存器0Xe000ed22的值为0Xf0,抢占式优先级为15,亚优先级为0。Systick的优先级寄存器0XE000ED23的值为0Xe0,抢占式优先级为14,亚优先级为0.
维根读头的外部中断设置为0号抢占式优先级。
如果OS_ENTER_CRITIAL()函数,即OS_CPU_A.S中的OSCPUSAVESR如下
MRS R0,PRIMASK
CPSID I
BX LR
那么程序进入临界区后,所有外部中断都会关闭,包括维根读头使用的外部中断0,导致接收维根数据出错。这时应该使用BASEPRI寄存器。
BASEPRI寄存器最多有9位(由表达优先级的位数决定,STM32F103中,高4位有效),它定义了被屏蔽优先级的阈值。当它设定成某个值后,所有优先级大于等于此值的中断都被关(优先级号越大,优先级越低)。小于此值的中断不会被关闭。若设成0,则不关闭任何中断,0是缺省值。
此处要保证维根读头使用的中断优先级0不被关闭,所以要设置BASEPRI高4位为1.
MASK_INTERRUPT_PRIORITY EQU 0X01
OSCPUSAVESR
MRS R0,BASEPRI
MOVS R1,#(MASK_INTERRUPT_PRIORITY<< 4)
MSR BASEPRI,R1
BX LR
OSCPURESTORESR
MSR BASEPRI,R0
BX LR
实际测试,这样改写后,接收的维根读头数据每次都正确。
ARM芯片使用的是STM32F105系列,没有FSMC接口,所以采用模拟16bit驱动TFT。TFT屏驱动芯片是SPFD5420A。3.0’屏,400*240大小。
SPFD5420A支持262144color,也就是屏资料介绍里常说的262K色或26万色。262144是怎么算出来的?我们先来了解TFT的显示原理。400*240的屏有96000个像素,每个像素的颜色由RGB三种颜色混合而成。SPFD5420A支持18bit接口,一个像素由R(6)G(6)B(6)共18位组成。如下所示:
R5 |
R4 |
R3 |
R2 |
R1 |
R0 |
G5 |
G4 |
G3 |
G2 |
G1 |
G0 |
B5 |
B4 |
B3 |
B2 |
B1 |
B0 |
那么一个像素能显示的颜色有2^18,也就是262144种颜色。
65K色屏一个像素由R(5)G(6)B(5)共16位组成,2^16=65536种颜色。
16.7M色屏一个像素由R(8)G(8)B(8)共24位组成,2^24=16777216种颜色。
这个参数和屏的大小无关,只和屏驱动芯片支持的RGB组合的位数有关。
SPFD5420A内部有233280byteGRAM,最大的像素为240*432,每个像素18bit,240*432*18/8=233280byte。
SPFD5420A支持8,9,16,18位并口通讯。SPFD5420D有一个16bit索引寄存器IR,一般作为存储器的指针使用。2个18bit数据寄存器,一个是写数据寄存器(WDR),一个是读数据寄存器(RDR)。
TFT屏接口图如下:
选择16bit模式,TFT的DB0和DB9脚没有使用,DB1-DB8接STM32的GPIOE的PIN0-PIN7脚,DB10-DB17接GPIOE的PIN8-PIN15脚。引脚定义如下:
#define TFT_CS GPIO_Pin_0
#define TFT_RS GPIO_Pin_1
#define TFT_RST GPIO_Pin_4
#define TFT_RD GPIO_Pin_5
#define TFT_MISO GPIO_Pin_6
#define TFT_MOSI GPIO_Pin_7
//————-PORTB———————
#define TFT_PCLK GPIO_Pin_12
#define TFT_DE GPIO_Pin_13
#define TFT_WR GPIO_Pin_14
#define TFT_HSYNC GPIO_Pin_15
//————-PORTC———————
#define TFT_BACKLIGHT GPIO_Pin_9
//————-PORTE———————
#define TFT_DATAPORT GPIOE
#define Lcd_Light_ON GPIOC->BSRR = GPIO_Pin_9;
#define Lcd_Light_OFF GPIOC->BRR = GPIO_Pin_9;
#define Set_Cs GPIO_SetBits(GPIOA,GPIO_Pin_0);
#define Clr_Cs GPIO_ResetBits(GPIOA,GPIO_Pin_0);
#define Set_Rs GPIO_SetBits(GPIOA,GPIO_Pin_1);
#define Clr_Rs GPIO_ResetBits(GPIOA,GPIO_Pin_1);
#define Set_nWr GPIO_SetBits(GPIOB,GPIO_Pin_14);
#define Clr_nWr GPIO_ResetBits(GPIOB,GPIO_Pin_14);
#define Set_nRd GPIO_SetBits(GPIOA,GPIO_Pin_5);
#define Clr_nRd GPIO_ResetBits(GPIOA,GPIO_Pin_5);
IO口初始化函数:
static void LCD_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = TFT_CS|TFT_RS |TFT_WR|TFT_RST|TFT_RD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = TFT_WR;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = TFT_BACKLIGHT;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* tft control gpio init */
GPIO_SetBits(GPIOA, TFT_RST); // RST
GPIO_SetBits(GPIOA, TFT_RD); // RD = 1
GPIO_SetBits(GPIOB, TFT_WR); // WR = 1
GPIO_SetBits(GPIOA, TFT_RS); // RS
GPIO_SetBits(GPIOA, TFT_CS); //CS = 1
}
STM32驱动5V的12864LCD,控制脚和数据较最好接到STM32能容忍5v的IO口,外加上拉电阻时,控制脚和数据脚设置成开漏输出。不加上拉电阻时,控制脚设置为推拉输出,数据脚输出时,设置成推拉输出,输入时设置成上拉输入。加不加上拉电阻都能点亮LCD,关键是脚位配置。
GPIOE的PE0-PE7做数据线,控制线定义如下:
//————-PORTE———————
#define R_S GPIO_Pin_8
#define R_W GPIO_Pin_9
#define EN GPIO_Pin_10
#define _CS1 GPIO_Pin_11
#define _CS2 GPIO_Pin_12
#define _RST GPIO_Pin_13
首先IO口初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE, EN );
GPIO_SetBits(GPIOE,_CS1 | _CS2 );
注意_CS1,_CS2初始化是一定要拉高,否则检测忙信号时,忙信号会一直是高。我就是因为这个问题卡了好几天。
以下是LCD驱动程序:
//指令宏定义
#define Display_On 0x3f //显示开指令
#define Display_Off 0x3e //显示关指令
#define Y_Col_Addr 0x40 //定位到第0列指令(列起始地址)(0-63)
#define X_Page_Addr 0xb8 //定位到第0页指令(页起始地址)(0-7)
#define Start_Line 0xc0 //定位从DDROM中的第0行开始往屏幕上显示
定义大小在startup_stm32f10x_hd.s
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
2.堆和栈位置
通过MAP文件可知
HEAP 0x200106f8 Section 512 startup_stm32f2xx.o(HEAP)
STACK 0x200108f8 Section 1024 startup_stm32f2xx.o(STACK)
__heap_base 0x200106f8 Data 0 startup_stm32f2xx.o(HEAP)
__heap_limit 0x200108f8 Data 0 startup_stm32f2xx.o(HEAP)
__initial_sp 0x20010cf8 Data 0 startup_stm32f2xx.o(STACK)
显然 Cortex-m3资料可知:__initial_sp是堆栈指针,它就是FLASH的0x8000000地址前面4个字节(它根据堆栈大小,由编译器自动生成)
显然堆和栈是相邻的。
3.堆和栈空间分配
栈:向低地址扩展
堆:向高地址扩展
显然如果依次定义变量
先定义的栈变量的内存地址比后定义的栈变量的内存地址要大
先定义的堆变量的内存地址比后定义的堆变量的内存地址要小
4.堆和栈变量
栈:临时变量,退出该作用域就会自动释放
堆:malloc变量,通过free函数释放
另外:堆栈溢出,编译不会提示,需要注意
下面是STM32使用手册上的串口发送时序图:
1、 使能串口发送TE,此时USART_DR为空,此时应查询TXE是否置1,TXE置1,TX脚先发送一个空闲帧,把F1帧写入USART_DR,TXE被清零。因为这时正在发送空闲帧,所以写入USART_DR的数据被放入TDR寄存器,还没有拷贝到移位寄存器。
2、 在空闲帧发送完后,TDR寄存器中的数据被拷贝到移位寄存器,此时应查询TXE是否置1,TXE置1,表示TDR已空,可以放入下一个数据。此时在TX脚上将会发送F1帧的数据,同时软件把F2帧的数据写入USART_DR,TXE被清零。
3、 在F1帧的停止位发送完后,因为TDR寄存器中的F2还没被拷入移位寄存器,所以此时TXE仍为0,TC不置1.此时应查询TXE是否置1,TXE置1,表示TDR已空,可以放入下一个数据。此时在TX脚上将会发送F2帧的数据,同时软件把F3帧的数据写入USART_DR,TXE被清零。
4、 在F2帧的停止位发送完后,因为TDR寄存器中的F3还没被拷入移位寄存器,所以此时TXE仍为0,TC不置1.此时应查询TXE是否置1,TXE置1,表示TDR已空,后面没有数据写入USART_DR,TXE保持高电平,此时在TX脚上将会发送F3帧的数据。
5、 在F3帧的停止位发送完后,因为此时TXE为1,所以TC标志会置1.如果TCIE为1,将会产生中断。