Single chip microcomputer is purely a personal hobby , There are many deficiencies. Please give us more advice , Please forgive me for some irregular names in the code .
This paper only realizes the simple design of wireless module ( Can send and receive one byte of data ), Always wanted to diy A wireless remote control car , You need to use the wireless module , I've been looking for a long time and found NRF24L01( Hereinafter referred to as NRF) It is the cheapest wireless module ( Excepting WiFi And Bluetooth module ), Just bought a few , because stm32f103 Prices , I chose the cheap one stm32f030, I found a lot of information on the Internet for stm32f030 There is very little information about , He and stm32f103 The code is the same , Just try stm32f103 Modify it based on the code , Just can't communicate , Can only be sent successfully , Unable to receive data , Shelved for a long time, and finally chose a new piece HC-12 Wireless communication module , This module is more expensive. Buy a set for the first time ( Send and receive two modules ) Cheaper , The farthest communication distance of open field of vision 1 km ( No real measurements have been made ), He uses serial communication , After writing the code and burning it, you can communicate , Finally, success diy Wireless remote control car , utilize HC-12 I feel overqualified , Finally, I thought about this again when I had nothing to do NRF modular , Finally we can communicate with each other , I don't know what's wrong , The only difference is , The previous is to modify the information found on the Internet , No interrupt is used , Use only the while Cycle for detection , This time when I started writing again, I used interrupt , After debugging, you can communicate .
Some problems encountered :
1. On the schematic diagram PA4 yes SPI1 The movie selection of spi1_nss Reuse of , When configuring PA4 It is also configured as reuse mode , Find that you can't succeed , The need to configure the output mode solves the problem
2.NRF Of IRQ When the foot configuration is interrupted, it needs to be configured as a falling edge trigger
3.stm32 Boards and NRF When the module is connected, the data output and input lines cannot be cross connected (MCU Of MISO and NRF Of MISO Connected to a , MOSI Empathy )
Here's the code , Apply to stm32f030
1. spi To configure

#ifndef __bsp_spi_h #define __bsp_spi_h #include "stm32f0xx_gpio.h" #define SPIx SPI1 //SPI_1 #define SPI1_PORT GPIOA //PA port #define PORTA_LCK RCC_AHBPeriph_GPIOA //GPIO The clock #define SPI_LCK RCC_APB2Periph_SPI1//spi The clock #define SPI1_CSN GPIO_Pin_1 //PA1 NSS #define SPI1_SCK GPIO_Pin_5 //PA5 SCK #define SPI1_MISO GPIO_Pin_6 //PA6 MISO #define SPI1_MOSI GPIO_Pin_7 //PA7 MOSI void SPI_Config(void); u8 SPI_SendByte(u8 byte); void Pin_CSN(u8 u); #endif

#include "bsp_spi.h" #include "stm32f0xx_gpio.h" // initialization void SPI_Config() { GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; // Port initialization RCC_AHBPeriphClockCmd(PORTA_LCK , ENABLE);// Turn on GPIO The clock RCC_APB2PeriphClockCmd(SPI_LCK, ENABLE);// Turn on SPI_1 The clock // Reuse mode GPIO_PinAFConfig(SPI1_PORT,GPIO_PinSource5,GPIO_AF_0);//SCK GPIO_PinAFConfig(SPI1_PORT,GPIO_PinSource6,GPIO_AF_0);//MISO GPIO_PinAFConfig(SPI1_PORT,GPIO_PinSource7,GPIO_AF_0);//MOSI GPIO_InitStruct.GPIO_Pin = SPI1_SCK | SPI1_MISO | SPI1_MOSI; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(SPI1_PORT, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = SPI1_CSN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_Init(SPI1_PORT , &GPIO_InitStruct); //spi initialization //SPI_I2S_DeInit(SPIx); // Reset the register to the default value //SPI_Cmd(SPIx, DISABLE); //SPI_Direction_2Lines_FullDuplex SPI_Direction_1Line_Rx SPI_Direction_1Line_Tx SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI_Mode_Master host SPI_Mode_Slave Slave SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//SPI_CPOL_Low SPI_CPOL_High SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;//SPI_CPHA_1Edge SPI_CPHA_2Edge SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //SPI_FirstBit_MSB SPI_FirstBit_LSB SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStruct.SPI_CRCPolynomial = 7; SPI_Init(SPIx, &SPI_InitStruct); //SPI_I2S_IT_TXE SPI_I2S_IT_RXNE SPI_I2S_IT_ERR SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_TXE | SPI_I2S_IT_RXNE, ENABLE);// interrupt SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF); // important , Set the response data bit to 8 position SPI_Cmd(SPIx, ENABLE);// Can make } //SPI Send and receive a byte u8 SPI_SendByte(u8 byte) { // Set time overflow u32 SPITimeout = 0xffff; /* Wait for the send buffer to be empty ,TXE event */ while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET) { if ((SPITimeout--) == 0) return 0; } /* Write data register , Write the data to be written to the send buffer */ SPI_SendData8(SPIx, byte);//SPI_I2S_SendData16 // Set time overflow SPITimeout = 0xfffff; /* Waiting to receive buffer not empty ,RXNE event */ while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET) { if ((SPITimeout--) == 0) return 0; } /* Read data register , Get receive buffer data */ return SPI_ReceiveData8(SPIx); } // Set the chip selection high and low level void Pin_CSN(u8 u) { if(u==0) { SPI1_PORT->BRR = SPI1_CSN; } else { SPI1_PORT->BSRR = SPI1_CSN; } }
2.nrf To configure

#ifndef __bsp_nrf0241_h #define __bsp_nrf0241_h #include "stm32f0xx_gpio.h" #define NRF_PORT GPIOA //PA port #define KEY0 GPIO_Pin_0 //KEY0 #define LED0 GPIO_Pin_4 //LED0 #define NRF_CE GPIO_Pin_2 //PA2 CE #define NRF_IRQ GPIO_Pin_3 //PA3 IRQ #define NOP 0xFF // Empty operation . It can be used to read Status register // Set up ce Set high and pull low void Pin_CE(u8 u); // obtain IRQ Interrupt level ,( Not used ) u8 Get_IRQ(void); // Write data u8 SPI_WriteBuf(u8 reg, u8 *pBuf, u8 len); // Reading data u8 SPI_ReadBuf(u8 reg, u8 *pBuf, u8 len); // Read and write an instruction u8 SPI_RWReg(u8 reg, u8 value); // To configure void NRF_Config(void); // Get the level of the key ( Used to judge whether the key is pressed ) u8 Get_KEY0(void); // Configure test keys and LED The lamp void KEY0_LED0_Config(void); // Set up LED The lamp ( Low level on ) void Pin_LED0(u8 u); // send data buff For data ,len Number of void send_data(u8 *buff,u8 len); // testing NRF Whether the module exists There is returned 0 u8 nrf24l0_check(void); // Read status register , Used to judge whether the transmission is successful , Successful reception , The transmission reached the maximum value u8 Get_Status(void); // receive data void receive_data(void); // Get the received data buf Store the received data , len The number of data void Get_Data(u8 *buf,u8 len); // Clear all ( send out , receive , Maximum number of transmissions ) The interrupt void ClearStatus(void); #endif

#include "bsp_nrf0241.h" #include "stm32f0xx_gpio.h" #include "bsp_spi.h" #include "delay.h" void KEY0_LED0_Config() { GPIO_InitTypeDef GPIO_InitStruct; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; RCC_AHBPeriphClockCmd(PORTA_LCK , ENABLE); // User tested keys and LED The lamp GPIO_InitStruct.GPIO_Pin = KEY0;// Key GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(NRF_PORT, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = LED0;//LED The lamp GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_Init(NRF_PORT , &GPIO_InitStruct); Pin_LED0(1);// Low level Extinguish LED The lamp // Add key interrupt EXTI_InitStructure.EXTI_Line = KEY0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI_Trigger_Rising , EXTI_Trigger_Falling , EXTI_Trigger_Rising_Falling EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;// Rising edge trigger EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // interrupt NVIC_InitStructure.NVIC_IRQChannel = EXTI0_1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 1; // Sub priority 1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ Channel enable NVIC_Init(&NVIC_InitStructure); // Initialize... According to the specified parameters VIC register } u8 Get_KEY0(void) { return GPIO_ReadInputDataBit(NRF_PORT, KEY0); } void Pin_LED0(u8 u) { if(u==0) { NRF_PORT->BRR = LED0; } else { NRF_PORT->BSRR = LED0; } } // Address u8 TX_ADDRESS1[5]= {0x34,0x43,0x10,0x10,0x01}; // Local address u8 RX_ADDRESS1[5]= {0x34,0x43,0x10,0x10,0x01}; // Receiving address // To configure void NRF_Config() { GPIO_InitTypeDef GPIO_InitStruct; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; // Port initialization RCC_AHBPeriphClockCmd(PORTA_LCK , ENABLE); GPIO_InitStruct.GPIO_Pin = NRF_CE;//CE Output GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(NRF_PORT, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = NRF_IRQ;//IRQ Interrupt input GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(NRF_PORT, &GPIO_InitStruct); // interrupt EXTI_InitStructure.EXTI_Line = NRF_IRQ; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI_Trigger_Rising , EXTI_Trigger_Falling , EXTI_Trigger_Rising_Falling EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;// Falling edge trigger EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // interrupt NVIC_InitStructure.NVIC_IRQChannel = EXTI2_3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 1; // Sub priority 1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ Channel enable NVIC_Init(&NVIC_InitStructure); // Initialize... According to the specified parameters VIC register // To configure RNF Pin_CSN(1); Pin_CE(0); //0x02 launch , 0x03 receive - crc Do not enable crc Pattern :8 Bit validity //0x0e launch , 0x0f receive - crc Can make crc Pattern :16 Bit validity SPI_RWReg(0x20+0x00,0x02);//0x02 launch , 0x03 receive SPI_RWReg(0x20+0x01,0x00);// Auto answer If channel 0 The reply :0x01 Auto answer is prohibited :0x00 SPI_RWReg(0x20+0x02,0x01);// The receiving address allows passageway 0 allow SPI_RWReg(0x20+0x03,0x03);// Set the address width 5 Address width SPI_RWReg(0x20+0x04,0x00);// Establish automatic retransmission Prohibit automatic retransmission SPI_RWReg(0x20+0x05,40);// RF channels SPI_RWReg(0x20+0x06,0x07);// RF register SPI_RWReg(0x20+0x07,0x70);// Status register SPI_WriteBuf(0x20+0x0A,RX_ADDRESS1,5);// passageway 0 Receiving address SPI_WriteBuf(0x20+0x10,TX_ADDRESS1,5);// Sending address SPI_RWReg(0x20+0x11,32);// Receive data channel 0 Effective data width 1-32 byte } // send out void TX_MODE() { // To configure RNF Pin_CSN(1); Pin_CE(0); //0x02 launch , 0x03 receive - crc Do not enable crc Pattern :8 Bit validity //0x0e launch , 0x0f receive - crc Can make crc Pattern :16 Bit validity SPI_RWReg(0x20+0x00,0x02); SPI_RWReg(0x20+0x01,0x00);// Auto answer If channel 0 The reply :0x01 Auto answer is prohibited :0x00 SPI_RWReg(0x20+0x02,0x01);// The receiving address allows passageway 0 allow SPI_RWReg(0x20+0x03,0x03);// Set the address width 5 Address width SPI_RWReg(0x20+0x04,0x00);// Establish automatic retransmission Prohibit automatic retransmission SPI_RWReg(0x20+0x05,40);// RF channels SPI_RWReg(0x20+0x06,0x07);// RF register SPI_RWReg(0x20+0x07,0x70);// Status register SPI_WriteBuf(0x20+0x0A,RX_ADDRESS1,5);// passageway 0 Receiving address SPI_WriteBuf(0x20+0x10,TX_ADDRESS1,5);// Sending address SPI_RWReg(0x20+0x11,32);// Receive data channel 0 Effective data width 1-32 byte } // receive void RX_MODE() { // To configure RNF Pin_CSN(1); Pin_CE(0); //0x02 launch , 0x03 receive - crc Do not enable crc Pattern :8 Bit validity //0x0e launch , 0x0f receive - crc Can make crc Pattern :16 Bit validity SPI_RWReg(0x20+0x00,0x03);//0x02 launch , 0x03 receive SPI_RWReg(0x20+0x01,0x00);// Auto answer , If channel 0 The reply :0x01 Auto answer is prohibited :0x00 SPI_RWReg(0x20+0x02,0x01);// The receiving address allows passageway 0 allow SPI_RWReg(0x20+0x03,0x03);// Set the address width 5 Address width SPI_RWReg(0x20+0x04,0x00);// Establish automatic retransmission Prohibit automatic retransmission SPI_RWReg(0x20+0x05,40);// RF channels SPI_RWReg(0x20+0x06,0x07);// RF register SPI_RWReg(0x20+0x07,0x70);// Status register SPI_WriteBuf(0x20+0x0A,RX_ADDRESS1,5);// passageway 0 Receiving address SPI_WriteBuf(0x20+0x10,TX_ADDRESS1,5);// Sending address SPI_RWReg(0x20+0x11,32);// Receive data channel 0 Effective data width 1-32 byte Pin_CE(1); delay_us(120); } // send data void send_data(u8 *buff,u8 len) { TX_MODE(); SPI_RWReg(0xE1,0xFF);// Clear the transmit register SPI_WriteBuf(0xA0,buff,len);// Write to send register Pin_CE(1); delay_us(20); Pin_CE(0); } // receive data void receive_data() { RX_MODE(); SPI_RWReg(0xE2,0xFF);// Clear the receive register Pin_CE(1); delay_us(120); } // get data void Get_Data(u8 *buf,u8 len) { SPI_ReadBuf(0x61,buf,len); } // Clear interrupt void ClearStatus() { Pin_CE(0); SPI_RWReg(0x20+0x07,0x70); Pin_CE(1); delay_us(20); } void Pin_CE(u8 u) { if(u==0) { NRF_PORT->BRR = NRF_CE; } else { NRF_PORT->BSRR = NRF_CE; } } u8 Get_IRQ(void) { return GPIO_ReadInputDataBit(NRF_PORT, NRF_IRQ); } // For writing data : For register address ,pBuf: Is the address of the data to be written ,uchars: Number of data written u8 SPI_WriteBuf(u8 reg, u8 *pBuf, u8 len) { u8 s,ctr; Pin_CSN(0); delay_us(100); s=SPI_SendByte(reg); for(ctr=0; ctr<len; ctr++) SPI_SendByte(*pBuf++); Pin_CSN(1); delay_us(100); return s; } // function : For reading data ,reg: For register address ,pBuf: Is the address of the data to be read ,uchars: Number of read data u8 SPI_ReadBuf(u8 reg, u8 *pBuf, u8 len) { u8 s,ctr; Pin_CSN(0); delay_us(100); s=SPI_SendByte(reg); for(ctr=0; ctr<len; ctr++) pBuf[ctr]=SPI_SendByte(NOP); Pin_CSN(1); delay_us(100); return s; } // function :NRF24L01 Read / write register function u8 SPI_RWReg(u8 reg, u8 value) { u8 status = 0x00; Pin_CSN(0); delay_us(100); status = SPI_SendByte(reg); // Send register address , SPI_SendByte(value);// Send register value delay_us(100); Pin_CSN(1); return (status); } // function :NRF24L01 Read / write register function u8 Get_Status() { return SPI_RWReg(0x07,0xFF); } // Detect the presence of u8 nrf24l0_check(void) { u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5}; u8 i; SPI_WriteBuf(0x20+0x10,buf,5); SPI_ReadBuf(0x10,buf,5); for(i=0; i<5; i++)if(buf[i]!=0xA5)break; if(i!=5)return 1; return 0; }
3.stm32f0xx_it.c Function in interrupt

#include "delay.h" #include "stm32f0xx.h" #include "bsp_nrf0241.h" // Key interrupt void EXTI0_1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0)!= RESET) { delay_ms(20); if(Get_KEY0()!= RESET)// Eliminate key jitter { u8 tx_buf[32]={0x11};// User test data send_data(tx_buf,32);// send out } EXTI_ClearITPendingBit(EXTI_Line0);// Clear interrupt flag } } //nrf2401 Send receive interrupt void EXTI2_3_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line3)!= RESET) { u8 gstatus = Get_Status();// To obtain state // send out if((gstatus & 0x20) != 0) { // Send successfully LED The light flashes once Pin_LED0(0); delay_ms(1000); Pin_LED0(1); } // Maximum number of times to send if((gstatus & 0x10) != 0) { // Maximum number of times to send Pin_LED0(0); delay_ms(1000); Pin_LED0(1); } // receive if((gstatus & 0x40) != 0) { // Data received u8 tx_bufr[32]; Get_Data(tx_bufr,32); if(tx_bufr[0] == 0x11)// Compare with the sent data { Pin_LED0(0); delay_ms(1000); Pin_LED0(1); } } // Clear interrupt flag EXTI_ClearITPendingBit(EXTI_Line3); } ClearStatus();// Clear interrupt receive_data();// Continue to receive }
4.delay.h The time delay function ( From the Internet )

#include "stm32f0xx.h" #include "delay.h" static u8 fac_us=0;//us Delay multiplier static u16 fac_ms=0;//ms Delay multiplier // Initialization delay function //SYSTICK The clock is fixed to HCLK Of the clock 1/8 //SYSCLK: The system clock void delay_init(u8 SYSCLK) { SysTick->CTRL&=0xfffffffb;//bit2 Empty , Select the external clock HCLK/8 fac_us=SYSCLK/8; fac_ms=(u16)fac_us*1000; } // Time delay nms // Be careful nms The scope of the //SysTick->LOAD by 24 Bit register , therefore , The maximum delay is : //nms<=0xffffff*8*1000/SYSCLK //SYSCLK Unit is Hz,nms Unit is ms // Yes 72M Under the condition of ,nms<=1864 void delay_ms(u16 nms) { u32 temp; SysTick->LOAD=(u32)nms*fac_ms;// Time loading (SysTick->LOAD by 24bit) SysTick->VAL =0x00; // Clear the counter SysTick->CTRL=0x01 ; // Start counting down do { temp=SysTick->CTRL; } while(temp&0x01&&!(temp&(1<<16)));// Wait for time to arrive SysTick->CTRL=0x00; // Turn off the counter SysTick->VAL =0X00; // Clear the counter } // Time delay nus //nus To delay us Count . void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nus*fac_us; SysTick->VAL=0x00; SysTick->CTRL=0x01 ; do { temp=SysTick->CTRL; } while(temp&0x01&&!(temp&(1<<16))); SysTick->CTRL=0x00; SysTick->VAL =0X00; }
5.main.c Entry test function

#include "stm32f0xx.h" #include "delay.h" #include "bsp_spi.h" #include "bsp_nrf0241.h" void daly(uint32_t a) { for(;a>0;a--); } int main(void) { // Initialization delay function stm32f030 by 48 delay_init(48); /* *KEY0_LED0_Config(); Configure a key and a LED The lamp * PA0 Key * PA4 LED The lamp ( Users of the system LED The lamp Low level on ) * *SPI_Config(); To configure SPI * PA1 NSS Chip selection * PA5 SCK The clock * PA6 MISO Host input and slave output * PA7 MOSI Host output slave input * *NRF_Config(); To configure NRF24L01 * PA2 CE Control transceiver * PA3 IRQ Send receive interrupt */ KEY0_LED0_Config(); SPI_Config(); NRF_Config(); delay_us(20); if(nrf24l0_check()==0) { // There is nrf2401 modular Pin_LED0(0); delay_ms(1000); Pin_LED0(1); receive_data();// receive } while(1){ } }
Prepare two pieces for sending and receiving stm32f030 SCM and NRF Wireless module Connect with DuPont wire ( Be careful MISO and MOSI To correspond to the connection , Do not cross connect ), It is also necessary to lead to... For testing key0 Button , LED It is the default of the board PA4, After connecting, insert a burner into the two boards to burn the compiled code , You can press KEY0 Tested , If another MCU LED The light on indicates success .
If you have insufficient places and experience, please exchange in the comment area .