CH7-Lora

Lora原理图

image-20240404152157908
Lora连接原理图
表1.stm32连接LORA引脚
STM32引脚 LORA引脚 功能
PA4 SPI1_NSS SPI 片选引脚,低电平有效
PA5 SPI1_SCK SPI 总线时钟引脚,常态为低电平,上升沿触发
PA6 SPI1_MISO SPI 总线从机输出引脚。当 MCU 读取模块数据时,模块数据由此引脚输出
PA7 SPI1_MOSI SPI 总线从机输入引脚。当 MCU 向模块写数据时,数据从此引脚写入

MDK配置

CubeMX配置

image-20240402233815111
下载线配置
image-20240406023118649
SPI配置1

由于SPI配置后只有三个引脚被配置,但数据通信时还有一个Lora通信SPI1_NSS对映的PA4需要配置为低电平

image-20240406023344830
SPI配置2
image-20240402233501605
OLED引脚配置
image-20240402233712239
GPIO引脚配置
image-20240402233846175
RTC配置
image-20240402233941646
USART_DMA配置
image-20240402234031056
USART参数配置
image-20240402234101539
USART中断配置(IDLE)
image-20240402234205995
NVIC配置
image-20240402234357385
时钟树配置
image-20240402234506717
生成文件配置1
image-20240402234538464
生成文件配置2

MDK代码编写

Lora

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned char SPI_WriteRead(unsigned char addr,unsigned char mss)//Lora读写函数
{
extern SPI_HandleTypeDef hspi1;

unsigned char tx_data[2],rx_data[2];
tx_data[0]=addr;//编写tx_data
tx_data[1]=mss;

HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET);//将PA4拉低,开始发送数据

HAL_SPI_TransmitReceive(&hspi1,tx_data,rx_data,2,0xff);//SPI传输函数,它可以同时发送和接收数据,hspi是SPI柄,pTxata是要发送的数据缓区指针,pRxpata是接收数据的缓冲中区指针,size是要发送/接收的数据字节数,Timeout是超时时间。


HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET);//将PA4拉高,结束发送数据

return rx_data[1];
}

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout)函数是一个STM32 HAL库中的SPI传输函数,它可以同时发送和接收数据。其中,hspi是SPI柄,pTxata是要发送的数据缓区指针,pRxpata是接收数据的缓冲中区指针,size是要发送/接收的数据字节数,Timeout是超时时间。

function.c

1
2
3
4
5
6
7
8
9
10
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "function.h"
#include "gpio.h"
#include "stdio.h" //fputc() 函数
#include "string.h"
#include "oled.h"
#include "lora.h"

extern UART_HandleTypeDef huart2; //外部调用串口的结构体
extern DMA_HandleTypeDef hdma_usart2_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;//外部调用串口DMA的结构体

extern I2C_HandleTypeDef hi2c3;//IIC的结构体
extern RTC_HandleTypeDef hrtc;//RTC结构体
extern SPI_HandleTypeDef hspi1;//spi结构体

#define Usart_RX_buffer_len 100//允许不定长接收的数据的缓存区长度
unsigned char usart_rx_sec_len;//接收到一帧串口数据的长度
unsigned char usart_rx_sec_flag;//接收到一帧串口数据接收时的标志位
unsigned char usart_rx_buffer[Usart_RX_buffer_len];//接收数据缓存数组

/********************************* 需要放进function.h *******************************************/
/* Init初始化函数 */
void ALL_Init(void)
{
Usart_RX_Init();
IIC_OLED_Init(100);
}
/* IDLE接收函数组 */
void Usart_RX_Init(void)
{
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);//使能IDLE的接收中断
HAL_UART_Receive_DMA(&huart2,usart_rx_buffer,Usart_RX_buffer_len);//打开DMA串口接收函数,第一个变量为串口2结构体,第二个变量为接收的存储数组,第三个为接收数据的大小
}

void Usart_RX_EXTI(void)//需要将`USART_RX_IDLE_EXTI()`放进stm32l0xx_it.c中的`void USART2_IRQHandler(void)`中
{
if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) == SET)//判断IDLE的接收是否产生中断,如果接收到一帧数据,那么就会触发中断,FLAG被置1
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除中断标志位
HAL_UART_DMAStop(&huart2);//关闭DMA防止在处理数据时候接收数据,产生干扰

usart_rx_sec_len = Usart_RX_buffer_len - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);//计算出接收缓存中的数据长度
usart_rx_sec_flag=1;//将检测接收完成的标志位置1
}
}
void Usart_RX_DEAL(void)
{
if(usart_rx_sec_flag==1)//判断接收完成标志位是否置1
{

if((Usart_RX_mess[0]=='L') && (Usart_RX_mess[1]=='E') && (Usart_RX_mess[2]=='D'))//用户自定义的函数,在这里我们可以根据需要,设置我们输入特定指令所触发的功能
{
HAL_UART_Transmit_DMA(&huart2,Usart_RX_mess,Usart_RX_sec_len);//
}


usart_rx_sec_flag=0;//将接收完成标志位置0
usart_rx_sec_len=0;//将接收到的数据清零
HAL_UART_Receive_DMA(&huart2,usart_rx_buffer,Usart_RX_buffer_len);//重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态
}
}
/* Lora */
unsigned char SPI_WriteRead(unsigned char addr,unsigned char mss)//Lora读写函数
{
extern SPI_HandleTypeDef hspi1;

unsigned char tx_data[2],rx_data[2];
tx_data[0]=addr;//编写tx_data
tx_data[1]=mss;

HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET);//将PA4拉低,开始发送数据

HAL_SPI_TransmitReceive(&hspi1,tx_data,rx_data,2,0xff);//SPI传输函数,它可以同时发送和接收数据,hspi是SPI柄,pTxata是要发送的数据缓区指针,pRxpata是接收数据的缓冲中区指针,size是要发送/接收的数据字节数,Timeout是超时时间。


HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET);//将PA4拉高,结束发送数据

return rx_data[1];
}
/* IIC OLED */
void IIC_OLED_Init(unsigned char ms)//初始化函数,需要延时100ms
{
HAL_GPIO_WritePin(OLED_POWER_GPIO_Port, OLED_POWER_Pin, GPIO_PIN_RESET);//使能OLED,PB5=LOW
HAL_Delay(ms);//延时100ms
OLED_Init(); //OLED.C中自带的初始化函数
OLED_Clear();//OLED.C中自带的清屏函数,否则会出现乱码
}


void OLED_Write(unsigned char addr,unsigned char data)//OLED的写函数
{
unsigned char pdata[2];//发送的存储数组
pdata[0]=addr;//存储第一个数据
pdata[1]=data;//存储第二个数据

HAL_I2C_Master_Transmit(&hi2c3,0x78,pdata,2,0xff);//在阻塞模式下将大量数据写入特定的内存地址,第一个参数为I2C指针,即用I2C1 还是 I2C2;第二个参数器件地址uint16_t DevAddress(0X78);第三个参数指向发送数据的指针;第四个参数数据的长度;第五个参数发送所需要的时间
}

/* RTC */
unsigned char * RTC_Data(void)
{
static unsigned char rtc_data[7];

RTC_TimeTypeDef rtc_time_data;
RTC_DateTypeDef rtc_date_data;

HAL_RTC_GetTime(&hrtc,&rtc_time_data,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&rtc_date_data,RTC_FORMAT_BIN);

rtc_data[0]=rtc_date_data.Year;
rtc_data[1]=rtc_date_data.Month;
rtc_data[2]=rtc_date_data.WeekDay;
rtc_data[3]=rtc_date_data.Date;

rtc_data[4]=rtc_time_data.Hours;
rtc_data[5]=rtc_time_data.Minutes;
rtc_data[6]=rtc_time_data.Seconds;

return rtc_data;
}
/********************************* 不用放进function.h *******************************************/
/* printf函数的子函数 */
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,0xffff);
//while(__HAL_UART_GET_IT(&huart2,UART_IT_TC) == RESET){}

return ch;
}

/* 外部中断函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(USER_KEY_Pin==GPIO_Pin)
{
flag_send_rtc=1;
}
}

function.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef __FUNCTION_H__
#define __FUNCTION_H__


void Layer_switch(const unsigned char layer_choose,const unsigned char layer_state);

void IDLE_RX_Init(void);
void IDLE_RX_Exit(void);
void IDLE_RX_Deal(void);

void Usart_puplish(const unsigned char *ms,const unsigned char ms_len);

unsigned char * RTC_Read(void);

unsigned char SPI_WriteRead(unsigned char addr,unsigned char mss);

void OLED_A_Init(unsigned char time_delay);
void OLED_Write(unsigned char addr,unsigned char mss);

void ALL_Init(void);

#endif

main.h

1
2
3
4
5
6
7
8
9
10
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
38
#include "function.h"
#include "stdio.h"
#include "string.h"
#include "oled.h"
#include "lora.h"

int main(void)
{
ALL_Init();
extern unsigned char flag_num;
extern unsigned char oled_2_pub[16];
unsigned char *rtc_rec_data;
unsigned char oled_1_pub[16];
unsigned char oled_3_pub[16];
unsigned char lora_a_rx[5];
while (1)
{
rtc_rec_data=RTC_Read();
if(1==flag_num)
{
flag_num=0;
printf("\r\n RTC:%d-%d-%d %02d:%02d:%02d \r\n",2000+rtc_rec_data[0],rtc_rec_data[1],rtc_rec_data[3],rtc_rec_data[4],rtc_rec_data[5],rtc_rec_data[6]);
}
sprintf((char *)oled_1_pub,"RTC:%02d:%02d:%02d",rtc_rec_data[4],rtc_rec_data[5],rtc_rec_data[6]);
OLED_ShowString(0,oled_1_pub);
//OLED_ShowString(2,oled_2_pub);
LORA_Tx(rtc_rec_data,7);
LORA_Rx(lora_a_rx);
sprintf((char *)oled_3_pub,"EX:%02d",lora_a_rx[0]);
OLED_ShowString(2,oled_3_pub);

IDLE_RX_Deal();
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}