打印
[LOOK]

LOOK+红杏头文件 学习第七帖:SPI(PDMA中断方式)

[复制链接]
4838|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Swallow_0322|  楼主 | 2011-6-22 17:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Swallow_0322 于 2011-6-25 15:30 编辑

本工程主要包含三个任务:①普通类任务task_LOOK_SPI_PDMA_t:等待标志Flag_SPI低四位任意位置位 然后完成来自于uart0的任务;
                        ②中断类任务LOOK_SPI_PDMA_t:通过PDMA中断完成SPI的读写操作;
      ③中断类任务uart0_t:uart 同步输出功能及接收功能

初始化设置:  时钟配置为 XTL12M_EN: 外部 4~24MHz 晶振使能
              CPU时钟频率为12MHz
     PB.0选择多功能输入RXD0 PB.1选择多功能输入TXD0
              UART时钟选择外部12MHZ,并使能UART0时钟
     SPI1时钟使能
     SPI的端口设置:PC.8~11 依次为MCLK、SPICLK1、MISO10、MOSI10
                    PB.9 TM1


主要完成功能: UART0 接收到字符‘R’或‘r’实现读取W25Q16BV Page0 的内容;
               UART0 接收到字符‘U’或‘u’实现W25Q16BV Page0 的内容加1;
      UART0 接收到字符‘D’或‘d’实现W25Q16BV Page0 的内容减1;
      UART0 接收到字符‘M’或‘m’实现读取W25Q16BV制造商ID及设备ID。


源文件:
main.h
 
#ifndef __LOOK_MAIN_H
#define __LOOK_MAIN_H
/*------------------------------------
LOOK头文件放入此处
------------------------------------*/
#include "look_config.h"
#include <look.h>
#include <instantiate>
/*------------------------------------
芯片及红杏头文件放入此处
------------------------------------*/
#include "NUC1xx.h"
#include "NUC1xxM051Seriescfg.h"
/*------------------------------------
系统定义的C++语言的头文件应该放入此处
------------------------------------*/
#include "LOOK_SPI_PDMA.h"
#include "SPI_PDMA.h"
#include "uart.h"
/*------------------------------------
全局类对象实例声明放入此位置
------------------------------------*/
#endif
SPI_PDMA.h
#ifndef __LOOK_SPI_H
#define __LOOK_SPI_H

#define Enable_SPI_CS GPIOAs.DOUT.Bits.Pin14 = 0
#define DISABLE_SPI_CS GPIOAs.DOUT.Bits.Pin14 = 1


// LOOK_SPI_PDMA_t 类为应用层提供了 SPI 接口
class LOOK_SPI_PDMA_t : public interrupt_t {
public:
LOOK_SPI_PDMA_t() __OPT_ATTR__; //构造函数
sem_t sem_pdma0; // 信号灯为PDMA0传输结束
sem_t sem_pdma1; // 信号灯为PDMA1传输结束
protected:
bool isr(int vector); //中断服务例程
void dsr(int vector, uintptr_t count); //中断滞后服务例程

private://私有方法
inline void SPI1_SingleWrite(uint32_t *pu32Data); //Write data to SPI bus and trigger SPI to start transfer.

public://公有方法及属性
uint32_t SPI_ReadMidDid(void);
uint32_t SPI_ReadStatusReg1(void);
void SPI_WaitReady(void);
void SPI_ChipErase(void);
void SPI_SectorErase(uint32_t StartAddress);
void SPI_ReadData(uint32_t StartAddress);
void SPI_PageProgram(uint32_t StartAddress);

public://私有属性
volatile uint8_t SrcArray[256], DestArray[256];//数据缓冲区
};
extern LOOK_SPI_PDMA_t SPI_PDMA;
#endif
SPI_PDMA.CPP(对该文件内PDMA初始化部分内容的改进参见#9楼 HOT大叔指导并使用红杏头文件V1.20
#include "main.h"

__OPT_INLINE__ LOOK_SPI_PDMA_t:OOK_SPI_PDMA_t():sem_pdma0(0),sem_pdma1(0) //构造函数
{
for (uint32_t i = 0; i < sizeof(SrcArray); i ++)
{ //清空缓存
SrcArray = 0;
DestArray = 0;
}

//SYSCLKs.APBCLK.Bits.SPI1_EN = 1; //使用LOOK进行初始化配置
SYSs.IPRSTC2.Bits.SPI1_RST = 1; //SPI1模块复位
SYSs.IPRSTC2.Bits.SPI1_RST = 0; //SPI1模块正常工作

// SYSs.GPCMFP.Bits.SPI1_SS0_MCLK = 1; //使用LOOK进行初始化配置
// SYSs.GPCMFP.Bits.SPI1_CLK = 1; //使用LOOK进行初始化配置
// SYSs.GPCMFP.Bits.SPI1_MOSI0= 1; //使用LOOK进行初始化配置
// SYSs.GPCMFP.Bits.SPI1_MISO0 = 1; //使用LOOK进行初始化配置
// SYSs.GPBMFP.Bits.TM1_SS11 = 1; //使用LOOK进行初始化配置
// SYSs.ALTMFP.Bits.PB9_S11 = 1; //使用LOOK进行初始化配置

SPI1s.CNTRL.Bits.TX_BIT_LEN = 0; //该寄存器用于标示一次传输中,完成的传输长度 32bits
SPI1s.CNTRL.Bits.SLAVE = 0; //MCU作为主机模式
SPI1s.SSR.Bits.SSR = 0; //当ASS位被清除,对该寄存器任何一位写1,将会激活SPI_SS [1:0]线,写0线上为非活动状态。
SPI1s.SSR.Bits.AUTOSS = 0; //该位清位,从机是否发出信号,由设置或清除SSR[1:0]寄存器决定
//禁止自动片选功能
SPI1s.CNTRL.Bits.CLKP = 0; // SCLK 低电平闲置
SPI1s.CNTRL.Bits.TX_NEG = 1; //SDO 信号在SPICLK的下降沿发送
SPI1s.CNTRL.Bits.RX_NEG = 0; //SDI 信号在SPICLK上升沿接收
//配置SPI1为主模式 TYPE1波形 32位传输

SPI1s.CNTRL.Bits.LSB = 0; //配置传输比特的顺序:优先发送/接收MSB

SPI1s.DIVIDER.Bits.DIVIDER2 = 0xFFFF;
SPI1s.DIVIDER.Bits.DIVIDER = ((12000000/2/1000000 + 1) >>1) - 1;
//设置SPI的时钟频率为1MHz

GPIOAs.PMD.Bits.PMD14 = GPIO_PMD_OUTPUT; //SPI_FLAH_CS配置为输出
DISABLE_SPI_CS;

// Enable PDMA Clock
SYSs.REGLOCK.Regs = 0x59;
SYSs.REGLOCK.Regs = 0x16;
SYSs.REGLOCK.Regs = 0x88;
SYSCLKs.AHBCLK.Bits.PDMA_EN = 1;
SYSs.REGLOCK.Regs = 0x00;

/* -------------------------------------------- */
/* Configure PDMA Channel 0 to receive SPI1 Rx0 */
/* -------------------------------------------- */
PDMA_GCR->DSSR0.SPI1_RXSEL = 0;
//APB设备选择 PDMA 通道 SPI选择PDMA通道0 读操作

//PDMA 通道0时钟使能
PDMA_GCR->GCRCSR.CLK0_EN = 1 ;
PDMA0s.CSR.Bits.PDMACEN = 1; //PDMA通道使能
PDMA0s.SAR.Regs = SPI1_BASE + 0x10; //PDMA 发送源地址寄存器 SPI1 Rx0
PDMA0s.DAR.Regs = (uint32_t)DestArray; //PDMA 传输目的地址寄存器
PDMA0s.CSR.Bits.SAD_SEL = 0b10; //传输源地址固定(适用于单数据 传递到多目的地址)
PDMA0s.CSR.Bits.DAD_SEL = 0b00; //传输目的地址 持续增加
PDMA0s.CSR.Bits.APB_TWS = 0b01; // 一字节(8 bits)为单位传输 用于 PDMA 运作
PDMA0s.CSR.Bits.MODE_SEL= 0b01; //01 = IP到存储器模式 (APB-to-Memory).
PDMA0s.BCR.Regs = 256; //PDMA 传输计数寄存器
//配置PDMA通道0


PDMA0s.IER.Bits.BLKD_IE = 1;
//使能PDMA通道0中断 中断源为:eDRVPDMA_BLKD(PDMA Transfer Done 中断使能)

/* -------------------------------------------- */
/* Configure PDMA Channel 1 to receive SPI1 Tx0 */
/* -------------------------------------------- */

PDMA_GCR->DSSR0.SPI1_TXSEL = 1;
//APB设备选择PDMA通道 SPI选择PDMA通道1 写操作
PDMA_GCR->GCRCSR.CLK1_EN = 1 ;
PDMA1s.SAR.Regs = (uint32_t)SrcArray; //PDMA 发送源地址寄存器
PDMA1s.DAR.Regs = SPI1_BASE + 0x20; //PDMA 传输目的地址寄存器
PDMA1s.CSR.Bits.SAD_SEL = 0b00; //传输源地址 持续增加
PDMA1s.CSR.Bits.DAD_SEL = 0b10; //传输目的地址不变
PDMA1s.CSR.Bits.APB_TWS = 0b01; // 一字节(8 bits)为单位传输 用于 PDMA 运作
PDMA1s.CSR.Bits.MODE_SEL= 0b10; //存储器到 IP 模式 (Memory-to-APB)
PDMA1s.BCR.Regs = 256; //PDMA 传输计数寄存器
//配置PDMA通道1

PDMA1s.IER.Bits.BLKD_IE = 1;
//使能PDMA通道1中断 中断源为:eDRVPDMA_BLKD(PDMA Transfer Done 中断使能)

attach(PDMA_IRQn); //连接PDMA中断
vector_t::enable(PDMA_IRQn); //使能PDMA中断
}
// 中断服务例程
bool LOOK_SPI_PDMA_t::isr(int vector)
{
if (PDMA0s.ISR.Bits.BLKD_IF)
{
PDMA0s.ISR.Bits.BLKD_IF = 1;
sem_pdma0.do_post(); //释放一个信号量资源
}
if (PDMA1s.ISR.Bits.BLKD_IF)
{
PDMA1s.ISR.Bits.BLKD_IF = 1;
sem_pdma1.do_post(); //释放一个信号量资源
}
return FALSE;
}
// 中断滞后服务例程
void LOOK_SPI_PDMA_t::dsr(int vector, uintptr_t count)
{
}

void LOOK_SPI_PDMA_t::SPI1_SingleWrite(uint32_t *pu32Data)
{
while (SPI1s.CNTRL.Bits.GO_BUSY);

SPI1s.TX0.Regs = *pu32Data;
SPI1s.CNTRL.Bits.GO_BUSY = 1;
}

void LOOK_SPI_PDMA_t::SPI_WaitReady(void)
{
uint32_t ReturnValue;

do{
ReturnValue = SPI_ReadStatusReg1();
ReturnValue = ReturnValue & 1;
}while(ReturnValue!=0);
//检查从设备状态寄存器1的BUSY位 等待其为0
}


/*****************************
** Name: SPI_ReadMidDid
** Function: W25Q16BV读制造商ID及设备ID函数
** Input: None
** OutPut: MID & DID
** Data: 2011-06-18
** Note:
****************************/
uint32_t LOOK_SPI_PDMA_t::SPI_ReadMidDid(void)
{
uint32_t au32SourceData;
uint32_t au32DestinationData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x90;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x90 (Read Manufacturer/Device ID)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits

au32SourceData = 0x0;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x00 (24-bit Address)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 16;
//配置SPI传输的比特长度:16 bits


au32SourceData = 0x0;
SPI1_SingleWrite(&au32SourceData);
//接收数据

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活

au32DestinationData = SPI1s.RX0.Regs;
//从接收寄存器读数据. 但是不触发下一次数据传输.

return (au32DestinationData & 0xffff);
}

/*****************************
** Name: SPI_ReadStatusReg1
** Function: W25Q16BV读状态寄存器1函数
** Input: None
** OutPut: ReadStatusReg1
** Data: 2011-06-18
** Note:
****************************/
uint32_t LOOK_SPI_PDMA_t::SPI_ReadStatusReg1(void)
{
uint32_t au32SourceData;
uint32_t au32DestinationData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 16;
//配置SPI传输的比特长度:16 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x0500;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x0500 (Read status register 1)

while (SPI1s.CNTRL.Bits.GO_BUSY) ;
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活

au32DestinationData = SPI1s.RX0.Regs;
//从接收寄存器读数据. 但是不触发下一次数据传输.

return (au32DestinationData & 0xFF);
}

/*****************************
** Name: SPI_ChipErase
** Function: W25Q16BV片擦除函数
** Input: None
** OutPut: None
** Data: 2011-06-18
** Note:
****************************/
void LOOK_SPI_PDMA_t::SPI_ChipErase(void)
{
uint32_t au32SourceData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x06;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x06 (Write enable)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0xC7;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0xC7 (Chip Erase)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活
}

/*****************************
** Name: SPI_SectorErase
** Function: W25Q16BV扇区擦除函数
** Input: StartAddress
** OutPut: None
** Data: 2011-06-18
** Note:
****************************/
void LOOK_SPI_PDMA_t::SPI_SectorErase(uint32_t StartAddress)
{

uint32_t au32SourceData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x06;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x06 (Write enable)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x20;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x20 (Sector Erase)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits

au32SourceData = StartAddress&0xFFF000;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: StartAddress

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活
}

/*****************************
** Name: SPI_ReadData
** Function: W25Q16BV读数据函数
** Input: StartAddress
** OutPut: None
** Data: 2011-06-18
** Note:
****************************/
void LOOK_SPI_PDMA_t::SPI_ReadData(uint32_t StartAddress)
{
uint32_t au32SourceData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x03;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x03 (Read data)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits

au32SourceData = StartAddress;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: StartAddress

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

SPI1s.DMA.Bits.RX_DMA_GO = 1;
//开始DMA接收模式,SPI模块自动切换为DMA模式接收

SPI1s.CNTRL.Bits.GO_BUSY = 1;
//设定GO_BUSY 比特来触发 SPI 数据传输.

while(!sem_pdma0.wait());

DISABLE_SPI_CS;
//从设备片选信号取消激活
}

/*****************************
** Name: SPI_PageProgram
** Function: W25Q16BV按页编程函数
** Input: StartAddress
** OutPut: None
** Data: 2011-06-18
** Note:
****************************/
void LOOK_SPI_PDMA_t::SPI_PageProgram(uint32_t StartAddress)
{
uint32_t au32SourceData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x06;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x06 (Write enable)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x02;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x02 (Page program)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits

au32SourceData = StartAddress;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: StartAddress

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits

SPI1s.DMA.Bits.TX_DMA_GO = 1;
//使能SPI1且模式为DMA-Transmitting模式

SPI1s.CNTRL.Bits.GO_BUSY = 1;
//设定GO_BUSY 比特来触发 SPI 数据传输.

while(!sem_pdma1.wait());

DISABLE_SPI_CS;
//从设备片选信号取消激活
}

LOOK_SPI_PDMA_t SPI_PDMA;

//=========================END OF FILE=========================//
其它文件见工程包!

工程包:
LOOK_SPI_PDMA.rar (771.58 KB)


相关帖子

沙发
hotpower| | 2011-6-22 17:55 | 只看该作者
谢谢三心发现红杏遗漏一个寄存器

使用特权

评论回复
板凳
john_lee| | 2011-6-22 17:56 | 只看该作者
先沙发,再看。

使用特权

评论回复
地板
Swallow_0322|  楼主 | 2011-6-22 21:05 | 只看该作者
2# hotpower

大叔客气!
我们应该感谢大叔提供这么方便使用的头文件!

使用特权

评论回复
5
Swallow_0322|  楼主 | 2011-6-22 21:06 | 只看该作者
3# john_lee

呵呵 Lee老师的沙发被秒杀了!:lol

使用特权

评论回复
6
hotpower| | 2011-6-22 22:35 | 只看该作者
本帖最后由 hotpower 于 2011-6-23 13:03 编辑

点击下载: NUC1xxM051Seriescfg.h(V1.20)


SPI_PDMA.CPP

#include "main.h"
__OPT_INLINE__ LOOK_SPI_PDMA_t::LOOK_SPI_PDMA_t():sem_pdma0(0),sem_pdma1(0)   //构造函数
{
for (uint32_t i = 0; i < sizeof(SrcArray); i ++)
{            //清空缓存
  SrcArray[i] = 0;
  DestArray[i] = 0;
}
//SYSCLKs.APBCLK.Bits.SPI1_EN = 1;   //使用LOOK进行初始化配置
SYSs.IPRSTC2.Bits.SPI1_RST = 1;   //SPI1模块复位
SYSs.IPRSTC2.Bits.SPI1_RST = 0;   //SPI1模块正常工作
// SYSs.GPCMFP.Bits.SPI1_SS0_MCLK = 1;  //使用LOOK进行初始化配置
// SYSs.GPCMFP.Bits.SPI1_CLK = 1;     //使用LOOK进行初始化配置
// SYSs.GPCMFP.Bits.SPI1_MOSI0= 1;    //使用LOOK进行初始化配置
// SYSs.GPCMFP.Bits.SPI1_MISO0 = 1;    //使用LOOK进行初始化配置
// SYSs.GPBMFP.Bits.TM1_SS11 = 1;    //使用LOOK进行初始化配置
// SYSs.ALTMFP.Bits.PB9_S11 = 1;     //使用LOOK进行初始化配置
SPI1s.CNTRL.Bits.TX_BIT_LEN = 0;  //该寄存器用于标示一次传输中,完成的传输长度 32bits
SPI1s.CNTRL.Bits.SLAVE = 0;    //MCU作为主机模式
SPI1s.SSR.Bits.SSR = 0;   //当ASS位被清除,对该寄存器任何一位写1,将会激活SPI_SS [1:0]线,写0线上为非活动状态。
SPI1s.SSR.Bits.AUTOSS = 0;  //该位清位,从机是否发出信号,由设置或清除SSR[1:0]寄存器决定
//禁止自动片选功能
SPI1s.CNTRL.Bits.CLKP = 0;  // SCLK 低电平闲置
SPI1s.CNTRL.Bits.TX_NEG = 1; //SDO 信号在SPICLK的下降沿发送
SPI1s.CNTRL.Bits.RX_NEG = 0; //SDI 信号在SPICLK上升沿接收   
//配置SPI1为主模式 TYPE1波形 32位传输
SPI1s.CNTRL.Bits.LSB = 0;  //配置传输比特的顺序:优先发送/接收MSB   

SPI1s.DIVIDER.Bits.DIVIDER2 = 0xFFFF;
SPI1s.DIVIDER.Bits.DIVIDER = ((12000000/2/1000000 + 1) >>1) - 1;
//设置SPI的时钟频率为1MHz
GPIOAs.PMD.Bits.PMD14 = GPIO_PMD_OUTPUT;     //SPI_FLAH_CS配置为输出
DISABLE_SPI_CS;
// Enable PDMA Clock
SYSs.REGLOCK.Regs = 0x59;
SYSs.REGLOCK.Regs = 0x16;
SYSs.REGLOCK.Regs = 0x88;
SYSCLKs.AHBCLK.Bits.PDMA_EN = 1;
SYSs.REGLOCK.Regs = 0x00;
/* -------------------------------------------- */
    /* Configure PDMA Channel 0 to receive SPI1 Rx0 */
/* -------------------------------------------- */
/// PDMA_GCR->PDSSR0.SPI1_RXSEL = 0;
PDMA_GCRs.PDSSR0.Bits.SPI1_RXSEL = 0;
//APB设备选择 PDMA 通道   SPI选择PDMA通道0 读操作
//PDMA 通道0时钟使能   
/// PDMA_GCR->GCRCSR.CLK0_EN = 1 ;
PDMA_GCRs.GCRCSR.Bits.CLK0_EN = 1 ;
PDMA0s.CSR.Bits.PDMACEN = 1;   //PDMA通道使能
/// PDMA0s.SAR.Regs = SPI1_BASE + 0x10;  //PDMA 发送源地址寄存器  SPI1 Rx0
PDMA0s.SAR.Regs = (uint32_t)&SPI1s.RX0.Regs;  //PDMA 发送源地址寄存器  SPI1 Rx0
PDMA0s.DAR.Regs = (uint32_t)DestArray; //PDMA 传输目的地址寄存器
PDMA0s.CSR.Bits.SAD_SEL = 0b10;   //传输源地址固定(适用于单数据 传递到多目的地址)
PDMA0s.CSR.Bits.DAD_SEL = 0b00;   //传输目的地址 持续增加
PDMA0s.CSR.Bits.APB_TWS = 0b01;   // 一字节(8 bits)为单位传输 用于 PDMA 运作
PDMA0s.CSR.Bits.MODE_SEL= 0b01;   //01 = IP到存储器模式 (APB-to-Memory).
PDMA0s.BCR.Regs = 256;     //PDMA 传输计数寄存器
//配置PDMA通道0


    PDMA0s.IER.Bits.BLKD_IE = 1;
//使能PDMA通道0中断  中断源为:eDRVPDMA_BLKD(PDMA Transfer Done 中断使能)
    /* -------------------------------------------- */
    /* Configure PDMA Channel 1 to receive SPI1 Tx0 */
/* -------------------------------------------- */

/// PDMA_GCR->PDSSR0.SPI1_TXSEL = 1;
PDMA_GCRs.PDSSR0.Bits.SPI1_TXSEL = 1;
//APB设备选择PDMA通道   SPI选择PDMA通道1 写操作
/// PDMA_GCR->GCRCSR.CLK1_EN = 1 ;
PDMA_GCRs.GCRCSR.Bits.CLK1_EN = 1 ;
PDMA1s.SAR.Regs = (uint32_t)SrcArray; //PDMA 发送源地址寄存器
/// PDMA1s.DAR.Regs = SPI1_BASE + 0x20;  //PDMA 传输目的地址寄存器
PDMA1s.DAR.Regs = (uint32_t)&SPI1s.TX0.Regs;  //PDMA 传输目的地址寄存器 TX0
PDMA1s.CSR.Bits.SAD_SEL = 0b00;   //传输源地址 持续增加
PDMA1s.CSR.Bits.DAD_SEL = 0b10;   //传输目的地址不变
PDMA1s.CSR.Bits.APB_TWS = 0b01;   // 一字节(8 bits)为单位传输 用于 PDMA 运作
PDMA1s.CSR.Bits.MODE_SEL= 0b10;   //存储器到 IP 模式 (Memory-to-APB)
PDMA1s.BCR.Regs = 256;     //PDMA 传输计数寄存器
//配置PDMA通道1

    PDMA1s.IER.Bits.BLKD_IE = 1;
//使能PDMA通道1中断  中断源为:eDRVPDMA_BLKD(PDMA Transfer Done 中断使能)
attach(PDMA_IRQn);     //连接PDMA中断
vector_t::enable(PDMA_IRQn);  //使能PDMA中断
}
// 中断服务例程
bool LOOK_SPI_PDMA_t::isr(int vector)
{
if (PDMA0s.ISR.Bits.BLKD_IF)
{
  PDMA0s.ISR.Bits.BLKD_IF = 1;
  sem_pdma0.do_post();   //释放一个信号量资源
}
if (PDMA1s.ISR.Bits.BLKD_IF)
{
  PDMA1s.ISR.Bits.BLKD_IF = 1;
  sem_pdma1.do_post();   //释放一个信号量资源
}
return FALSE;
}
// 中断滞后服务例程
void LOOK_SPI_PDMA_t::dsr(int vector, uintptr_t count)
{
}
void LOOK_SPI_PDMA_t::SPI1_SingleWrite(uint32_t *pu32Data)
{
while (SPI1s.CNTRL.Bits.GO_BUSY);
SPI1s.TX0.Regs = *pu32Data;
SPI1s.CNTRL.Bits.GO_BUSY = 1;
}
void LOOK_SPI_PDMA_t::SPI_WaitReady(void)
{
uint32_t ReturnValue;

do{
  ReturnValue = SPI_ReadStatusReg1();
  ReturnValue = ReturnValue & 1;
}while(ReturnValue!=0);
//检查从设备状态寄存器1的BUSY位 等待其为0
}

/*****************************
** Name:      SPI_ReadMidDid
** Function:  W25Q16BV读制造商ID及设备ID函数
** Input:     None
** OutPut:    MID & DID
** Data:      2011-06-18
** Note:      
****************************/
uint32_t LOOK_SPI_PDMA_t::SPI_ReadMidDid(void)
{
uint32_t au32SourceData;
    uint32_t au32DestinationData;

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits
Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x90;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x90 (Read Manufacturer/Device ID)
     
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits
au32SourceData = 0x0;
    SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x00 (24-bit Address)
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲
   
SPI1s.CNTRL.Bits.TX_BIT_LEN = 16;
//配置SPI传输的比特长度:16 bits

au32SourceData = 0x0;
SPI1_SingleWrite(&au32SourceData);
//接收数据

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活
au32DestinationData = SPI1s.RX0.Regs;
//从接收寄存器读数据.  但是不触发下一次数据传输.
    return (au32DestinationData & 0xffff);
}
/*****************************
** Name:      SPI_ReadStatusReg1
** Function:  W25Q16BV读状态寄存器1函数
** Input:     None
** OutPut:    ReadStatusReg1
** Data:      2011-06-18
** Note:      
****************************/
uint32_t LOOK_SPI_PDMA_t::SPI_ReadStatusReg1(void)
{
    uint32_t au32SourceData;
    uint32_t au32DestinationData;  
SPI1s.CNTRL.Bits.TX_BIT_LEN = 16;
//配置SPI传输的比特长度:16 bits
  
Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x0500;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x0500 (Read status register 1)
while (SPI1s.CNTRL.Bits.GO_BUSY) ;
//等待SPI端口空闲
  
DISABLE_SPI_CS;
//从设备片选信号取消激活
au32DestinationData = SPI1s.RX0.Regs;
//从接收寄存器读数据.  但是不触发下一次数据传输.
return (au32DestinationData & 0xFF);
}
/*****************************
** Name:      SPI_ChipErase
** Function:  W25Q16BV片擦除函数
** Input:     None
** OutPut:    None
** Data:      2011-06-18
** Note:      
****************************/
void LOOK_SPI_PDMA_t::SPI_ChipErase(void)
{
uint32_t au32SourceData;
SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits
Enable_SPI_CS;
//激活/配置从设备片选信号
au32SourceData = 0x06;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x06 (Write enable)
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲
DISABLE_SPI_CS;
//从设备片选信号取消激活
Enable_SPI_CS;
//激活/配置从设备片选信号
au32SourceData = 0xC7;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0xC7 (Chip Erase)
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲
DISABLE_SPI_CS;
//从设备片选信号取消激活
}
/*****************************
** Name:      SPI_SectorErase
** Function:  W25Q16BV扇区擦除函数
** Input:     StartAddress
** OutPut:    None
** Data:      2011-06-18
** Note:      
****************************/
void LOOK_SPI_PDMA_t::SPI_SectorErase(uint32_t StartAddress)
{

uint32_t au32SourceData;
SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits
Enable_SPI_CS;
//激活/配置从设备片选信号
au32SourceData = 0x06;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x06 (Write enable)
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲
DISABLE_SPI_CS;
//从设备片选信号取消激活
SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits
Enable_SPI_CS;
//激活/配置从设备片选信号
au32SourceData = 0x20;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x20 (Sector Erase)
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲
SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits  

au32SourceData = StartAddress&0xFFF000;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: StartAddress
  
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲
DISABLE_SPI_CS;
//从设备片选信号取消激活
}
/*****************************
** Name:      SPI_ReadData
** Function:  W25Q16BV读数据函数
** Input:     StartAddress
** OutPut:    None
** Data:      2011-06-18
** Note:      
****************************/
void LOOK_SPI_PDMA_t::SPI_ReadData(uint32_t StartAddress)
{
uint32_t au32SourceData;
SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits
Enable_SPI_CS;
//激活/配置从设备片选信号
au32SourceData = 0x03;
SPI1_SingleWrite(&au32SourceData);  
//发送数据到 SPI 总线: 0x03 (Read data)
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits
au32SourceData = StartAddress;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: StartAddress
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits  
SPI1s.DMA.Bits.RX_DMA_GO = 1;
//开始DMA接收模式,SPI模块自动切换为DMA模式接收

SPI1s.CNTRL.Bits.GO_BUSY = 1;
//设定GO_BUSY 比特来触发 SPI 数据传输.
while(!sem_pdma0.wait());

DISABLE_SPI_CS;
//从设备片选信号取消激活
}
/*****************************
** Name:      SPI_PageProgram
** Function:  W25Q16BV按页编程函数
** Input:     StartAddress
** OutPut:    None
** Data:      2011-06-18
** Note:      
****************************/
void LOOK_SPI_PDMA_t::SPI_PageProgram(uint32_t StartAddress)
{
uint32_t au32SourceData;
    SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits  

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x06;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x06 (Write enable)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

DISABLE_SPI_CS;
//从设备片选信号取消激活

Enable_SPI_CS;
//激活/配置从设备片选信号

au32SourceData = 0x02;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: 0x02 (Page program)

while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 24;
//配置SPI传输的比特长度:24 bits  

au32SourceData = StartAddress;
SPI1_SingleWrite(&au32SourceData);
//发送数据到 SPI 总线: StartAddress
  
while (SPI1s.CNTRL.Bits.GO_BUSY) {}
//等待SPI端口空闲

SPI1s.CNTRL.Bits.TX_BIT_LEN = 8;
//配置SPI传输的比特长度:8 bits  

SPI1s.DMA.Bits.TX_DMA_GO = 1;
//使能SPI1且模式为DMA-Transmitting模式
SPI1s.CNTRL.Bits.GO_BUSY = 1;
//设定GO_BUSY 比特来触发 SPI 数据传输.

while(!sem_pdma1.wait());

DISABLE_SPI_CS;
//从设备片选信号取消激活
}
LOOK_SPI_PDMA_t SPI_PDMA;
//=========================END OF FILE=========================//

使用特权

评论回复
7
Swallow_0322|  楼主 | 2011-6-23 07:53 | 只看该作者
7# hotpower

收到,大叔辛苦!

使用特权

评论回复
8
hotpower| | 2011-6-23 11:29 | 只看该作者
三心,红杏改好了。我也将你的例程改了两句,中午上传

使用特权

评论回复
9
hotpower| | 2011-6-23 13:06 | 只看该作者
三心:注意6楼我改的几行:三个'/'
///        PDMA_GCR->DSSR0.SPI1_RXSEL        =        0;
        PDMA_GCRs.PDSSR0.Bits.SPI1_RXSEL        =        0;        //APB设备选择 PDMA 通道   SPI选择PDMA通道0 读操作

        //PDMA 通道0时钟使能   
///        PDMA_GCR->GCRCSR.CLK0_EN = 1 ;
        PDMA_GCRs.GCRCSR.Bits.CLK0_EN = 1 ;
        PDMA0s.CSR.Bits.PDMACEN = 1;                        //PDMA通道使能
///        PDMA0s.SAR.Regs = SPI1_BASE + 0x10;                //PDMA 发送源地址寄存器  SPI1 Rx0
        PDMA0s.SAR.Regs = (uint32_t)&SPI1s.RX0.Regs;                //PDMA 发送源地址寄存器  SPI1 Rx0
///        PDMA_GCR->DSSR0.SPI1_TXSEL        = 1;
        PDMA_GCRs.PDSSR0.Bits.SPI1_TXSEL        = 1;
        //APB设备选择PDMA通道   SPI选择PDMA通道1 写操作
///        PDMA_GCR->GCRCSR.CLK1_EN = 1 ;
        PDMA_GCRs.GCRCSR.Bits.CLK1_EN = 1 ;
        PDMA1s.SAR.Regs = (uint32_t)SrcArray;        //PDMA 发送源地址寄存器
///        PDMA1s.DAR.Regs = SPI1_BASE + 0x20;                //PDMA 传输目的地址寄存器
        PDMA1s.DAR.Regs = (uint32_t)&SPI1s.TX0.Regs;                //PDMA 传输目的地址寄存器 TX0

使用特权

评论回复
10
Swallow_0322|  楼主 | 2011-6-23 19:36 | 只看该作者
9# hotpower

收到,多谢大叔指点!
(*^__^*) 嘻嘻,一看我的例程就是山寨版,大叔的这叫原版,还是原版好!

使用特权

评论回复
11
hotpower| | 2011-6-24 02:00 | 只看该作者
不是的,你的例程是原创,俺是学习的~~~

使用特权

评论回复
12
Swallow_0322|  楼主 | 2011-6-24 10:28 | 只看该作者
大叔太谦虚了!

使用特权

评论回复
13
plc_avr| | 2011-6-24 20:56 | 只看该作者
先标记,有空再细看,呵呵。

使用特权

评论回复
14
hotpower| | 2011-6-24 23:55 | 只看该作者
大叔太谦虚了!
Swallow_0322 发表于 2011-6-24 10:28


准确地讲是互相学习

使用特权

评论回复
15
john_lee| | 2011-10-13 17:47 | 只看该作者
本帖最后由 john_lee 于 2011-10-13 17:57 编辑

用了两天时间,写了一个PDMA的驱动组件,测试通过了,感觉还算好用吧。

类框架如下:
class pdma_t : public interrupt_t {             // PDMA设备类
public:
    class channel_t : public sfrs::pdma_t {     // PDMA通道类
        ...
        __INLINE__ void transfer(volatile void* dst, const uint32_t* src, uint_fast16_t cnt);   // M2P 32位数据传输
        __INLINE__ void transfer(volatile void* dst, const uint16_t* src, uint_fast16_t cnt);   // M2P 16位数据传输
        __INLINE__ void transfer(volatile void* dst, const uint8_t* src, uint_fast16_t cnt);    // M2P 8位数据传输
        __INLINE__ void transfer(uint32_t* dst, volatile void* src, uint_fast16_t cnt);         // P2M 32位数据传输
        __INLINE__ void transfer(uint16_t* dst, volatile void* src, uint_fast16_t cnt);         // P2M 16位数据传输
        __INLINE__ void transfer(uint8_t* dst, volatile void* src, uint_fast16_t cnt);          // P2M 8位数据传输
        __INLINE__ result_t wait(uintptr_t timeout = 0);                                        // 等待传输结束
    };
    friend class channel_t;
    enum class dev_t {      // 外设选择
        SPI0RX = 0,
        SPI0TX = 1,
        ...
        I2SRX = 24,
        I2STX = 25
    };
public:
    __INLINE__ pdma_t(uintptr_t channels);                              // 构造函数
    __WEAK__ channel_t* alloc(device_t dev, uintptr_t timeout = 0);     // 分配通道
    __WEAK__ void free(channel_t* channel);                             // 释放通道
protected:
    __WEAK__ bool isr(int vector);                                      // 中断服务例程
    __WEAK__ void dsr(int vector, uintptr_t count);                     // 中断滞后服务例程
    __INLINE__ uintptr_t wait(channel_t* channel, uintptr_t timeout);
private:
    uintptr_t intr_bits;            // 中断标志,用于 isr() 和 dsr() 之间的数据传递
    flag_t sync;                    // 同步对象,bit0:15 - 通道分配释放,bit16:31 - 传输结束信号
};


整个驱动由两个类管理:pdma_t 和 channel_t。

pdma_t 主要负责 PDMA 通道和中断管理,和 PDMA_GCR 寄存器组的操作,channel_t 是 pdma_t 的内部类(应该写为 pdma_t::channel_t),负责 PDMA 寄存器组的操作。

pdma 驱动组件的使用是比较简单的,举一个UART0发送的例子:
/*
    int uart0_t::write(const void* buf, uint_fast16_t len);
    uart0发送数据。
   
    参数
        buf:要发送的数据
        len:数据长度
*/
int uart0_t::write(const void* buf, uint_fast16_t len)
{
    using namespace numicro;
    auto channel = pdma.alloc(pdma_t::dev_t::UART0TX);      // 为UART0发送分配PDMA通道
    if (channel == 0)                                       // 分配失败
        return 0;
    UART0.IER(0).DMA_TX_EN(1);                              // 使能UART0的DMA发送
    channel->transfer(&UART0.THR, static_cast<const uint8_t*>(buf), len);   // 使用分配到的PDMA通道发送数据
    channel->wait();                                        // 等待发送完成
    UART0.IER(0);                                           // 禁能UART0的DMA发送
    pdma.free(channel);                                     // 释放PDMA通道
    return len;
}


例程先调用 pdma_t::alloc() 函数分配一个 PDMA 通道,如果没有可用的通道,任务将被阻塞;分配到通道后,例程使用 channel_t::transfer() 函数来发送数据,然后使用 channel_t::wait() 函数等待发送结束,在等待时,任务也将被阻塞,发送完成后,任务被 PDMA 中断唤醒,然后使用 pdma_t::free() 函数释放通道。

使用特权

评论回复
16
Swallow_0322|  楼主 | 2011-10-14 07:25 | 只看该作者
很好很强大,建议老师边写边发布,这样测试得快些,每次发布有不同版本,有个简单备注就可以,再未完成前只发布出来研究不答疑也可以,免得影响老师的思路!

使用特权

评论回复
17
JIESHENGKEJI| | 2013-5-7 11:59 | 只看该作者
学习中

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:播种一种行为,收获一种习惯;播种一种习惯,收获一种性格;播种一种性格,收获一种人生!

121

主题

1393

帖子

4

粉丝