[STM32F1] STM32F10x之SPI Master

[复制链接]
993|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参数设置
  1. void spimCfg(SPI_TypeDef* SPIx, uint16_t prescaler)
  2. {
  3.     SPI_InitTypeDef  SPI_InitStructure;
  4.     SPI_Cmd(SPIx, DISABLE);
  5.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  6.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  7.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  8.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  9.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
  10.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  
  11.     SPI_InitStructure.SPI_BaudRatePrescaler = prescaler;//SPI_BaudRatePrescaler_256;
  12.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  13.     SPI_InitStructure.SPI_CRCPolynomial = 7;
  14.     SPI_Init(SPIx, &SPI_InitStructure);
  15.     SPI_Cmd(SPIx, ENABLE);
  16. }


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

15017649bfce76c43c.png
14754649bfcee00535.png
 楼主| 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)
————————————————
271649c153f41943.png
6001649c154748f23.png
49081649c155055e3f.png
 楼主| SHOPQQ 发表于 2023-6-28 19:11 | 显示全部楼层
位9和位8需要组合使用,这里位9都为0,因此SPI_Mode_Maste定义为0x0104不是太理解。
 楼主| 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)
51803649c15719df55.png
 楼主| SHOPQQ 发表于 2023-6-28 19:12 | 显示全部楼层
2.4 SPI模式(SPI_CPOL和SPI_CPHA)

95110649c1585d7548.png

模式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
 楼主| 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
61712649c15a85663f.png

23866649c15b5b3c84.png
p; 对于SPI Master方式来说,这个NSS控制方式无效,CS脚需要使用GPIO的方式控制。
 楼主| SHOPQQ 发表于 2023-6-28 19:14 | 显示全部楼层
2.6  SPI频率设置(SPI_BaudRatePrescaler)
  1. #define SPI_BaudRatePrescaler_2         ((uint16_t)0x0000)
  2. #define SPI_BaudRatePrescaler_4         ((uint16_t)0x0008)
  3. #define SPI_BaudRatePrescaler_8         ((uint16_t)0x0010)
  4. #define SPI_BaudRatePrescaler_16        ((uint16_t)0x0018)
  5. #define SPI_BaudRatePrescaler_32        ((uint16_t)0x0020)
  6. #define SPI_BaudRatePrescaler_64        ((uint16_t)0x0028)
  7. #define SPI_BaudRatePrescaler_128       ((uint16_t)0x0030)
  8. #define SPI_BaudRatePrescaler_256       ((uint16_t)0x0038)
 楼主| SHOPQQ 发表于 2023-6-28 19:14 | 显示全部楼层
对应寄存器CR1的位5:3


13174649c162203261.png
PCLK是提供给低速总线APB的时钟信号; STM32F10x有PCLK1(72MHz)和PCLK2(36MHz),而SPI1使用的是PCLK2,SPI2、SPI3使用的是PCLK1。即SPI1的最高频率是18MHz,而SPI2、SPI3的最高频率是36MHz。
 楼主| 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

26292649c1636808a3.png
 楼主| SHOPQQ 发表于 2023-6-28 19:15 | 显示全部楼层
CRC设置(SPI_CRCPolynomial)
对应CRCPR寄存器 92748649c1657b6da2.png
这里没意义,没有使用CRC功能。z
 楼主| SHOPQQ 发表于 2023-6-28 20:03 | 显示全部楼层
使能RCC
  1. switch(port)
  2. {
  3.     case HW_SPI0:
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
  5.         break;
  6.     case HW_SPI1:
  7.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  8.         break;
  9.     //case HW_SPI2:
  10.     //    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
  11.     //    break;
  12.     default:
  13.         return;
  14. }
 楼主| SHOPQQ 发表于 2023-6-28 20:03 | 显示全部楼层
读写
  1. void spimTransferBytes(uint8_t port, uint8_t* wrBuf, uint8_t* rdBuf, uint16_t len)
  2. {
  3.     SPI_TypeDef* SPIx;
  4.     uint16_t i;
  5.     if(len == 0 || port >= HW_SPI_MAX)
  6.         return;
  7.     SPIx = spimGroup[port];
  8.     while((SPIx->SR & SPI_I2S_FLAG_TXE) == 0);
  9.     for(i = 0; i < len; i++)
  10.     {
  11.         SPIx->DR = (wrBuf != NULL) ? wrBuf[i] : 0xff;
  12.         // Wait to receive a byte  
  13.         while((SPIx->SR & SPI_I2S_FLAG_RXNE) == 0);
  14.         if(rdBuf != NULL)
  15.             rdBuf[i] = SPIx->DR;
  16.         else
  17.             SPIx->DR;
  18.     }
  19. }
 楼主| SHOPQQ 发表于 2023-6-28 20:03 | 显示全部楼层
SPI是全双工传输数据的,这里只用一个API函数来实现读写,当只读时,参数wrBuf为空指针,SPI上输出0xFF,而只写时,参数rdBuf为空指针,注意需要空读DR寄存器。
 楼主| SHOPQQ 发表于 2023-6-28 20:04 | 显示全部楼层
DMA
STM32F10x的SPI有3组,SPI1和SPI2使用DMA1,而SPI3使用DMA2。
 楼主| SHOPQQ 发表于 2023-6-28 20:04 | 显示全部楼层
使能DMA
  1. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  2. RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1EN,ENABLE);

  3. SPI_I2S_DMACmd(SPIx,SPI_I2S_DMAReq_Tx,ENABLE);
  4. SPI_I2S_DMACmd(SPIx,SPI_I2S_DMAReq_Rx,ENABLE);
 楼主| SHOPQQ 发表于 2023-6-28 20:04 | 显示全部楼层
读写函数
需要重写读写函数
  1. void spimTransferBytes(uint8_t port, uint8_t* wrBuf, uint8_t* rdBuf, uint16_t len)
  2. {
  3.     SPI_TypeDef* SPIx;
  4.     uint8_t spiBuf = 0;
  5.     DMA_Channel_TypeDef * dmaChannel[] =
  6.     {
  7.         DMA1_Channel2, DMA1_Channel3, //SPI1 Rd, Wr DMA channel
  8.         DMA1_Channel4, DMA1_Channel5, //SPI2 Rd, Wr DMA channel
  9.     };
  10.     uint8_t dmaIndex = 0;
  11.     if(len == 0 || port >= HW_SPI_MAX)
  12.         return;
  13.     SPIx = spimGroup[port];
  14.     if(SPIx == SPI2)
  15.         dmaIndex = 2;
  16.     dmaChannel[dmaIndex]->CPAR = (uint32_t)(&SPIx->DR);     //peripheral's address
  17.     dmaChannel[dmaIndex]->CMAR = (uint32_t)rdBuf;           //memory's addrss
  18.     dmaChannel[dmaIndex]->CNDTR = len ;                     //the length of transfer
  19.     dmaChannel[dmaIndex]->CCR = (0 << 14) |                 //peripheral to memory
  20.             (3 << 12) |                                     //the priority is highest.
  21.             (0 << 11) |                                     //the width of memory transfer : 8bit
  22.             (0 << 10) |
  23.             (0 <<  9) |                                     //the width of peripheral transfer : 8bit
  24.             (0 <<  8) |
  25.             (1 <<  7) |                                     //the address of memory: increase
  26.             (0 <<  6) |                                     //the address of peripheral: fixed
  27.             (0 <<  5) |                                     //not circle mode
  28.             (0 <<  4) |                                     //transfer direction: peripheral to memory
  29.             (0 <<  3) |                                     //disable the interrupt of transfer error.
  30.             (0 <<  2) |                                     //disable the interrupt of transfer half.
  31.             (0 <<  1) |                                     //disable the interrupt of transfer complete.
  32.             (0);                                            //Disable channel.
  33.     if(rdBuf == NULL)
  34.     {
  35.         dmaChannel[dmaIndex]->CMAR = (uint32_t)&spiBuf;     //memory's addrss
  36.         dmaChannel[dmaIndex]->CCR &= (~(1 <<  7));          //the address of memory: fixed
  37.     }
  38.     dmaChannel[dmaIndex + 1]->CPAR = (uint32_t)(&SPIx->DR);
  39.     dmaChannel[dmaIndex + 1]->CNDTR = len;
  40.     dmaChannel[dmaIndex + 1]->CMAR = (uint32_t)wrBuf;
  41.     dmaChannel[dmaIndex + 1]->CCR = (0 << 14) |
  42.         (3 << 12) |
  43.         (0 << 11) |
  44.         (0 << 10) |
  45.         (0 <<  9) |
  46.         (0 <<  8) |
  47.         (1 <<  7) |
  48.         (0 <<  6) |
  49.         (0 <<  5) |
  50.         (1 <<  4) |                                                 //transfer direction: memory to peripheral
  51.         (0 <<  3) |
  52.         (0 <<  2) |
  53.         (0 <<  1) |
  54.         (0);
  55.     if(wrBuf == NULL)
  56.     {
  57.         dmaChannel[dmaIndex + 1]->CMAR = (uint32_t)&spiBuf;
  58.         dmaChannel[dmaIndex + 1]->CCR &= (~(1 <<  7));
  59.     }
  60.     SPIx->DR;
  61.     //Wait for DMA finish
  62.     dmaChannel[dmaIndex]->CCR |= 0x01;
  63.     dmaChannel[dmaIndex + 1]->CCR |= 0x01;
  64.     while(dmaChannel[dmaIndex]->CNDTR); //Because the SPI is set to FullDuplex, so only check one channel.
  65.     dmaChannel[dmaIndex]->CCR = 0x00;
  66.     dmaChannel[dmaIndex + 1]->CCR = 0x00;
  67. }
Clyde011 发表于 2024-1-23 11:24 | 显示全部楼层

然后使用铣削工具将孔与铜一起切成两半。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

183

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部