SPI - flash代码分析
更新时间:2024-03-18 18:49:01 阅读量: 综合文库 文档下载
- spirit推荐度:
- 相关推荐
SPI_flash代码分析
以下是主函数
int main(void) {
Xuint8 send_data[16], recv_data[16]; Xuint8 error, SF_sr;
Xuint16 sector_select, page_select; Xuint32 i, j;
xil_printf(\); /* 初始化 FLASH_SPI控制器 */ //1、初始化SPI
//2、设这控制寄存器CR为主MASTER transaction inhibit disable,人工选择从设备以及设置SPI为master
//3、初始化SSR(从设备选择寄存器)为不选择任何一个从设备。 Initialize_Spi_Controller(XPAR_SPI_FLASH_BASEADDR); /* 使能SPI控制器 */
// Description : 设置SPICR寄存器的SPI System Enable位 //1、设置SPE=1,SPI SYSTEM ENABLE
XSpi_Set_Enable(XPAR_SPI_FLASH_BASEADDR);
/* 设置FLASH的WRSR寄存器,设置成功说明FLASH控制已经OK */ do { //设置FLASH以使FLASH任何一部分都受保护protected
SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x7c); SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); } while(SF_sr != 0x7C);
/* 设置FLASH的WRSR寄存器,使能All sector可写 */ do {
//设置FLASH以使FLASH任何一部分都不受保护protected
SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x00); SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); } while(SF_sr != 0x00);
/*整片擦除 */
xil_printf(\); SF_bulk_erase(XPAR_SPI_FLASH_BASEADDR);
xil_printf(\);
/*整片校验看是不是全0XFF*/
SF_start_read(XPAR_SPI_FLASH_BASEADDR, 0x00, 0x00, 0x00, SCK_FASTER_THAN_20MHz); error = 0;
for (i = 0; i < SF_BYTES/16; i++) {
/*每次接收16个字节 校验*/
spi_transfer(XPAR_SPI_FLASH_BASEADDR,send_data, recv_data, 16); for (j = 0; j < 16; j++) {
if(recv_data[j] != 0xFF) {
xil_printf(\, i,j);
j = 16;
i = SF_BYTES; error = 1; } } }
if(!error)
xil_printf(\);
SF_end_read (XPAR_SPI_FLASH_BASEADDR); /*SPI FLASH page编程*/
xil_printf(\);
for(sector_select = 0; sector_select < SF_SECTORS; sector_select++)
for(page_select = 0; page_select < SF_PAGES_PER_SECTOR; page_select++) {
/*发送 page编程命令 */
SF_start_page_program (XPAR_SPI_FLASH_BASEADDR, sector_select, page_select, 0x00);
for (i = 0; i < SF_BYTES_PER_PAGE; i+=16) {
for(j = 0; j < 16; j++) send_data[j] = i + j; /*每次发送16个字节 */
spi_transfer(XPAR_SPI_FLASH_BASEADDR,send_data, recv_data, 16); }
SF_end_page_program (XPAR_SPI_FLASH_BASEADDR); }
xil_printf(\, i);
/*SPI FLASH 读取数据进行验证*/
SF_start_read (XPAR_SPI_FLASH_BASEADDR, 0x00, 0x00, 0x00, SCK_FASTER_THAN_20MHz); error = 0;
for (i = 0; i < SF_BYTES; i+=16) {
/*每次读取16个字节进行验证 */
spi_transfer(XPAR_SPI_FLASH_BASEADDR,send_data, recv_data, 16); for(j = 0; j < 16; j++) {
if(recv_data[j] != ((i + j) & 0xFF)) {
xil_printf(\, i,j); xil_printf(\, recv_data[j]); xil_printf(\, (i+j)); j = 16;
i = SF_BYTES; error = 1; } } }
if(!error)
xil_printf(\); else xil_printf(\); SF_end_read (XPAR_SPI_FLASH_BASEADDR); return 0; }
接下来我们一句一句话分析主函数main()
1、Initialize_Spi_Controller(XPAR_SPI_FLASH_BASEADDR); 以下是这个函数的定义
#define Initialize_Spi_Controller(BaseAddress) \\ { \\
XSpi_WriteReg(BaseAddress, XSP_SRR_OFFSET,XSP_SRR_RESET_MASK); \\ XSpi_WriteReg(BaseAddress, XSP_CR_OFFSET, INIT_CREG); \\ XSpi_WriteReg(BaseAddress, XSP_SSR_OFFSET, SPI_NONE_SELECT); \\ }
这三句话分别代表的含义是:
XSpi_WriteReg(BaseAddress, XSP_SRR_OFFSET,XSP_SRR_RESET_MASK);代表
初始化FPGA内部SPI接口的reset register,即重置FPGA内部SPI接口。以下是关于寄存器SRR的描述。
通过文档《xps_spi.pdf》有关SRR的描述,只能向SRR中写入0x0000000A.
XSpi_WriteReg(BaseAddress, XSP_CR_OFFSET, INIT_CREG);代表初始化FPGA内部的SPI接口中的控制寄存器control register,以下是关于CR控制寄存器的描述
写入的值为
#define INIT_CREG (XSP_CR_TRANS_INHIBIT_MASK | XSP_CR_MANUAL_SS_MASK | XSP_CR_MASTER_MODE_MASK)代表禁止FPGA内部的SPI接口传输数据,人工选择从设备,FPGA内部的SPI接口为主设备master
XSpi_WriteReg(BaseAddress, XSP_SSR_OFFSET, SPI_NONE_SELECT);以下是SSR寄存
器的描述。:
初始化FPGA内部的SPI接口中的slave select register,写入的值为不选择任何一个从设备。 2、XSpi_Set_Enable(XPAR_SPI_FLASH_BASEADDR); 以下是该函数的定义
#define XSpi_Set_Enable(BaseAddress) \\ { \\
Xuint16 Control; \\
Control = XSpi_ReadReg(BaseAddress, XSP_CR_OFFSET); \\ Control |= XSP_CR_ENABLE_MASK; \\
XSpi_WriteReg(BaseAddress, XSP_CR_OFFSET, Control); \\ }
该函数代表重新初始化Control register,以使能FPGA内部的SPI接口可用即将控制寄存器CR中的SPE位置1。 3、do { //设置FLASH以使FLASH任何一部分都受保护protected
SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x7c); SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); } while(SF_sr != 0x7C);
首先分析SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x7c); 该函数的定义为:
void SF_write_status_register (Xuint32 BaseAddress, Xuint8 data) {
Xuint8 op_code[2], bogus_data[2]; // 设置SPI Flash 的写使能
SF_write_enable(BaseAddress); //选中第一个设备使能 SPI_FLASH_CS_LOW();
// 写SPI FLASH的状态 寄存器 op_code[0] = WRSR; op_code[1] = data;
spi_transfer(BaseAddress, op_code, bogus_data, 2);
//关闭选中设备以使任何一个设备都没有选中 SPI_FLASH_CS_HIGH();
// 等待写寄存器/编程/擦除命令操作结束 poll_until_complete(BaseAddress); // 设置SPI Flash 的写不使能
SF_write_disable(BaseAddress); }
这个函数的功能主要是要将data的值写入到STATUS REGISTER中去。下面是有关WRSR指令的描述和时序,
从中可以看出,在执行WRSR之前,必须首先要执行一下WREN指令,然后将Chip Select选中置0,这样之后才能执行,等将指令发送到FLASH后就得将具Chip Select置1以使flash无效,进入自身的the self-timed write status register cycle。具体的查看其数据手册。 接下来分析函数SF_write_enable(BaseAddress); 该函数的定义为:
void SF_write_enable (Xuint32 BaseAddress) {
Xuint8 op_code = WREN, bogus_data; //选中第一个设备使能。 SPI_FLASH_CS_LOW(); // 发送写使能命令
spi_transfer(BaseAddress, &op_code, &bogus_data, 1); //关闭选中设备以使任何一个设备都没有选中 SPI_FLASH_CS_HIGH(); }
首先该函数选中第一个从设备也就是我们这个FLASH。
接下来我们分析函数spi_transfer(BaseAddress, &op_code, &bogus_data, 1); 该函数的定义为:
void spi_transfer (Xuint32 BaseAddress, Xuint8 *send, Xuint8 *recv, Xuint8 num_bytes) {
Xuint8 NumBytesRcvd = 0; Xuint8 NumBytesSent = 0;
// 把发送的数据写入到SPI控制器的发送FIFO中 while(num_bytes--) {
XSpi_WriteReg(BaseAddress, XSP_DTR_OFFSET, *send++); NumBytesSent++; }
// 设置SPICR寄存器,使能Master Transactions XSpi_Clear_Inhibit(BaseAddress); // 等待发送FIFO为空
while (!(XSpi_ReadReg(BaseAddress, XSP_SR_OFFSET) & XSP_SR_TX_EMPTY_MASK)); // 从接收FIFO中接收数据
while ((XSpi_ReadReg(BaseAddress, XSP_SR_OFFSET) & XSP_SR_RX_EMPTY_MASK) == 0) {
*recv++ = XSpi_ReadReg(BaseAddress,XSP_DRR_OFFSET); NumBytesRcvd++; }
// 设置SPICR寄存器,Master Transactions 不使能 XSpi_Set_Inhibit(BaseAddress);
/*如果没有数据发送或发送的数据不等于接收的数据,则显示错误
* If no data was sent or the data that was sent was not received, * then return an error */
if ((NumBytesSent != NumBytesRcvd) || (NumBytesSent == 0)) {
print(\); } }
首先这个函数先将FPGA内部的SPI接口要传输的数据写入到FIFO中去, 然后设置Control register控制寄存器,以清楚禁止master传输位以使SPI接口能够传输数据。接下来就是开始传输数据了,确切的说是交换数据。 然后开始循环查询FPGA内部的SPI接口的Status register查看发送FIFO中的数据是否为空,假如为空则发送完毕,假如没有为空,则继续发送数据。
接下来就是读取接收FIFO中的数据,查询Status register看接收FIFO是否为空,不为空则读取数据,直到接收FIFO中的数据为空。这样就把数据传输完毕。
紧接着设置Control register寄存器以使传输禁止即Master Transactions 不使能。 检查接收的数据和发送的数据是否数量相等,相等则为正常,不相等则为错误。
我们再返回到函数void SF_write_enable (Xuint32 BaseAddress)
{
Xuint8 op_code = WREN, bogus_data; //选中第一个设备使能。 SPI_FLASH_CS_LOW(); // 发送写使能命令
spi_transfer(BaseAddress, &op_code, &bogus_data, 1); //关闭选中设备以使任何一个设备都没有选中 SPI_FLASH_CS_HIGH(); }
// 发送写使能命令
spi_transfer(BaseAddress, &op_code, &bogus_data, 1); 这句话就代表给FLASH设备一个WREN指令,允许写指令。 SPI_FLASH_CS_HIGH();
代表关闭选中设备以使任何一个设备都处于没有选中状态。
我们再返回到函数void SF_write_status_register (Xuint32 BaseAddress, Xuint8 data) {
Xuint8 op_code[2], bogus_data[2]; // 设置SPI Flash 的写使能
SF_write_enable(BaseAddress); //选中第一个设备使能 SPI_FLASH_CS_LOW();
// 写SPI FLASH的状态 寄存器 op_code[0] = WRSR; op_code[1] = data;
spi_transfer(BaseAddress, op_code, bogus_data, 2); //关闭选中设备以使任何一个设备都没有选中 SPI_FLASH_CS_HIGH();
// 等待写寄存器/编程/擦除命令操作结束 poll_until_complete(BaseAddress); // 设置SPI Flash 的写不使能
SF_write_disable(BaseAddress); }
SF_write_enable(BaseAddress);这句话整体代表向FLASH传输一条WREN指令,以使FLASH做好写操作准备。
SPI_FLASH_CS_LOW();代表选中第一个设备
spi_transfer(BaseAddress, op_code, bogus_data, 2);这句话代表传输两个数据,第一个字节为命令WRSR,第二个字节为要写入的数据。
SPI_FLASH_CS_HIGH();代表关闭选中设备以使任何一个设备都没有选中 poll_until_complete(BaseAddress);该函数的定义为 void poll_until_complete (Xuint32 BaseAddress) {
Xuint8 status_reg, op_code; op_code = RDSR;
SPI_FLASH_CS_LOW();
spi_transfer(BaseAddress, &op_code, &status_reg, 1); // 查询SPI FLASH 状态寄存器,检查WIP位直到WIP位为0 do {
spi_transfer(BaseAddress, &op_code, &status_reg, 1); }
while((status_reg & WRITE_IN_PROGRESS_MASK) == WRITE_IN_PROGRESS_MASK); SPI_FLASH_CS_HIGH(); }
该函数首先选中该FLASH即第一个设备为从设备,传输RDSR指令,然后不断读取FLASH中STATUS REGISTER中的值直到WIP=0.然后关闭该从设备以使任何一个设备都没有选中。
我们再返回到函数void SF_write_status_register (Xuint32 BaseAddress, Xuint8 data) {
Xuint8 op_code[2], bogus_data[2]; // 设置SPI Flash 的写使能
SF_write_enable(BaseAddress); //选中第一个设备使能 SPI_FLASH_CS_LOW();
// 写SPI FLASH的状态 寄存器 op_code[0] = WRSR; op_code[1] = data;
spi_transfer(BaseAddress, op_code, bogus_data, 2); //关闭选中设备以使任何一个设备都没有选中 SPI_FLASH_CS_HIGH();
// 等待写寄存器/编程/擦除命令操作结束 poll_until_complete(BaseAddress); // 设置SPI Flash 的写不使能
SF_write_disable(BaseAddress); }
SF_write_disable(BaseAddress);以下为该函数的定义 void SF_write_disable (Xuint32 BaseAddress) {
Xuint8 op_code = WRDI, bogus_data; SPI_FLASH_CS_LOW(); // 发送写不使能命令
spi_transfer(BaseAddress, &op_code, &bogus_data, 1); SPI_FLASH_CS_HIGH(); }
首先先选中该FLASH为从设备使能,然后发送WRDI指令到该FLASH中,最后使该从设备无效以使任何一个设备都不处于选中状态。
最后我们整体分析一下函数void SF_write_status_register (Xuint32 BaseAddress, Xuint8 data) {
Xuint8 op_code[2], bogus_data[2]; // 设置SPI Flash 的写使能
SF_write_enable(BaseAddress); //选中第一个设备使能 SPI_FLASH_CS_LOW();
// 写SPI FLASH的状态 寄存器 op_code[0] = WRSR; op_code[1] = data;
spi_transfer(BaseAddress, op_code, bogus_data, 2); //关闭选中设备以使任何一个设备都没有选中 SPI_FLASH_CS_HIGH();
// 等待写寄存器/编程/擦除命令操作结束 poll_until_complete(BaseAddress); // 设置SPI Flash 的写不使能
SF_write_disable(BaseAddress); }
1、首先给FLASH发送写使能指令WREN 2、接下来选中第一个从设备
3、给FLASH传输WRSR指令和要写入SR状态寄存器的值data 4、关闭选中的从设备FLASH以使任何一个设备都不被选中 5、等待flash自己写操作周期完毕。 6、给FLASH发送写不使能指令WRDI
所以整体的一个功能就是向SR状态寄存器写入data值。
所以语句SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x7c);向FLASH的STATUS REGISTER写入01111100接下来我们查看status register各个位代表的含义
即SRWD=0;BP3=1;TB=1;BP2=1;BP1=1;BP0=1;WEL=0;WIP=0; 根据保护
表格
所以这句话代表所有区域都受写,擦除保护,而且是软件保护,允许WRSR指令对于SR状态寄存器的写操作。 do { //设置FLASH以使FLASH任何一部分都受保护protected
SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x7c); SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); } while(SF_sr != 0x7C);
接下来继续分析SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); 我们先看函数SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); 的定义
Xuint8 SF_read_status_register (Xuint32 BaseAddress) {
Xuint8 status_reg[2], op_code[2]; op_code[0] = RDSR;
SPI_FLASH_CS_LOW(); // 读状态寄存器
spi_transfer(BaseAddress, op_code, status_reg, 2);
SPI_FLASH_CS_HIGH();
return status_reg[1]; }
以下是芯片手册上关于指令RDSR的描述
首先选中flash为从设备,然后传输RDSR指令,并且读取FLASH中SR中的值到返回值中去,最后关闭该从设备以使任何一个设备都处于不选中状态。 do { //设置FLASH以使FLASH任何一部分都受保护protected
SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x7c); SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); } while(SF_sr != 0x7C);
所以这三句话代表向flash中的STATUS REGISTER寄存器写入0X7C,并且读取flash中STATUS regigter的值是否为0X7C确认写入成功。
4、do {
//设置FLASH以使FLASH任何一部分都不受保护protected
SF_write_status_register (XPAR_SPI_FLASH_BASEADDR, 0x00); SF_sr = SF_read_status_register(XPAR_SPI_FLASH_BASEADDR); } while(SF_sr != 0x00);
这三句话代表向FLASH的状态寄存器SR中写入00000000.即没有任何一部分受保护都可以写入和擦除。
5、SF_bulk_erase(XPAR_SPI_FLASH_BASEADDR);我们查看该函数的定义
void SF_bulk_erase (Xuint32 BaseAddress) {
Xuint8 op_code, bogus_data; // FLASH写使能
SF_write_enable(BaseAddress); SPI_FLASH_CS_LOW(); // 发送FLASH块擦除命令 op_code = BE;
spi_transfer(BaseAddress, &op_code, &bogus_data, 1); SPI_FLASH_CS_HIGH();
// 等待写寄存器/编程/擦除命令操作结束 poll_until_complete(BaseAddress); // FLASH写不使能
SF_write_disable(BaseAddress); }
以下是数据手册上关于指令BE的相关描述
首先给flash发送一条写使能指令WREN,然后选中该flash从设备,向flash发送BE整块擦除指令。关闭该flash从设备以使任何一个从设备都处于不选中状态以使FLASH进入the self-timed Bulk Erase cycle周期。查询FLASH的status register看是否已经擦除完毕。最后给flash传输一个写不使能指令WRDI指令以使flash不能再被写入。
6、 SF_start_read(XPAR_SPI_FLASH_BASEADDR, 0x00, 0x00, 0x00, SCK_FASTER_THAN_20MHz); 该函数的定义如下:
void SF_start_read (Xuint32 BaseAddress, Xuint8 sector_address, Xuint8 page_address, Xuint8 page_offset, Xuint8 speed_setting) {
Xuint8 op_code[5], bogus_data[5]; SPI_FLASH_CS_LOW();
// 发送SPF FLASH 读命令或快速读命令 if(speed_setting) op_code[0] = FAST; else
op_code[0] = READ;
op_code[1] = sector_address; op_code[2] = page_address; op_code[3] = page_offset; op_code[4] = DUMMY_BYTE; if(speed_setting)
spi_transfer(BaseAddress, op_code, bogus_data, 5); else
spi_transfer(BaseAddress, op_code, bogus_data, 4); }
以下是数据手册关于flash读操作read指令和快速读操作指令fast-read的描述
该函数首先选中flash从设备,然后根据是否是快速读操作传输5个字节或者4个字节。第一个字节是读操作或者快速读操作指令,紧接着三个字节是地址,假如是快速读操作的话第五个字节代表空转字节的个数即忽略多少个开始读出的数据字节数。 SF_start_read(XPAR_SPI_FLASH_BASEADDR, 0x00, 0x00, 0x00,
SCK_FASTER_THAN_20MHz);这句话代表向flash传输READ指令和地址为0x000000.
7、 error = 0;
for (i = 0; i < SF_BYTES/16; i++) {
/*每次接收16个字节 校验*/
spi_transfer(XPAR_SPI_FLASH_BASEADDR,send_data, recv_data, 16); for (j = 0; j < 16; j++) {
if(recv_data[j] != 0xFF) {
xil_printf(\ j = 16;
i = SF_BYTES; error = 1; } } }
if(!error)
xil_printf(\
代表每次接收16个字节并且检测该16个字节是否都为0Xff,假如是的话正常,否则输出错误信息。
8、SF_end_read (XPAR_SPI_FLASH_BASEADDR);下面是该函数的定义 void SF_end_read (Xuint32 BaseAddress) {
SPI_FLASH_CS_HIGH(); }
关闭flash从设备以使任何一个设备都没有选中。
9、for(sector_select = 0; sector_select < SF_SECTORS; sector_select++) for(page_select = 0; page_select < SF_PAGES_PER_SECTOR; page_select++) {
/*发送 page编程命令 */
SF_start_page_program (XPAR_SPI_FLASH_BASEADDR, sector_select, page_select, 0x00);
for (i = 0; i < SF_BYTES_PER_PAGE; i+=16) {
for(j = 0; j < 16; j++) send_data[j] = i + j; /*每次发送16个字节 */
spi_transfer(XPAR_SPI_FLASH_BASEADDR,send_data, recv_data, 16); }
SF_end_page_program (XPAR_SPI_FLASH_BASEADDR); }
xil_printf(\
下面我们首先看函数SF_start_page_program (XPAR_SPI_FLASH_BASEADDR, sector_select, page_select, 0x00);的定义
void SF_start_page_program (Xuint32 BaseAddress, Xuint8 sector_address, Xuint8 page_address, \\
Xuint8 page_offset) {
Xuint8 op_code[4], bogus_data[4];
// FLASH写使能
SF_write_enable(BaseAddress);
SPI_FLASH_CS_LOW();
// 发送page编程命令 op_code[0] = PP;
op_code[1] = sector_address; op_code[2] = page_address; op_code[3] = page_offset;
spi_transfer(BaseAddress, op_code, bogus_data, 4); }
以下是数据手册关于PP指令的描述
正在阅读:
SPI - flash代码分析03-18
人教版必修二元素周期表教学设计05-09
护坡及挡土墙施工方案06-29
2017年小学生寒假安全知识教育内容02-15
关于辩证的否定观的研究04-24
生物分析化学作业05-23
今日基督徒普遍的可怜的光景01-26
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 代码
- 分析
- flash
- SPI
- SQL Server(青鸟-复习试题及参考答案-2010.6)
- 2019年入党志愿书填写范文模板
- 当代大学生恋爱问题调查问卷
- 2007级数值分析试卷一及参考答案
- 1.3元素周期表应用
- 电子商务与物流配送 - 图文
- 无聊的说说句子
- 活动方案大学生文化衫创意图形设计大赛活动方案
- 思南县乌江三桥杨家田1号桥孔桩开工报告
- 简单修改就可实用的入党申请书范文修改版
- 一般酒店管理系统 - 图文
- 《福楼拜家的星期天》课后练习答案
- 聚乙烯操作规程(4) - 图文
- 钻孔灌注桩监理要点
- 全球锂离子电池材料市场分析及产业现状
- 信号与系统12new
- 2016全生命周期项目管理国际新理念、新视野试卷满分
- 独柱墩连续箱梁桥抗倾覆稳定性验算分析
- 长三角港口谋求海铁联运对接一带一路战略
- 图解项目管理例题与答案及要点分析