STM32与OOK通讯技术详解:OOK发送与接收及CubeMX配置指南
一、简介
关于OOK通信,我的理解就是将要发送的信息转换成高低电平(即0和1),然后接收方通过对01进行解码获得信息。于是我用了非常朴素的字符串转二进制+AM调制来完成通信链。话不多说我们直接开始!!!
二、发送机详解
2.1 发送机cubemx配置
之前从来没有配置过的同学可以看一看我之前写的AHT20的配置。
发送机只需要配置一个东西,就是将你选定的高低电平输出引脚配置成OUTPUT!!
这里我选择了PC1作为输出引脚,你也可以选择自己心动的引脚嘉宾,但是一定要配成OUTPUT哦。
2.2 发送机代码呈现
很朴素的代码,字符串转换成二进制然后输出。
1.字符串转换成二进制
注意:此处每个字符转换成7个二进制编码,不是8个。
void charToBinary(char c, char* result) {
for (int i = 6; i >= 0; i--) {
result[6 - i] = (c >> i) & 1 ? '1' : '0';
}
result[7] = '\0';
}
void stringToBinary(char* s, char* binary) {
int binaryIndex = 0;
const int binaryBufferSize = 1024;
char tempBinary[9];
while (*s) {
charToBinary(*s++, tempBinary);
strcat(binary + binaryIndex, tempBinary);
binaryIndex += 7;
}
}
void numchar_to_output(char *s){
int length=strlen(s);
for (int i=0;i<length;i++){
if(s[i]=='0'){
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);
HAL_Delay(0);
HAL_UART_Transmit(&hlpuart1,(uint8_t*)c,strlen(c),500);
}
else if(s[i]=='1'){
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);
HAL_Delay(0);
HAL_UART_Transmit(&hlpuart1,(uint8_t*)b,strlen(b),500);
}
}
}
三个小函数,那么在主函数中如何调用呢?
2.主函数调用
先来几个定义:
char s[5000]="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
//这是我设置的起始位。
char inputStr[500] = {0};
//要进行传输的字符串,比如说hello!!
char binary[1024] = {0};
//字符串转换成的二进制字符串
s[5000]这是我设置的起始位。因为我发现接收机接收到的波在没有输出的时候是高电平,所以我加了很多0来表示开始读取。
注意:0的个数要足够大。因为我输出的逻辑是一毫秒输出一个电平,在我写的字符串转二进制中,一个字符串转换成7个01编码。而0000000对应的正好是字符串结束符'\0',所以为了保险起见+担心采样的时间偏差,我加了一大串0作为起始符。
这样的好处还有一个:我设置的是将要发送的信息循环发送,也就是这个信息前面是一串0,后面也是一串0,在进行解码的时候只需要处理前面的0,后面的0会被解码为结束符'\0',字符串会自动结束,也就是说对信息后面的0不用进行处理。
主函数调用:
stringToBinary(inputStr, binary);
strcat(s,binary);
while(1){numchar_to_output(s);}
这样你就可以愉快地输出对应的高低电平了!
2.3 发送机端结构
单片机输出———————>
乘法器 —————> 功率放大器 ————> 天线
高频载波(20Mhz)———>
至此,发送端完成!!
三、接收端详解
3.1 接收端结构
天线 ——> 功率放大器 ——>滤波器 ——> 检波器 ——> 放大器 ——> 单片机处理
实际操作中我只用了天线、检波器和单片机。可能因为距离比较短,深刻原因是功率放大器没接负载or输入信号太大被烧了TT
单片机处理的整体思路是:先进行长度为100的小采样,再进行长度为5000的大采样。
前面已经提到过起始符是一大串0,发送频率是1k。我的采样频率设置为10k,也就是说一个字符对应采样为70个采样点。先进行长度为100的小采样,如果这100个点中有非0的值(也就是不在起始符中),就重新进行小采样。一直到小采样中所有点都是0值,也就是现在采集的这一段都在起始符中,则退出小采样进行大采样。大采样就包含了传递的字符串。
然后再对大采样进行解码,即可获得传递的信息。
3.2 接收机cubemx配置
需要配置一个定时器和一个ADC。
定时器我选用了TIM3
7200-1这个地方是根据设定的时钟以及你所期待的采样频率设置的。我希望采样频率是10khz,而我的时钟是72Mhz,72M除以7200正好是10k,后面记得-1哦。
ADC使用了ADC1,配置如下:
我采用了单端输入,由定时器3触发进行DMA采样。
刚开始DMA里面什么都没有,点一下加号进行设置。
配置结束。
3.3 接收机代码呈现
main.c
#define f0 10000
//采样频率
#define ADC_SNUM 100
//小采样大小
#define ADC_LNUM 5000
//大采样大小
__IO uint8_t ADC_con_flag = 0;
uint16_t adc_sample_data[ADC_SNUM];
//小采样的采样数组
uint16_t adc_sample_data2[ADC_LNUM];
//大采样的采样数组
char mes[20]={0};
int caiyang(void);
//判断小采样是不是都是0,如果都是0就进行大采样
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_LPUART1_UART_Init();
MX_TIM3_Init();
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);
int flag=0;//小采样结束的标志
flag=caiyang();
HAL_Delay(0);
while(!flag){
flag=caiyang();
HAL_Delay(0);
}
HAL_Delay(0);
//开始进行大采样
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_sample_data2, ADC_LNUM);
HAL_TIM_Base_Start(&htim3);
while(!ADC_con_flag)
{
;
}
sprintf(mes,"flag is :%d\n",flag);
HAL_UART_Transmit(&hlpuart1,(uint8_t*)mes,strlen(mes),500);
for(int i=0;i<ADC_LNUM;i++){
if(adc_sample_data2[i]>2200) adc_sample_data2[i]=1000;
//此处我设置的是采样数值大于2200就判断为1,具体需要根据自己的情况设置。
else adc_sample_data2[i]=0;
//这里的处理是为了让数据变得更工整,便于后续的解码
sprintf(mes,"%d\n",adc_sample_data2[i]);
HAL_UART_Transmit(&hlpuart1,(uint8_t*)mes,strlen(mes),500);
}
output(adc_sample_data2);
//这个函数是我自己写的解码函数,在output.c文件中。下文会提到
}
int caiyang(void){
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_sample_data, ADC_SNUM);
HAL_TIM_Base_Start(&htim3);
while(!ADC_con_flag)
{
;
}
for(int i=0;i<ADC_SNUM;i++){
if(adc_sample_data[i]>2200) return 0;//有一就是return0
//同样要根据自己的情况设置。
}
return 1;//都是0,开始读取!!!
}
output.c
#include <string.h>
#include <stdio.h>
#include "output.h"
#define ADC_LNUM 5000
char binaryStringToCharSimple(const char* binaryStr) {
unsigned char result = 0;
for (int i = 0; i < 7; i++) {
result = (result << 1) | (binaryStr[i] - '0');
}
return result;
}
void output(uint16_t* adc_sample_data2){
char initial[ADC_LNUM/10];
int t_num=0;
int begin_num=0;
while(adc_sample_data2[begin_num]==0)begin_num++;
for(int i=begin_num-1;i<ADC_LNUM;i=i+10){
int f_1=0;
for(int j=i;j<i+10;j++){
if(adc_sample_data2[j]>700)f_1++;
}
if(f_1>7)initial[t_num++]='1';
else initial[t_num++]='0';
}
initial[t_num]='\0';
char binaryStr[20];
char result[200];
int char_len=strlen(initial);
int begin=0,i=0,j=0;
for (i = 0; i < char_len-7; i=i+7)
{
for(j=i;j<i+7;j++){
binaryStr[j-i]=initial[j];
}
binaryStr[j-i]='\0';
char a=binaryStringToCharSimple(binaryStr);
result[begin]=a;
begin++;
}
for(i=char_len-7;i<char_len;i++)binaryStr[i-(char_len-7)]=initial[i];
char a=binaryStringToCharSimple(binaryStr);
result[begin]=a;
begin++;
result[begin]='\0';
HAL_UART_Transmit(&hlpuart1,(uint8_t*)result,strlen(result),500);
}
output.h
#ifndef __OUTPUT_H__
#define __OUTPUT_H__
#endif
#include "main.h"
#include "usart.h"
void output(uint16_t adc_sample_data2[]);
ADC采样需要在你的stm32g4xx_it.c(不同的单片机不同,但都是xx_it)加上两句话:
extern uint8_t ADC_con_flag;
//这句在头文件下面加
//在DMA1_Channel1_IRQHandler函数中加一句
ADC_con_flag=1;
最后DMA1_Channel1_IRQHandler(void)应该是:
void DMA1_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_adc1);
ADC_con_flag=1;
}
至此,代码部分结束!你只需要在串口输出上看解码的结果就可以了。
注意:代码中时不时跳出来的delay(0)是我发现如果不加上会卡死,这个也请根据实际情况调整。经过我的测试发现加上是必要的。
四、资料分享
由于发射机部分比较简单,所以就不分享工程了。
OK接收机部分工程如下:
链接:https://pan.baidu.com/s/1L3NiK3ZRC1kMg3RnPYlywg?pwd=6666
提取码:6666
–来自百度网盘超级会员V2的分享
百度网盘自取。
作者:她与我心皆失