打印
[STM32F1]

STM32F10x之SPI Master

[复制链接]
433|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
SHOPQQ|  楼主 | 2023-6-28 17:26 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
STM32F10x最多支持3组SPI。这里只配置2组SPI,CS脚由GPIO方式控制,所以CS不在SPI代码中,由具体的外设控制。

在大容量产品上,SPI接口还可以配置为支持I2S音频协议。

1. IO配置
采用默认的复用脚。

//SPI1
//A5 : SCK, A6: MISO, A7 : MOSI
GPIOA->CRL &= ~(0x00000FFF << ((5 % 8) * 4));
GPIOA->CRL |= (0x00000B4B << ((5 % 8) * 4));
GPIOA->BSRR = ((uint32_t)0x5 << 5);
//SPI2
//B13 : SCK, B14 : MISO, B15 : MOSI
GPIOB->CRH &= ~(0x00000FFF << ((13 % 8) * 4));
GPIOB->CRH |= (0x00000B4B << ((13 % 8) * 4));
GPIOB->BSRR = ((uint32_t)0x5 << 13);
SCK和MOSI都是GPIO_Mode_AF_PP,默认输出1,MISO是GPIO_Mode_IN_FLOATING。

使用特权

评论回复
沙发
SHOPQQ|  楼主 | 2023-6-28 17:26 | 只看该作者
SPI参数设置
void spimCfg(SPI_TypeDef* SPIx, uint16_t prescaler)
{
    SPI_InitTypeDef  SPI_InitStructure;
    SPI_Cmd(SPIx, DISABLE);
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  
    SPI_InitStructure.SPI_BaudRatePrescaler = prescaler;//SPI_BaudRatePrescaler_256;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPIx, &SPI_InitStructure);
    SPI_Cmd(SPIx, ENABLE);
}


STM32F10x的SPI有不少配置,这里采用常用的4线双工模式0的配置。

使用特权

评论回复
板凳
SHOPQQ|  楼主 | 2023-6-28 17:26 | 只看该作者
SPI数据方向设置(SPI_InitStructure.SPI_Direction)
有4种配置

#define SPI_Direction_2Lines_FullDuplex ((uint16_t)0x0000)
#define SPI_Direction_2Lines_RxOnly     ((uint16_t)0x0400)
#define SPI_Direction_1Line_Rx          ((uint16_t)0x8000)
#define SPI_Direction_1Line_Tx          ((uint16_t)0xC000)
SPI_Direction_2Lines_FullDuplex: 4线全双工。

SPI_Direction_2Lines_RxOnly:4线,只接收模式,一般用于多从设备模式,当设备未被访问时设备处于这种模式,从而不会导致总线上数据冲突。

SPI_Direction_1Line_Rx:3线,只接收。“单线”数据线在主设备端为MOSI引脚,在从设备端为MISO引脚。

SPI_Direction_1Line_Tx:3线,只发送。数据线同上。和SPI_Direction_1Line_Rx一起组成“单线双向”模式。

使用特权

评论回复
地板
SHOPQQ|  楼主 | 2023-6-28 17:27 | 只看该作者
该设置对应寄存器SPIn->CR1的bit10, bit14, bit15


使用特权

评论回复
5
SHOPQQ|  楼主 | 2023-6-28 19:11 | 只看该作者
2.2  SPI主从模式选择(SPI_Mode)
#define SPI_Mode_Master                 ((uint16_t)0x0104)
#define SPI_Mode_Slave                  ((uint16_t)0x0000)
————————————————


使用特权

评论回复
6
SHOPQQ|  楼主 | 2023-6-28 19:11 | 只看该作者
位9和位8需要组合使用,这里位9都为0,因此SPI_Mode_Maste定义为0x0104不是太理解。

使用特权

评论回复
7
SHOPQQ|  楼主 | 2023-6-28 19:11 | 只看该作者
2.3 SPI数据格式(SPI_DataSize)
#define SPI_DataSize_16b                ((uint16_t)0x0800)
#define SPI_DataSize_8b                 ((uint16_t)0x0000)

使用特权

评论回复
8
SHOPQQ|  楼主 | 2023-6-28 19:12 | 只看该作者
2.4 SPI模式(SPI_CPOL和SPI_CPHA)



模式0: SPI_CPOL_Low,SPI_CPHA_1Edge

模式1: SPI_CPOL_Low,SPI_CPHA_2Edge

模式2: SPI_CPOL_High,SPI_CPHA_1Edge

模式3: SPI_CPOL_High,SPI_CPHA_2Edge

使用特权

评论回复
9
SHOPQQ|  楼主 | 2023-6-28 19:13 | 只看该作者
2.5 SPI NSS控制方式(SPI_NSS)
#define SPI_NSS_Soft                    ((uint16_t)0x0200)
#define SPI_NSS_Hard                    ((uint16_t)0x0000)
对应寄存器CR1的位9



p; 对于SPI Master方式来说,这个NSS控制方式无效,CS脚需要使用GPIO的方式控制。

使用特权

评论回复
10
SHOPQQ|  楼主 | 2023-6-28 19:14 | 只看该作者
2.6  SPI频率设置(SPI_BaudRatePrescaler)
#define SPI_BaudRatePrescaler_2         ((uint16_t)0x0000)
#define SPI_BaudRatePrescaler_4         ((uint16_t)0x0008)
#define SPI_BaudRatePrescaler_8         ((uint16_t)0x0010)
#define SPI_BaudRatePrescaler_16        ((uint16_t)0x0018)
#define SPI_BaudRatePrescaler_32        ((uint16_t)0x0020)
#define SPI_BaudRatePrescaler_64        ((uint16_t)0x0028)
#define SPI_BaudRatePrescaler_128       ((uint16_t)0x0030)
#define SPI_BaudRatePrescaler_256       ((uint16_t)0x0038)

使用特权

评论回复
11
SHOPQQ|  楼主 | 2023-6-28 19:14 | 只看该作者
对应寄存器CR1的位5:3



PCLK是提供给低速总线APB的时钟信号; STM32F10x有PCLK1(72MHz)和PCLK2(36MHz),而SPI1使用的是PCLK2,SPI2、SPI3使用的是PCLK1。即SPI1的最高频率是18MHz,而SPI2、SPI3的最高频率是36MHz。

使用特权

评论回复
12
SHOPQQ|  楼主 | 2023-6-28 19:15 | 只看该作者
SPI帧模式设置(SPI_FirstBit)
#define SPI_FirstBit_MSB                ((uint16_t)0x0000)
#define SPI_FirstBit_LSB                ((uint16_t)0x0080)
对应寄存器CR1的位7

使用特权

评论回复
13
SHOPQQ|  楼主 | 2023-6-28 19:15 | 只看该作者
CRC设置(SPI_CRCPolynomial)
对应CRCPR寄存器
这里没意义,没有使用CRC功能。z

使用特权

评论回复
14
SHOPQQ|  楼主 | 2023-6-28 20:03 | 只看该作者
使能RCC
switch(port)
{
    case HW_SPI0:
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
        break;
    case HW_SPI1:
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
        break;
    //case HW_SPI2:
    //    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
    //    break;
    default:
        return;
}

使用特权

评论回复
15
SHOPQQ|  楼主 | 2023-6-28 20:03 | 只看该作者
读写
void spimTransferBytes(uint8_t port, uint8_t* wrBuf, uint8_t* rdBuf, uint16_t len)
{
    SPI_TypeDef* SPIx;
    uint16_t i;
    if(len == 0 || port >= HW_SPI_MAX)
        return;
    SPIx = spimGroup[port];
    while((SPIx->SR & SPI_I2S_FLAG_TXE) == 0);
    for(i = 0; i < len; i++)
    {
        SPIx->DR = (wrBuf != NULL) ? wrBuf[i] : 0xff;
        // Wait to receive a byte  
        while((SPIx->SR & SPI_I2S_FLAG_RXNE) == 0);
        if(rdBuf != NULL)
            rdBuf[i] = SPIx->DR;
        else
            SPIx->DR;
    }
}

使用特权

评论回复
16
SHOPQQ|  楼主 | 2023-6-28 20:03 | 只看该作者
SPI是全双工传输数据的,这里只用一个API函数来实现读写,当只读时,参数wrBuf为空指针,SPI上输出0xFF,而只写时,参数rdBuf为空指针,注意需要空读DR寄存器。

使用特权

评论回复
17
SHOPQQ|  楼主 | 2023-6-28 20:04 | 只看该作者
DMA
STM32F10x的SPI有3组,SPI1和SPI2使用DMA1,而SPI3使用DMA2。

使用特权

评论回复
18
SHOPQQ|  楼主 | 2023-6-28 20:04 | 只看该作者
使能DMA
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1EN,ENABLE);

SPI_I2S_DMACmd(SPIx,SPI_I2S_DMAReq_Tx,ENABLE);
SPI_I2S_DMACmd(SPIx,SPI_I2S_DMAReq_Rx,ENABLE);

使用特权

评论回复
19
SHOPQQ|  楼主 | 2023-6-28 20:04 | 只看该作者
读写函数
需要重写读写函数
void spimTransferBytes(uint8_t port, uint8_t* wrBuf, uint8_t* rdBuf, uint16_t len)
{
    SPI_TypeDef* SPIx;
    uint8_t spiBuf = 0;
    DMA_Channel_TypeDef * dmaChannel[] =
    {
        DMA1_Channel2, DMA1_Channel3, //SPI1 Rd, Wr DMA channel
        DMA1_Channel4, DMA1_Channel5, //SPI2 Rd, Wr DMA channel
    };
    uint8_t dmaIndex = 0;
    if(len == 0 || port >= HW_SPI_MAX)
        return;
    SPIx = spimGroup[port];
    if(SPIx == SPI2)
        dmaIndex = 2;
    dmaChannel[dmaIndex]->CPAR = (uint32_t)(&SPIx->DR);     //peripheral's address
    dmaChannel[dmaIndex]->CMAR = (uint32_t)rdBuf;           //memory's addrss
    dmaChannel[dmaIndex]->CNDTR = len ;                     //the length of transfer
    dmaChannel[dmaIndex]->CCR = (0 << 14) |                 //peripheral to memory
            (3 << 12) |                                     //the priority is highest.
            (0 << 11) |                                     //the width of memory transfer : 8bit
            (0 << 10) |
            (0 <<  9) |                                     //the width of peripheral transfer : 8bit
            (0 <<  8) |
            (1 <<  7) |                                     //the address of memory: increase
            (0 <<  6) |                                     //the address of peripheral: fixed
            (0 <<  5) |                                     //not circle mode
            (0 <<  4) |                                     //transfer direction: peripheral to memory
            (0 <<  3) |                                     //disable the interrupt of transfer error.
            (0 <<  2) |                                     //disable the interrupt of transfer half.
            (0 <<  1) |                                     //disable the interrupt of transfer complete.
            (0);                                            //Disable channel.
    if(rdBuf == NULL)
    {
        dmaChannel[dmaIndex]->CMAR = (uint32_t)&spiBuf;     //memory's addrss
        dmaChannel[dmaIndex]->CCR &= (~(1 <<  7));          //the address of memory: fixed
    }
    dmaChannel[dmaIndex + 1]->CPAR = (uint32_t)(&SPIx->DR);
    dmaChannel[dmaIndex + 1]->CNDTR = len;
    dmaChannel[dmaIndex + 1]->CMAR = (uint32_t)wrBuf;
    dmaChannel[dmaIndex + 1]->CCR = (0 << 14) |
        (3 << 12) |
        (0 << 11) |
        (0 << 10) |
        (0 <<  9) |
        (0 <<  8) |
        (1 <<  7) |
        (0 <<  6) |
        (0 <<  5) |
        (1 <<  4) |                                                 //transfer direction: memory to peripheral
        (0 <<  3) |
        (0 <<  2) |
        (0 <<  1) |
        (0);
    if(wrBuf == NULL)
    {
        dmaChannel[dmaIndex + 1]->CMAR = (uint32_t)&spiBuf;
        dmaChannel[dmaIndex + 1]->CCR &= (~(1 <<  7));
    }
    SPIx->DR;
    //Wait for DMA finish
    dmaChannel[dmaIndex]->CCR |= 0x01;
    dmaChannel[dmaIndex + 1]->CCR |= 0x01;
    while(dmaChannel[dmaIndex]->CNDTR); //Because the SPI is set to FullDuplex, so only check one channel.
    dmaChannel[dmaIndex]->CCR = 0x00;
    dmaChannel[dmaIndex + 1]->CCR = 0x00;
}

使用特权

评论回复
20
Clyde011| | 2024-1-23 11:24 | 只看该作者

然后使用铣削工具将孔与铜一起切成两半。

使用特权

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

本版积分规则

9

主题

183

帖子

0

粉丝