本文讲解是以F4为例,F1接线和F4一模一样,代码部分串口配置有一点区别,具体自己看代码,文末都有给出
先准备一个ATK-M750C 4G模块,然后下载正点原子官方资料4G Cat1 DTU通信模块ATK-IDM751C.IDM750C — 正点原子资料下载中心 1.0.0 文档 (openedv.com)
下载完成后打开5,实例源码(基于STM32)文件夹,里面有一个压缩包,选择库函数版本,适合探索者F407开发板,再选择(库函数版本,适合探索者F407开发板) ATK-M750 TTL通讯(链接见文末)【1】
解压出来后打开USER双击ATK-M750.uvprojx打开工程,直接编译会报错,如图所示
是因为没有勾选C99 Mode,打开魔术棒勾选此处即可
重新编译,没有报错
此时自己再解压一个官方的串口demo例程【2】,或者使用我整理的(链接在文末,所有提到的文件均放在一起并与文中红色序号对应)
其中usart.c除了初始化串口1,还要初始化串口2,代码如下(注:基于正点原子demo修改)
#include "sys.h"
#include "usart.h"
#include "ring_buffer.h"
#include "string.h"
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA = 0; //接收状态标记
//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//使能USART1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10复用为USART1
//串口2引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2复用为USART2
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3复用为USART2
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9,PA10
//USART2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2与GPIOA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA2,PA3
//USART1 初始化设置
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
//USART2 初始化设置
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(USART2, &USART_InitStructure); //初始化串口2
USART_Cmd(USART1, ENABLE); //使能串口1
USART_Cmd(USART2, ENABLE); //使能串口 2
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ClearFlag(USART2, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启接受中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
//Usart2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
extern ring_buffer rb;
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res = USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
}
}
void usart1_send_data(u8 *data, u32 size)
{
for(u32 i = 0; i < size; i++)
{
while((USART1->SR & 0X40) == 0);
USART1->DR = data[i];
}
}
extern ring_buffer p_uart2_rxbuf;
void USART2_IRQHandler(void) //串口2中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
Res = USART_ReceiveData(USART2); //读取接收到的数据
RB_Write_Byte(&p_uart2_rxbuf, Res); //放入缓存
}
}
void usart2_send_data(u8 *data, u32 size)
{
for(u32 i = 0; i < size; i++)
{
while((USART2->SR & 0X40) == 0);
USART2->DR = data[i];
}
}
usart.h代码如下(注:基于正点原子demo修改)
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "stm32f4xx_conf.h"
#include "sys.h"
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
void usart1_send_data(u8 *data, u32 size);
void usart2_send_data(u8 *data, u32 size);
#endif
参考官方自带的ATK-M750C的demo,可以看到使用了ringbuffer缓冲区,我们这里使用一个轻量级环形缓冲区即可,不需要外部SRAM,参考链接如下[开源]轻量级环形缓冲区Ring Buffer,非常适合用于MCU嵌入式平台的串口收发 - STM32/8 (51hei.com)
开源ringbuffer从GitHub下载后打开RingBuffer-main.zip(链接见文末)【3】
将其中的ring_buffer.c、ring_buffer.h添加到串口demo工程中,并在ring_buffer.h中添加头文件stm32f4xx.h
然后再ring_buffer.c中添加Reset功能,在ring_buffer.h中添加声明
uint8_t RB_Reset(ring_buffer *rb_handle)
{
return rb_handle->head = rb_handle->tail = rb_handle->Length = 0 ;
}
uint8_t RB_Reset(ring_buffer *rb_handle);
然后把官方例程中的atk_m750.c和atk_m750.h放入到串口demo中,修改相关的头文件和ringbuffer函数,部分修改如下
注意函数中的取地址符&
主函数main.c如下
#include "stm32f4xx.h" // Device header
#include "delay.h"
#include "led.h"
#include "OLED.h"
#include "sys.h"
#include "usart.h"
#include "ring_buffer.h"
#include "string.h"
#include "atk_m750.h"
#define DTU_TEST_DATA "ALIENTEK ATK-IDM750C TEST"
#define DTU_NETDATA_RX_BUF (1024)
static uint32_t dtu_rxlen = 0;
static uint8_t dtu_rxbuf[DTU_NETDATA_RX_BUF];
ring_buffer p_uart2_rxbuf;
int main(void) {
int ret;
uint32_t timeout = 0;
uint8_t buf;
uint8_t key;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
uart_init(115200); //串口初始化波特率为115200
LED_Init(); //初始化与LED连接的硬件接口
RB_Init(&p_uart2_rxbuf, dtu_rxbuf, DTU_NETDATA_RX_BUF);
printf("Wait for Cat1 DTU to start, wait 10s.... \r\n");
{
while( timeout <= 10 ) /* 等待Cat1 DTU启动,需要等待5-6s才能启动 */
{
ret = dtu_config_init(DTU_WORKMODE_NET); /*初始化DTU工作参数*/
if( ret == 0 )
break;
timeout++;
delay_ms(1000);
}
while( timeout > 10 ) /* 超时 */
{
printf("**************************************************************************\r\n");
printf("ATK-DTU Init Fail ...\r\n");
printf("请按照以下步骤进行检查:\r\n");
printf("1.使用电脑上位机配置软件检查DTU能否单独正常工作\r\n");
printf("2.检查DTU串口参数与STM32通讯的串口参数是否一致\r\n");
printf("3.检查DTU与STM32串口的接线是否正确\r\n");
printf("4.检查DTU供电是否正常,DTU推荐使用12V/1A电源供电,不要使用USB的5V给模块供电!!\r\n");
printf("**************************************************************************\r\n\r\n");
delay_ms(3000);
}
}
printf("Cat1 DTU Init Success \r\n");
dtu_rxlen = 0;
RB_Reset(&p_uart2_rxbuf);
/*
DTU进入透传状态后,就可以把它当成普通串口使用,必须确保:1.硬件连接完好 2.串口参数与DTU的保持一致
注意:DTU每次上电需要一定的时间,在等待连接过程中,MCU可以向DTU发送数据并缓存在DTU中,等到与服务器连接上,DTU会自动将数据缓存数据全部转发到服务器上。
*/
while (1)
{
if (RB_Get_Length(&p_uart2_rxbuf) > 0) /*接收到DTU传送过来的服务器数据*/
{
RB_Read_Byte(&p_uart2_rxbuf, &buf);
dtu_rxbuf[dtu_rxlen++] = buf;
dtu_get_urc_info(buf); /*解析DTU上报的URC信息*/
if (dtu_rxlen >= DTU_NETDATA_RX_BUF) /*接收缓存溢出*/
{
usart1_send_data(dtu_rxbuf, dtu_rxlen); /*接收到从DTU传过来的网络数据,转发到调试串口1输出*/
dtu_rxlen = 0;
}
}
else
{
if (dtu_rxlen > 0)
{
usart1_send_data(dtu_rxbuf, dtu_rxlen); /*接收到从DTU传过来的网络数据,转发到调试串口1输出*/
dtu_rxlen = 0;
}
LED0 = !LED0;
delay_ms(100);
}
// key = KEY_Scan(0);
// if(key == KEY0_PRES)
// {
// /*如果服务器格式对数据有要求,请修改对应的数据格式,这里只限于例程测试使用*/
// send_data_to_dtu((uint8_t *)DTU_TEST_DATA, strlen(DTU_TEST_DATA));
// }
}
}
至此,已全部移植修改完毕,完整工程见文末链接中的【4】
注意,最重要的是接线,官方推荐供电为12V/1A,可以找一个常用的电源适配器(注意看输出电压电流是否符合)对其供电,VI接正极,G接负极
然后模块的TX接PA3、RX接PA2
PA9、PA10接USB TO TTL来打印串口信息
最后,如果你使用的是开发板,则模块的另一个G要接到开发板的GND实现共地,否则模块无法正常工作。
最后看一下现象(无SIM卡),先打开串口助手,打卡串口,选择波特率为115200,然后下载程序,复位,可以看到打印的信息。
F1的代码见链接中的【5】
链接:https://pan.baidu.com/s/1Eh1913twcj9vrrCD3whZSA
提取码:0htg