menu 牢记自己是菜
STM32学习 ----- week2
1539 浏览 | 2021-06-14 | 阅读时间: 约 5 分钟 | 分类: 计算机 | 标签:
请注意,本文编写于 97 天前,最后修改于 97 天前,其中某些信息可能已经过时。

stm32串口原理

通信方法

  1. 并行传输
  2. 串行传输

串行通信

  • 单工通信
  • 半双工通信
  • 双工通信

通信方式

  • 同步通信:SPI,IIC通信接口
  • 异步通信:UART(通用异步收发器)

STM32的串口通信接口

  • UART:通用异步收发器(2个)
  • USART:通用同步异步收发器(3个)

stm32串口通信实验

串口初始化

  1. 串口时钟使能,GPIO 时钟使能
  2. 串口复位
  3. GPIO 端口模式设置
  4. 串口参数初始化
  5. 初始化 NVIC 并且开启中断
  6. 使能串口
void uart_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA时钟
  
    //USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX      GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

    USART_InitStructure.USART_BaudRate = bound;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收发模式

  USART_Init(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

接收协议

这是正点原子为串行通信写的一个协议,与其说是协议,更像是一个逐字符读取的函数。打个比方,在c语言输入字符不确定的情况下,我们可以使用逐字读取,然后当接收到换行符的时候停止即可。代码:

void USART1_IRQHandler(void)                    //串口1中断服务程序
    {
    u8 Res;
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntEnter();    
#endif
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
        Res =USART_ReceiveData(USART1);    //读取接收到的数据
        
        if((USART_RX_STA&0x8000)==0)//接收未完成
            {
            if(USART_RX_STA&0x4000)//接收到了0x0d
                {
                if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
                else USART_RX_STA|=0x8000;    //接收完成了 
                }
            else //还没收到0X0D
                {    
                if(Res==0x0d)USART_RX_STA|=0x4000;
                else
                    {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收      
                    }         
                }
            }            
     } 
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntExit();                                               
#endif
} 

主函数

主函数做的则是一个检查检查中断的操作,他会检查寄存器buf中是否有数据,如果有的话就将其读取至程序。我哦们可以利用这个特性将程序改写成一个小加密程序。

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
//ALIENTEK Mini STM32开发板范例代码3
//串口实验   
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司
 int main(void)
 {    
    u8 t;
    u8 len;    
    u16 times=0; 
 
    delay_init();             //延时函数初始化    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
    uart_init(9600);     //串口初始化为9600
    LED_Init();               //初始化与LED连接的硬件接口 
 
    while(1)
    {
        if(USART_RX_STA&0x8000)
        {                       
            len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
            printf("\r\n您发送的消息加密后为:\r\n");
            for(t=0;t<len;t++)
            {
                char a = USART_RX_BUF[t] ^ 0x30;
                USART1->DR=a;
                while((USART1->SR&0X40)==0);//等待发送结束
            }
            printf("\r\n\r\n");//插入换行
            USART_RX_STA=0;
        }else
        {
            times++;
            if(times%5000==0)
            {
                printf("\r\nALIENTEK MiniSTM32开发板 串口实验\r\n");
                printf("正点原子@ALIENTEK\r\n\r\n\r\n");
            }
            if(times%200==0)printf("请输入数据,以回车键结束\r\n");  
            if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
            delay_ms(10);   
        }
    }     
}

我们这里就直接进行一个0x30的小异或加密。

esp_8266

模块到了,稍微玩一下,硬件连接。

硬件连接

AT指令测试

注意这里将波特率更改为115200,才可以进行通讯。我们主要准备学习一下使用AT指令进行开发,等啥时候学明白了在进行SDK开发。

AT+GMR

响应:AT version:1.7.0.0(Aug 16 2018 00:57:04)
AT version:1.7.0.0(Aug 16 2018 00:57:04)
SDK version:3.0.0(e27cf60)
compile time:Oct 30 2018 18:18:34
OK

ESP_8266

ATK_ESP8266 模块支持 STA/AP/STA+AP 三种工作模式:

  • STA 模式:ESP8266 模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的
    远程控制。
  • AP 模式:默认模式 ATK_ESP8266 模块作为热点,实现手机或电脑直接与模块通信,实
    现局域网无线控制。
  • STA+AP 模式:两种模式的共存模式,(STA 模式)即可以通过路由器连接到互联网,并通过互联网控制设备;(AP 模式)也可作为 wifi 热点,其他 wifi 设备连接到模块。这样实现局域网和广域网的无缝切换,方便操作。

但是按照我我们的实验设计这里应该要使用透传, ATK_ESP8266 模块仅在 TCP Client 和 UDP,支持透传模式。

AT+CWLAP

响应:返回查询到的热点信息

AT+CWJAP

加入热点,此时我们芯片就已经上线了。

AT+CIPSTART

这里属实还没太想明白,不知道为啥死活连不上服务器,明天请教一下。咋想觉得拓扑感觉都有问题

设置随身WiFi实验

上一个时使用的STA 模式,我们再来试一下,AP 模式的效果。

  1. AT+CWMODE=2
  2. AT+RST
  3. AT+CWSAP="ATK-ESP8266","12345678",1,4
  4. AT+CIPMUX=1
  5. AT+CIPSERVER=1,8086

五条指令,分别配置了AT模式,重启芯片,配置WiFi名称与IP。然后在手机就可以搜索到WiFi信号了。

除此以外,可以直接访问IP和端口,也会有反应,但是无法接受信息,明天再研究吧。

连接原子云

原子云是正点原子给我们提供的免费云服务物联网服务器,先稍微的玩一下,研究研究具体功能,如果好用的话就暂时先不研究如何和阿里云物联网服务器进行交互了。毕竟阿里云要收钱(穷b哭泣)。

首先我们先在原子云注册,创建设备。

实验继续采取AT指令的方式,不要问为啥不自己搞一下底层原理。。时间紧任务重,先应付一下。

  1. 设置模块为 STA 模式 AT+CWMODE=1
  2. 设置模块连接到 Wifi 路由器 AT+CWJAP="iPhone (3)",""
  3. 接原子云服务器 // 83884767545526260457 是设备 1 的设备编号,12345678 是设备密码 AT+ATKCLDSTA=" 83884767545526260457 ","12345678"

之后就可以经过透传传递信息了,基本上到时候把串口更换为stm32就可以完成将数据上传到原子云上了。

原子云API

本身是想使用原子云作为物联网服务器的,就稍微的研究了一下API的写法,结果发现,API不能提供双向通信,有点失望。这里就先不深入研究了,之后征求一下大家的意见,在磨合一下后续方案。

import requests
import json
token = "***********************************" #唯一身份码
#device_id = "83884767545526260457"         #esp_8266编号

def requests_GET(url,head): #GET请求封装模块
    join_in = requests.get(url,headers=head)
    return_json = join_in.json()
    return return_json

def join():
    '''
    获取连接状态
    '''
    head={'token':token,  'Content-Type':
        'application/x-www-form-urlencoded;charset=UTF-8'}#
    url = "https://cloud.alientek.com/api/orgs"
    '''
    join_in = requests.get("https://cloud.alientek.com/api/orgs",headers=head)
    return_json = join_in.json()
    '''
    return_json = requests_GET(url,head)
    json_str = json.dumps(return_json)
    json_dict = json.loads(json_str)
    #print(json_dict)
    json_data = json_dict["data"][0] #返回接口的data数据段 类型字典
    return json_data

def my_device_groups(org_id): #必要字段:token,org_id( GET请求
    '''
    设备分组列表
    '''
    head={'token':token, 'org_id': str(org_id) , 'Content-Type':
        'application/x-www-form-urlencoded;charset=UTF-8'}#
    url = "https://cloud.alientek.com/api/orgs/"  + str(org_id) + "/grouplist"
    return_json = requests_GET(url,head)
    json_str = json.dumps(return_json)
    json_dict = json.loads(json_str)
    #print(json_dict)
    json_data = json_dict["data"] #返回接口的data数据段 类型字典
    return json_data

def my_device_list(org_id,group_id): #获取设备列表 token org_id(url中) group_id
    '''
    获取device_id
    返回该分组下所有固件信息
    '''
    head={'token':token, 'org_id': str(org_id) ,'group_id': str(group_id) , 'Content-Type':
        'application/x-www-form-urlencoded;charset=UTF-8'}#
    url = "https://cloud.alientek.com/api/orgs/"+ str(org_id) +"/groups/" + str(group_id) + "/devices"
    return_json = requests_GET(url,head)
    json_str = json.dumps(return_json)
    json_dict = json.loads(json_str)
    #print(json_dict)
    json_data = json_dict["data"] #返回接口的data数据段 类型字典
    return json_data

def my_device_connect(org_id,device_id): #token org_id device_id
    '''
    获取某个设备的连接状态
    '''
    head={'token':token, 'org_id': str(org_id) , 'device_id': str(device_id) , 'Content-Type':
        'application/x-www-form-urlencoded;charset=UTF-8'}#
    url = "https://cloud.alientek.com/api/orgs/" + str(org_id) + "/devicestate/" + str(device_id)
    return_json = requests_GET(url,head)
    json_str = json.dumps(return_json)
    json_dict = json.loads(json_str)
    #print(json_dict)
    json_data = json_dict["data"] #返回接口的data数据段 类型字典
    return json_data

def my_device_message(org_id,device_number): #token org_id device_number
    '''
    获取服务器实时消息
    '''
    head={'token':token,  'Content-Type':
        'application/x-www-form-urlencoded;charset=UTF-8'}#
    url = "https://cloud.alientek.com/api/orgs/"+ str(org_id) + "/devicepacket/" + str(device_number)
    return_json = requests_GET(url,head)
    json_str = json.dumps(return_json)
    json_dict = json.loads(json_str)
    print(json_dict)
    json_data = json_dict["data"] #返回接口的data数据段 类型字典
    return json_data
'''
程序主函数
'''
if __name__ == '__main__':
    My_join_data = join()  #返回原始data段数据
    My_id = My_join_data["id"] #机构唯⼀标识,通过“账号机构列表”接⼝获取
    My_device_groups_data = my_device_groups(My_id) #获取原子云分组
    '''
    返回的分组 由于我们只用到一个分组,这里就不做分组过滤了
    for i in My_device_groups_data:
        print(i)
    '''
    My_group_id = My_device_groups_data[0]["id"]  #设备分组唯⼀标识
    my_device_list_data = my_device_list(My_id,My_group_id) #获取每组列表
    esp8266_data = my_device_list_data[0] #由于分组我们只有一个芯片,所以我们直接获取其信息 dict
    #print(esp8266_data)
    esp8266_data_id = esp8266_data["id"] # device_id 该分组下唯一设备编号
    device_connect = my_device_connect(My_id,esp8266_data_id)
    #print(device_connect)
    message = my_device_message(My_id,esp8266_data_id)
    

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!