SPI+DMA

1.上一篇文章介绍的是PWM+DMA发送数据,这次来一个SPI+DMA发送数据的方式,巩固一下STM32的知识,之前没使用过STM32的SPI驱动方式,DMA也很少使用,不过DMA使用起来确实好,不占用CPU的资源,直接内存到外设。本次实验STM32F103的SPI+DMA功能,实验的模块还是WS2812灯带。

2

.STM32C8T6基本的资源介绍:

STM32F103x8和STM32F103xB增强型系列使用高性能的ARM® Cortex™-M3 32位的RISC内核,工

作频率为72MHz,内置高速存储器(高达128K字节的闪存和20K字节的SRAM),丰富的增强I/O端口

和联接到两条APB总线的外设。所有型号的器件都包含2个12位的ADC、3个通用16位定时器和1个

PWM定时器,还包含标准和先进的通信接口:多达2个I

2

C接口和SPI接口、3个USART接口、一个

USB接口和一个CAN接口。

3.

WS2812B:就是一个RGB灯带,只给信号,就量相应的颜色,理论上像素255*255*255种颜色,主要实验3原色显示。

4.下面是STM32F103配置SPI+DMA的方式,使用的标准库。我使用的是PA7,SPI_1,因为是驱动的是WS2812B灯带,主需要用到SPI1的MOSI引脚就行,不要配置其他的。再看一下DMA1对应SPI1_TX的通道,通过手册可以看出SPI1_TX在DMA1的3通道上。因为WS2812B的最大传输速度是800KHZ,所以SPI的速度要设置合理,不然数据可能会出错,导致灯带的颜色和顺序不一样。SPI1挂在的APB2上,系统时钟默认是72M,SPI_BaudRatePrescaler_8 八分频后是9M,发送一位的时间是111nu.然后对照一下WS2812B的0码和1码的时序图。

1码:1111 1110  =0x0fe     777ns

0码: 1110 000 =0xe0        333ns

第1个WS2812B灯珠接收到了第1个24bit的数据,做出响应(发光);第N个WS2812B灯珠接收到了第N个24bit的数据,

再收到第二个24bit的数据后,直接转发给第二个WS2812B灯珠,由第二个WS2812B灯珠做出响应;依次类推。

下面是STM32F103配置SPI1+DMA1的代码

void Ws2812b_SPI_Dma_GpioConfig(void)

{

   GPIO_InitTypeDef GPIO_InitStructure;

   SPI_InitTypeDef SPI_InitStructure;

   DMA_InitTypeDef DMA_InitStructure;

  

   RCC_APB2PeriphClockCmd(WS2812B_SPIx_GPIO_CLK, ENABLE); //PORTA时钟使能 

   RCC_APB2PeriphClockCmd(WS2812B_SPIx_CLK, ENABLE); //SPI1时钟使能 

   RCC_AHBPeriphClockCmd(WS2812B_SPIx_DMAx_CLK, ENABLE); //使能DMA传输

  

  

   /* PA7  SPI1_MOSI */

   GPIO_InitStructure.GPIO_Pin = WS2812B_SPIx_PIN;

   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA7复用推挽输出 SPI

   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

   GPIO_Init(WS2812B_SPIx_PORT, &GPIO_InitStructure);//初始化GPIOA

  

   SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

   SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI

   SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构

   SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为低电平

   SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;  //串行同步时钟的第2个跳变沿(上升或下降)数据被采样

   SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

   SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为9m

   SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始

   SPI_InitStructure.SPI_CRCPolynomial = 7;  //CRC值计算的多项式

   SPI_Init(WS2812B_SPIx, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

  

   SPI_Cmd(WS2812B_SPIx, ENABLE); //使能SPI外设

   SPI_I2S_DMACmd(WS2812B_SPIx, SPI_I2S_DMAReq_Tx, ENABLE);//启用SPI发送DMA请求

   

   DMA_DeInit(WS2812B_SPIx_DMAx_Channelx); //将DMA的通道1寄存器重设为缺省值

   DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(WS2812B_SPIx -> DR); //cpar;  //DMA外设ADC基地址

   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Pixel_Buff; //cmar;  //DMA内存基地址

   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设

   DMA_InitStructure.DMA_BufferSize = PIXEL_NUM * RGB; //DMA通道的DMA缓存的大小

   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变

   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增

   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位

   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位

   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式

   DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 

   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输

   DMA_Init(WS2812B_SPIx_DMAx_Channelx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器 

}

下面是驱动WS2812灯带的代码:

//缓存数据

u8 Pixel_Buff[PIXEL_NUM][RGB]={0};

void Ws281b_Show(void)

{

  DMA_SetCurrDataCounter(WS2812B_SPIx_DMAx_Channelx, (u16)(PIXEL_NUM * RGB));  //设定DMA要传输数据的大小 

  DMA_Cmd(WS2812B_SPIx_DMAx_Channelx, ENABLE);

  while(DMA_GetFlagStatus(WS2812B_SPIx_DMA_FLAG) != SET);    //等待传输完成

  DMA_ClearFlag(WS2812B_SPIx_DMA_FLAG);

  DMA_Cmd(WS2812B_SPIx_DMAx_Channelx, DISABLE);

WS2812B_PIN_L;

Delay_us(350);

}

void Ws2812b_CloseAll(void)//熄灭

{

  u16 i;

  u8 j;

  

  for(i = 0; i < PIXEL_NUM; ++i)

  {

    for(j = 0; j < 24; ++j)

    {

      Pixel_Buff[i][j] = T0H;

// printf("%x",Pixel_Buff[i][j]);

    }

// printf("\r\n");

  }

  Ws281b_Show(); //传输数据

}

/**

  * @brief  将RGB颜色组合到一个32位数据中存储

  * @param  red:  0-255

  *         green:0-255

  *         blue: 0-255

  * @retval None

  */

u32 ws281x_color(u8 r, u8 g, u8 b)

{

  return r << 16 | g << 8 | b;

}

/**

  * @brief  给特定LED灯设定颜色

  * @param  n:LED灯号

*         GRBClor: 32位的颜色值

  * @retval None

  */

void ws281x_setPixelColor(u16 n, uint32_t GRBColor)

{

  u8 i;

  if(n < PIXEL_NUM)

  {

    for(i = 0; i < RGB; i++)

    {

      Pixel_Buff[n][i] = ((GRBColor << i) & 0x800000) ? T1H : T0H;

   //printf(" %x ",Pixel_Buff[n][i]);

    }

// printf("\r\n");

  }

  Ws281b_Show(); //传输数据

}

把数据填充到数组里面,然后启动DMA发送信息就行了。我也是第一次使用改模式,有很多细节需要自己摸索。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值