你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

SPI 通信,既有单字节数据传输,又有多字节数据传输,该如正确使用 DMA?

[复制链接]
Ha~ha~ 提问时间:2024-11-8 09:30 / 未解决
    使用 ENC28J60 + UIP 实现 UDP 数据的收发,其中ENC28J60 通过 SPI 接口和单片机连接;在收发数据的时候,要先对 ENC28J60 的寄存器进行读写,然后才写入或者读取要收发的数据。
    读写寄存器的时候,开启了 SPI 的片选,只需要读取 1 到 2 个字节的数据;在正式读写收发的数据的时候,开启 SPI 片选需要读写多个字节的数据。
    这样的情况下如何配置DMA,如果在开启片选信号的时候开启DMA,关闭片选信号的情况下关闭DMA;在进行寄存器读写的时候,岂不是会出现频繁开关DMA的情况,这样算不算降低了程序的执行效率,而且还增加了程序的不稳定性?
    我想在进行寄存器读写的时候,单独使用SPI,不使能DMA,在传输数据的时候,再开启DMA,请问这样配置合理吗?或者说还有无其他的实现方式?

读写寄存器部分代码:
/*********************************************
函数名:ENC28J60_ReadOp
功  能:从器件寄存器读取状态
形  参:op--操作码  address--控制寄存器地址
返回值:寄存器状态值
备  注:命令时序图--29页
**********************************************/
uint8_t ENC28J60_ReadOp(uint8_t op, uint8_t address)
{
    uint8_t dat = 0;

    CS_L;

    dat = op | (address & ADDR_MASK); //重构操作码和地址,参见手册第28页。
    SPI_ReadWrite(dat);

    dat = SPI_ReadWrite(0xFF);
    //如果需要,进行虚拟读取(对于MAC和MII,请参见手册第29页)
        /* 如果是MAC和MII寄存器,第一个读取的字节无效,该信息包含在地址的最高位 */
    if(address & 0x80)
    {
        dat = SPI_ReadWrite(0xFF);
    }

    CS_H;
    return dat;
}

/*********************************************
函数名:ENC28J60_WriteOp
功  能:向器件寄存器写入1byte
形  参:op--操作码  address--控制寄存器地址  data--数据字节
**********************************************/
void ENC28J60_WriteOp(uint8_t op, uint8_t address, uint8_t data)
{
    uint8_t dat = 0;

    CS_L;
    dat = op | (address & ADDR_MASK);//重构操作码和地址,参见手册第28页。
    SPI_ReadWrite(dat);

    SPI_ReadWrite(data);//写入数据
    CS_H;

}

读写数据部分代码:
/*********************************************
函数名:ENC28J60_ReadBuffer
功  能:读取缓冲存储器的数据
形  参:len--长度   data--存放数据的指针
**********************************************/
void ENC28J60_ReadBuffer(uint32_t len, uint8_t* data)
{
    CS_L;
    SPI_ReadWrite(ENC28J60_READ_BUF_MEM);
    while(len)
    {
        len--;
        *data = (uint8_t)SPI_ReadWrite(0);
        data++;
    }
    *data = '\0';
    CS_H;
}
/*********************************************
函数名:ENC28J60_WriteBuffer
功  能:向缓冲存储器写入数据
形  参:len--字节长度   data--数据内容的指针
**********************************************/
void ENC28J60_WriteBuffer(uint32_t len, uint8_t* data)
{
    CS_L;
    SPI_ReadWrite(ENC28J60_WRITE_BUF_MEM);
    while(len)
    {
        len--;
        SPI_ReadWrite(*data);
        data++;
    }
    CS_H;

}
收藏 评论5 发布时间:2024-11-8 09:30

举报

5个回答
xmshao 回答时间:2024-11-8 11:12:01
读写寄存器的时候,开启了 SPI 的片选,只需要读取 1 到 2 个字节的数据;在正式读写收发的数据的时候,开启 SPI 片选需要读写多个字节的数据。

==>片选你可以使用STM32 SPI外设的NSS脚或者另外单独拉一个GPIO来完成。你希望每次单独做不同长度的读取,这是没问题的。对于片选脚,想通信时就先拉低,通信完毕拉高即可。当然,如果使用NSS,交由硬件管理也可以。

这样的情况下如何配置DMA,如果在开启片选信号的时候开启DMA,关闭片选信号的情况下关闭DMA;在进行寄存器读写的时候,
岂不是会出现频繁开关DMA的情况,这样算不算降低了程序的执行效率,而且还增加了程序的不稳定性?

==》
DMA配置其实只关心触发源,目的地,源端地,传输长度,再就是传输模式。

鉴于你目前应用,你这里配置为Normal模式合适,传输长度你具体启动SPI的DMA收发时给定。每完成启动一轮SPI收发后DMA会自行停止,
无须手动干预。这样使用谈不上降低程序执行效率或增加不稳定性。

我想在进行寄存器读写的时候,单独使用SPI,不使能DMA,在传输数据的时候,再开启DMA,请问这样配置合理吗?或者说还有无其他的实现方式?
==》
也是可以的。你现在配置DMA为Normal模式,不想使用DMA时,基于查询或中断方式进行SPI收发也可以。说不上什么不合理,但似乎没有
什么必要,反正DMA都配置好,随时待命了。
butterflyspring 回答时间:2024-11-8 09:37:24
标题和内容都改了,与时俱进,本内容也得改了~~~~

xmshao 回答时间:2024-11-8 09:55:01
标题和内容都改了,刚回复的只好删除撤掉了。
Ha~ha~ 回答时间:2024-11-8 10:01:02

xmshao 发表于 2024-11-8 09:55
标题和内容都改了,刚回复的只好删除撤掉了。

本来发布了,刚刚看发现只文章居然是空白的,然后就把内容补上去了,顺带修改了一下题目.

xmshao 回答时间:2024-11-8 10:15:44

Ha~ha~ 发表于 2024-11-8 10:01
本来发布了,刚刚看发现只文章居然是空白的,然后就把内容补上去了,顺带修改了一下题目.
...

image.png

OK,简单交流下。

片选可以使用STM32 SPI模块的片选脚,或者单拉一个GPIO也行。你这里的SPI_READWRITE换成相应SPI库函数,并使用DMA模式。 当然,你要在SPI配置那里使能SPI的DMA收发配置。你这里len对应 SPI DMA收发API函数里的size,如果只读写1个数据,size就是1。

显然,当你把SPI收发换成DMA方式后,代码会更简洁些。效率也会更高。

STM32各个系列的Cube库里都有关于SPI的基本例程,可以参考,尤其看懂那几个基本的收发API函数。

所属标签

相似问题

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版