完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
应老师的要求,一个STM32只负责数据监测,另一个STM32只负责数据收发,它们之间通过SPI进行通信。
两个STM32之间的SPI通信跟SPI——FLASH还是有极大区别的。 SPI的双机通信我走了不少弯路,犯了很多错误,愣是搞了差不多一星期。 先吐槽一下: SPI的双向全双工模式是假的全双工,从机还是受主机支配 对于两个双向通行频繁的设备不建议使用SPI,因为无论如何从机都要受到主机的影响,真的是很配 slave 这个称呼 我的程序是主机负责向从机发送数据(num,data),从机收到后在中断中处理 按下主机的按键1,发数据,从机随手回复发来的数据,从机没人权!!! 连线方式 SCK----SCK MOSI----MOSI MISO----MISO NSS----NSS 废话不多说,直接放我的库函数。 主机部分: bsp_spi.h #ifndef _BSP_SPI_H_ #define _BSP_SPI_H_ #include "stm32f10x.h" #include #define SPI_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define SPI_GPIO_CLK RCC_APB2Periph_GPIOA #define SPI_MOSI_GPIO_PORT GPIOA #define SPI_MOSI_GPIO_PIN GPIO_Pin_7 #define SPI_MISO_GPIO_PORT GPIOA #define SPI_MISO_GPIO_PIN GPIO_Pin_6 #define SPI_SCK_GPIO_PORT GPIOA #define SPI_SCK_GPIO_PIN GPIO_Pin_5 #define SPI_APBxClkCmd RCC_APB2PeriphClockCmd #define SPI_CLK RCC_APB2Periph_SPI1 #define SPIX SPI1 #define SPI_CS_PORT GPIOA #define SPI_CS_PIN GPIO_Pin_4 #define SPIX_IRQ SPI1_IRQn #define SPIX_IRQHandler SPI1_IRQHandler //选择flash #define SPI_CS_HIGH GPIO_SetBits(SPI_CS_PORT,SPI_CS_PIN); #define SPI_CS_LOW GPIO_ResetBits(SPI_CS_PORT,SPI_CS_PIN); /*等待超时时间*/ #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000) #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT)) /*信息输出*/ #define DEBUG_ON 1 #define INFO(fmt,arg...) printf("<<-INFO->> "fmt"n",##arg) #define ERROR(fmt,arg...) printf("<<-ERROR->> "fmt"n",##arg) #define DEBUG(fmt,arg...) do{ if(DEBUG_ON) printf("<<-DEBUG->> [%s][%d]"fmt"n",__FILE__,__LINE__, ##arg); }while(0) void SPI_M_M_Init(void); uint8_t SPI_Send_Byte(u16 data); uint8_t SPI_Read_Byte(void); uint8_t SPI_Send_Data(u16 num,u16 data); #endif // _BSP_SPI_H_ bsp_spi.c #include "bsp_spi.h" #include static __IO uint32_t TimeOut = SPIT_LONG_TIMEOUT; static void SPI_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; //开端口时钟 SPI_GPIO_APBxClkCmd(SPI_GPIO_CLK,ENABLE); //配置MOSI端口 GPIO_InitStructure.GPIO_Pin = SPI_MOSI_GPIO_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(SPI_MOSI_GPIO_PORT,&GPIO_InitStructure); //配置MISO端口 GPIO_InitStructure.GPIO_Pin = SPI_MISO_GPIO_PIN; //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SPI_MISO_GPIO_PORT,&GPIO_InitStructure); //配置SCK端口 GPIO_InitStructure.GPIO_Pin = SPI_SCK_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(SPI_SCK_GPIO_PORT,&GPIO_InitStructure); //配置CS端口 GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(SPI_CS_PORT,&GPIO_InitStructure); SPI_CS_HIGH; } static void SPI_Config(void) { SPI_InitTypeDef SPI_InitStructure; //开SPI时钟 SPI_APBxClkCmd(SPI_CLK,ENABLE); //配置SPI结构体 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CRCPolynomial = 0; SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主机模式 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //开启接收中断 //SPI_I2S_ITConfig(SPIX, SPI_I2S_IT_RXNE, ENABLE); SPI_Init(SPIX,&SPI_InitStructure); SPI_Cmd(SPIX,ENABLE); } static void SPI_NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = SPIX_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //SPI_FLASH初始化函数 void SPI_M_M_Init(void) { SPI_GPIO_Config(); SPI_Config(); SPI_NVIC_Configuration(); } //SPI 发送一个字节,接收一个字节 //实际上主机不需要等待从机发送,因为只有在主机发送时,从机才会发送 发送缓冲区 中的东西 uint8_t SPI_Send_Byte(u16 data) { TimeOut = SPIT_LONG_TIMEOUT; while(SPI_I2S_GetFlagStatus(SPIX,SPI_I2S_FLAG_TXE) == RESET)//等待发送完毕 { if(TimeOut-- == 0){DEBUG("发送超时");return 0;} } SPI_I2S_SendData(SPIX,data); TimeOut = SPIT_LONG_TIMEOUT; while(SPI_I2S_GetFlagStatus(SPIX,SPI_I2S_FLAG_RXNE) == RESET)//等待接收完毕 { if(TimeOut-- == 0){DEBUG("接收超时");return 0;} } return SPI_I2S_ReceiveData(SPIX); } //这是一个延时函数 void delay(u32 n) { while(n--); } //向另一个mcu发送数据 //num 位置 data 数据 //正常 return 0 异常return 1 uint8_t SPI_Send_Data(u16 num,u16 data) { uint16_t temp = 0; printf("发送 num:%d data:%dn",num,data); SPI_CS_LOW;//拉低CS SPI_Send_Byte(num); //加延时是因为从机使用了中断处理数据,相对于SPI来说 //中断太慢了,所以不加延时函数无法让从机及时回复 //不过我让从机回复只是为了证明可以双向通行罢了,如果只是发送,完全可以把延时删掉 delay(0xffff); temp = SPI_Send_Byte(data); if(temp != num) { printf("num err :%dn",temp); SPI_CS_HIGH;//拉高CS return 1; } delay(0xffff); temp = SPI_Send_Byte(0xff); if(temp != data) { printf("data err :%dn",temp); SPI_CS_HIGH;//拉高CS return 1; } SPI_CS_HIGH;//拉高CS return 0; 主机main函数 int main(void) { int i = 0; int n = 0; SPI_M_M_Init(); USART_Config(); Key_Configer(); printf("开机!!!n"); while(1) { if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN)) { i++; if(i>3) { i = 0; } if(0 == SPI_Send_Data(i,i+n)) { printf("通信正常 %d,%dn",i,i+n); }else { printf("通信异常n"); } n++; } if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN)) { printf("(||—_—)n"); } } } 从机部分 bsp_spi.h #ifndef _BSP_SPI_H_ #define _BSP_SPI_H_ #include "stm32f10x.h" #include #define SPI_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define SPI_GPIO_CLK RCC_APB2Periph_GPIOA #define SPI_MOSI_GPIO_PORT GPIOA #define SPI_MOSI_GPIO_PIN GPIO_Pin_7 #define SPI_MISO_GPIO_PORT GPIOA #define SPI_MISO_GPIO_PIN GPIO_Pin_6 #define SPI_SCK_GPIO_PORT GPIOA #define SPI_SCK_GPIO_PIN GPIO_Pin_5 #define SPI_APBxClkCmd RCC_APB2PeriphClockCmd #define SPI_CLK RCC_APB2Periph_SPI1 #define SPIX SPI1 #define SPI_CS_PORT GPIOA #define SPI_CS_PIN GPIO_Pin_4 #define SPIX_IRQ SPI1_IRQn #define SPIX_IRQHandler SPI1_IRQHandler //选择flash #define SPI_CS_HIGH GPIO_SetBits(SPI_CS_PORT,SPI_CS_PIN); #define SPI_CS_LOW GPIO_ResetBits(SPI_CS_PORT,SPI_CS_PIN); /*等待超时时间*/ #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000) #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT)) /*信息输出*/ #define DEBUG_ON 1 #define INFO(fmt,arg...) printf("<<-INFO->> "fmt"n",##arg) #define ERROR(fmt,arg...) printf("<<-ERROR->> "fmt"n",##arg) #define DEBUG(fmt,arg...) do{ if(DEBUG_ON) printf("<<-DEBUG->> [%s][%d]"fmt"n",__FILE__,__LINE__, ##arg); }while(0) void SPI_M_M_Init(void); uint8_t SPI_Send_Byte(u16 data); uint8_t SPI_Read_Byte(void); #endif // _BSP_SPI_H_ bsp_spi.c #include "bsp_spi.h" #include uint16_t DataBuf[16] = {0}; static __IO uint32_t TimeOut = SPIT_LONG_TIMEOUT; static void SPI_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; //开端口时钟 SPI_GPIO_APBxClkCmd(SPI_GPIO_CLK,ENABLE); //配置MOSI端口 GPIO_InitStructure.GPIO_Pin = SPI_MOSI_GPIO_PIN; //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SPI_MOSI_GPIO_PORT,&GPIO_InitStructure); //配置MISO端口 GPIO_InitStructure.GPIO_Pin = SPI_MISO_GPIO_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//这个错误我能记一辈子!! GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(SPI_MISO_GPIO_PORT,&GPIO_InitStructure); //配置SCK端口 GPIO_InitStructure.GPIO_Pin = SPI_SCK_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SPI_SCK_GPIO_PORT,&GPIO_InitStructure); //配置CS端口 GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SPI_CS_PORT,&GPIO_InitStructure); } static void SPI_Config(void) { SPI_InitTypeDef SPI_InitStructure; //开SPI时钟 SPI_APBxClkCmd(SPI_CLK,ENABLE); //配置SPI结构体 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;//从机模式 SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;//能在NSS为高时忽略CLK,杜邦线连接信号不稳定 SPI_Init(SPIX,&SPI_InitStructure); //开启接收中断 SPI_I2S_ITConfig(SPIX, SPI_I2S_IT_RXNE, ENABLE); SPI_Cmd(SPIX,ENABLE); } static void SPI_NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = SPIX_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //SPI_FLASH初始化函数 void SPI_M_M_Init(void) { SPI_GPIO_Config(); SPI_Config(); SPI_NVIC_Configuration(); } //SPI 发送一个字节,接收一个字节 uint8_t SPI_Send_Byte(u16 data) { TimeOut = SPIT_LONG_TIMEOUT; while(SPI_I2S_GetFlagStatus(SPIX,SPI_I2S_FLAG_TXE) == RESET) { if(TimeOut-- == 0){DEBUG("发送超时");return 0;} } //printf("发送n"); SPI_I2S_SendData(SPIX,data);//把data写入SPIX->DR return SPI_I2S_ReceiveData(SPIX); } 从机的中断服务函数 //SPI中断服务函数 void SPIX_IRQHandler(void) { uint16_t data; //接收中断 if(SPI_I2S_GetITStatus(SPIX, SPI_I2S_IT_RXNE)) { //接收主机发来的数据 data = SPI_I2S_ReceiveData(SPIX); //打印一下 printf("data = %dn",data); if(data == 0xff) { return ; } //SPI_I2S_SendData(SPIX,data); SPIX->DR = data; //spi_num != -1说明这次发的是数据,写入数据 if(spi_num != -1 && data != 0xff) { if(data >= TmpDataBuf[spi_num]+10 || data <= TmpDataBuf[spi_num]-10) { TmpDataBuf[spi_num] = data; printf("TmpDataBuf[%d] = %dn",spi_num,data); }else { printf("data变化太小--%dn",TmpDataBuf[spi_num]-data); } } //spi_num == -1,发的是位置,判断是否在0~15之间 if(spi_num == -1 && data <= 15) { spi_num = data;//记录位置 }else { spi_num = -1; } //不用清除中断标志位,读取数据时就清过了 //而且没有办法用软件清除RXNE标志,不信去看手册 } } 从机的main函数 uint16_t TmpDataBuf[16]; void delay(int n) { while(n--); } int main(void) { SPI_M_M_Init(); USART_Config(); printf("开机!!!n"); while(1) { } } 有趣的是虽然我选择的模式叫双线全双工模式,但是 !!!!!从机永远无法主动联系主机!!!!! !!!!!从机永远无法主动联系主机!!!!! !!!!!从机永远无法主动联系主机!!!!! !!!!!从机永远无法主动联系主机!!!!! 唯一的方法是 从机在主机发来数据之前把要发送的数据填入DR,然后在主机发来数据的同时, 从机把之前在DR中的数据通过MOSI发送给主机。 通信永远是 主机发->从机发->主机发->从机发->主机发->…->从机发->主机发->从机发->主机发->从机发 发送缓冲区、接收缓冲区、DR里的东西不同, 比如主机发来了1,从机接收缓冲区里是1,DR里是1,发送缓冲区是0 主机再发东西时,从机同时回复了发送缓冲区中的0, 如果不向DR中进行写操作,发送缓冲区中的内容不变。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1632 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1559 浏览 1 评论
985 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
688 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1605 浏览 2 评论
1869浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
653浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
523浏览 3评论
540浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
509浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-25 15:26 , Processed in 0.809861 second(s), Total 78, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号