@[两块STM32之间 SPI DMA通信]

这里讲的是两块STM32F407板子的互相通讯,折腾了3天,终于比较清楚了,特此记录。
两块板子互相通讯,除了SPI的接线,肯定还有其他信号线,必须注意这些信号线的上拉下拉,输出口,最好配置为上拉,输入口,直接不拉。
我遇到,一边输入口,一边输出口,2边都配成下拉,接口当输出口输出时,电压只有2.01V,然后通信通不起,把我整了很久。其实想想原因很简单,当输出时,内部电路给电,然后两边的下拉电阻相当于并联对地(下拉电阻相当于减小一般),然后STM32内部向上驱动力不足,导致对方检测不到输入。所以看来STM32,能用上拉电阻就用上拉,使用时向下的驱动强些。

下面进入正题,直接上代码,都是在用的。
首先,硬件连接方式,
----- 主机 --------------------------------------从机
NSS(PB12) --------------------------NSS(PB12)
SPI_SCK ---------------------------------SPI_SCK
SPI_MISO--------------------------------SPI_MISO
SPI_MOSI---------------------------------SPI_MOSI
PDin(6)-------------------<-----------PCout(0) //含义 1 = 从机初始化完成了,开始吧。
PDout(10)-------------->--------------PCin(4) //含义 1 = 主机要发送主机了

//NSS引脚,其实可以不用。我已经测试,在用的时候和不用的时候,实际没有区别。

先上从机代码
//从机SPI初始化
void SPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2时钟
//GPIOFB13,14,15初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;//PB12复用NSS
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
//GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//
//GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
//GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_SPI2); //PB13复用为 SPI2 (这里是注释点了的,注释过后,其实NSS引脚也就没用了)
GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2); //PB14复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15复用为 SPI2
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave ; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //设置SPI的数据大小:SPI发送接收16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为低电平,这个高低电平,必须配合时钟线来,我这里时钟线是PB13,是采用的下拉
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样,这个跳变沿,和主机一样。
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制,这个选为软件,硬件,似乎,没有什么影响。网上有人说,还是选为硬件,他的解释是,要通信的时候,主机先发送片选命令,就是NSS引脚置低(当为从机时,NSS引脚拉低就表示选中了这个芯片,当为主机时,NSS引脚拉高就表示选中了这个芯片),这样让从机提前一点点知道要发送数据了,SPI外设就自动提前准备好,表面上一看比较有道理。我的理解是,如果设置为NSS软件管理,那么这个芯片是否被片选,就由SPI_InitStructure.SPI_Mode = SPI_Mode_Slave 这个来决定的,那么说白了,芯片只要一初始化,SPI外设就随时都初始化好了,只要在至收发数据期间,没有干扰,就没为题。所以,这个选为硬件,不是那么必要。
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定义波特率预分频的值:波特率预分频值为4
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx ,ENABLE); //使能SPI2 DMA功能
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}

//从机SPI读写函数
//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}

// 从机 DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量
//Ddtd:存储器到外设模式DMA_DIR_MemoryToPeripheral ,外设到存储器DMA_DIR_PeripheralToMemory,
// DMA_DIR_MemoryToMemory 内存到内存
void MYDMA_Config(DMA_Stream_TypeDef DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr,u32 Ddtd)(这是个通用函数,主机也是这个函数,只是参数不一样
{
DMA_InitTypeDef DMA_InitStructure;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
}else
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
}
DMA_DeInit(DMA_Streamx);
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置
/ 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器0地址
DMA_InitStructure.DMA_DIR = Ddtd;//存储器到外设模式DMA_DIR_MemoryToPeripheral ,外设到存储器DMA_DIR_PeripheralToMemory
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据长度:16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器数据长度:16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream
}

// 从机 开启一次DMA传输
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)(这是个通用函数,主机也是这个函数,只是参数不一样
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA传输
}

//从机 接收数据的函数,主函数调用就行
u32 DataDranInit_Check(void)
{
u16 l;
SPI2_Init(); //第一步,SPI初始化,SPI已使能
MYDMA_Config(DMA1_Stream3,DMA_Channel_0,(u32)&SPI2->DR,(u32)test_8,10,DMA_DIR_PeripheralToMemory);
//Delay_ms(500);
l = SPI2->DR;//第三步,这一步相当重要,我们这个是从机的函数。
//从机的SPI设置为了从站模式,并且上面第一步已经使能,然后由于可能主机还在初始化,在其初始化过程中,有可能SPI时钟线会产生震荡,导致从机SPI->SP寄存器之RXNE位置1,最终导致下面DMA开启的时候发生错位。
//作为从机来讲,SPI外设与DMA外设的通信工作过程就是,SPI收到信号,产生RXNE标志位,DMA收到该标志位,就进行一次数据传输。
//第三部的操作,就是读SPI数据寄存器的值,将自动复位RXNE标志。相当于有些人说的对SPI->DR的清零。

ReData:
DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_TCIF3 | DMA_FLAG_HTIF3 | DMA_FLAG_TEIF3 | DMA_FLAG_DMEIF3 | DMA_FLAG_FEIF3 );//清除DMA的标志位,必须要清除这些标志位,才能重新对DMA进行使能,DMA才会重启。光关闭DMA使能,再开DMA使能,DMA是不会重启的。
PDout(0)=0; //告诉对方初始化完成
while(PDin(4)==1){} //等待对方的信号
MYDMA_Enable(DMA1_Stream3,10); //检测到响应数据,转SPI DMA,接收数据
while(DMA1_Stream3->NDTR){} //等待传输完成
PDout(0)=1; //复位初始化信号
for(i=0; i<10; i++)
{ printf("\n–%d—%d----%d—\n",i,test_8[i],DMA1_Stream3->NDTR);}
Delay_ms(30);
goto ReData; }//循环采集数据。这个只是测试程序,所以这样做的,个人根据实际情况修改

下面是主机的函数
//主机 SPI初始化
void SPI2_Tx_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2时钟
//GPIOFB13,14,15初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;//
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;//
//GPIO_InitStructure.GPIO_OType = GPIO_OType_PP ;//推挽输出
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz ;//100MHz
//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;//上拉
//GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2); //PB14复用为 SPI2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15复用为 SPI2
//这里只针对SPI口初始化
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; //设置SPI工作模式:设置为从SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //设置SPI的数据大小:SPI发送接收16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定义波特率预分频的值:波特率预分频值为4
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE); //使能SPI2 DMA功能
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}
//主机 SPI2 读写一个字节(和从机是一样的
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}

//主机 DMAx的各通道配置 (和从机一样的
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量
//Ddtd:存储器到外设模式DMA_DIR_MemoryToPeripheral ,外设到存储器DMA_DIR_PeripheralToMemory,
// DMA_DIR_MemoryToMemory 内存到内存
void MYDMA_Config(DMA_Stream_TypeDef DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr,u32 Ddtd)
{
DMA_InitTypeDef DMA_InitStructure;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
}else
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
}
DMA_DeInit(DMA_Streamx);
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置
/
配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par; //DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar; //DMA 存储器0地址
DMA_InitStructure.DMA_DIR = Ddtd; //存储器到外设模式DMA_DIR_MemoryToPeripheral ,
//外设到存储器DMA_DIR_PeripheralToMemory
DMA_InitStructure.DMA_BufferSize = ndtr; //数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据长度:16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据长度:16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure); //初始化DMA Stream
}
//(主机)开启一次DMA传输(和从机一样的
//DMA_Streamx:DMA数据流,DMA1_Stream07/DMA2_Stream07
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA传输
}

u16 test_8[37]={40960,53,46,288,72,6,97,8,91,130,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37}; //主机的 输入参数

//主机,SPI DMA发送数据的程序
void DataDranInit_Check(void)
{
SPI2_Tx_Init(); //使能SPI为从,开启SPI2发送数据的DMA,等待发送数据
//配置DMA为发送数据模式
MYDMA_Config(DMA1_Stream4,DMA_Channel_0,(u32)&SPI2->DR,(u32)test_8,10,DMA_DIR_MemoryToPeripheral);
//Delay_ms(200);
PBout(12)= 1 ;
PCout(10)=1; //告诉对方,可以读取了。
sendData:
if ( PCin(6) == 0 ) //等待对方初始化完成的信号
{
//Delay_us(10); //合理延时,等待对方响应
//这个延时可以不要,只是做测试时加在这的,我不删,是如果不行,给你提供的思路。。。
if(test_8[1]>32767)
test_8[1]=1;
test_8[1]++;//这只是让发送的数据变化,测试时,好知道传输没断,是一直在传而已。
DMA_ClearFlag(DMA1_Stream4, DMA_FLAG_TCIF4 | DMA_FLAG_HTIF4 | DMA_FLAG_TEIF4 | DMA_FLAG_DMEIF4 | DMA_FLAG_FEIF4 ); //必须清数据位,否则DMA根本不会重启。
PCout(10)=0; //告诉对方,主机准备好了,要发送开始数据了。
Delay_us(4); //这个延时很随意,酌情。。。
PBout(12)= 0 ; //使能NSS,片选从机,从机如果是Soft就不用这个了。
Delay_us(1); //这个延时很随意,酌情。。。
MYDMA_Enable(DMA1_Stream4,10); //使能发送数据
while(DMA_GetCurrDataCounter(DMA1_Stream4)){}
PCout(10)=1; //发送完毕,复位。
Delay_ms(1); //这个延时很随意,酌情。。。
PBout(12)= 1 ; //恢复片选
aafd = DMA_GetCurrDataCounter(DMA1_Stream4);
Delay_ms(1);
}
goto sendData; //
}

STM32 的SPI DMA通讯,也不算坑吧,用着还好。
另外,所谓的 CPOL CHPA,就是SPI的四种模式,建议用逻辑分析仪看下,根据清空设置就行了,我这的话,反正主从设置为一样,我觉得应该都是一样就行了。

两块STM32之间 SPI DMA通信相关推荐

  1. 【嵌入式基础】STM32中断及DMA通信原理编程

    本文主要学习stm32中断.DMA通信原理和编程方法.使用stm32tubemx和HAL库分别完成中断模式编程和串口通信中断实验. 目录 一.STM32中断,DMA通信原理编程 1.STM32中断 ( ...

  2. STM32中断与DMA通信编程

    文章目录 一.中断与DMA通信原理 1.中断 2.DMA通信原理 二.中断控制LED灯点亮熄灭 1.新建工程 2.配置外部中断 3.配置中断优先级 4.完成创建 5.编写中断函数 6.程序烧录 7.运 ...

  3. 【嵌入式】STM32实现SPI双机通信的一些细节(2)片选总结

    [嵌入式]STM32实现SPI双机通信的一些细节(2)SPI软硬件片选总结 SPI片选总结 坑爹的手册示意图 理想中的硬件片选 主机软件片选就对了 从机硬件片选 从机软件片选 代码 主机软件片选SPI ...

  4. 两块STM32F1之间互相通信(串口)

    首先准备两块STM32F103的板子,以我这个为例,我准备了一块STM32F103和CH32F103最小系统板子,其他杜邦线.下载器及接线方法以及通信原理不再多说.这里我用的是STM32F103最小系 ...

  5. 【STM32】SPI协议通信详解

    目录 一.SPI协议简介 二.SPI物理层 三.SPI协议层 1.通讯的起始和停止信号 2.数据有效性 3.时钟信号的相位和极性(重点) 四.SPI 特性及架构(重点) 1.通信引脚 2.时钟控制逻辑 ...

  6. 和12岁小同志搞创客开发:两个控制器之间如何实现通信?

    目录 1.有线通信 2.无线通信 3.串口点灯 机缘巧合在网上认识一位12岁小同志,从零开始系统辅导其创客开发思维和技巧. ​​​项目专栏:https://blog.csdn.net/m0_38106 ...

  7. Mixly使用两块UNO使用Arduino Json通信(Arduino Json库用V6版本)

    一.JSON简介 JSON(JavaScript Object Notation) 是一种通用的轻量级数据交换文本格式.它很容易让人阅读和编写,也便于机器进行解析和生成.它使用JavaScript语法 ...

  8. STM32双机SPI全双工通信

    (基于STM32F407的SPI全双工通信时序不同步问题!!) 首先吐槽一波,调一个星期的SPI,始终没有很好的效果. 网上有很多SPI主从通信的例子,但是两片STM32单片机进行通信,基本很少,就算 ...

  9. GD32E230 SPI DMA通信(读取传感器数据)

    一场疫情让公司的生意越来越好,忙得不可开交,产品大卖特卖.结果ST的单片机开始出现交期不稳定,供货慢,价格翻倍.无奈之下只好从国产单片机下手.于是就有了我的ST-GD的代码移植. 目录 一.SPI初始 ...

最新文章

  1. 乐意使人恐惧,源于自身的空虚
  2. ubuntu 12.04/11.10 PPA 安装 Nvidia 295.59
  3. Java I/O演进与Linux网络I/O模型
  4. 解决原子性问题?你首先需要的是宏观理解
  5. python常见错误-新手常见Python错误及异常解决处理方案
  6. php $globa作用是l,php 关键字global在定义变量中的作用
  7. 线程阻塞和挂起(网络收集)
  8. Nginx—— Rewrite规则的使用
  9. GAN-代码实现资料整合(1)
  10. 吴恩达深度学习 —— 2.8 计算图的导数计算
  11. svc的参考文献_WCF服务三:svc文件详解
  12. haproxy高可用
  13. 嵌入式开发 ARM Cortex-M3处理器技术优势分析
  14. windows98 java_在win98下安装JSP环境
  15. python身份证号码共18位_用Python写一个身份证号码校验工具
  16. Html开屏广告源码,开屏(Splash)广告样式
  17. 5G消息来了,它会干掉微信还是变成另一个飞信?
  18. java读取指定位置arraylist,获取元素在Java ArrayList中的位置
  19. 对百度Bingo算法的猜测
  20. vb.net电子时钟实例

热门文章

  1. 软件测试之如何做好回归测试
  2. Python图像处理丨详解图像去雾处理方法
  3. 怎样去掉“交互式服务对话框检测”提示对话框
  4. Pycharm 金融Python实战二:用Python编写一个金融计算器——编写函数 调用命令 实例年金现值 利率换算 净现值法 投资回报期 内部收益率及其法则(带程序和结果)
  5. R统计笔记(二):投影运算与转换
  6. ntfs磁盘 0字节 0byte 文件目录损坏且无法读取 RAW chkdsk失败
  7. 苹果服务器维护时间表2019,ios 内购详解(2019)
  8. 转载:浅谈Session与Cookie的区别与联系
  9. 5.5 jmeter组件—取样器
  10. 常见的http状态消息