最近在玩一些传感器,因为涉及到通信协议,在网上看了很多帖子之后,修修补补,写了个简单能用的
硬件电路
主要是采用RS485电路进行数据采集
软件设计
直接把代码贴出来
485初始化
void rs485_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
//复用IO口 PB10 TX PB11 RX
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
//GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
//配置使能端口 接收 PC5
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
//发送 PC4
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
//串口初始化 串口3
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART3, &USART_InitStruct);
//使能串口
USART_Cmd(USART3, ENABLE);
USART_ClearFlag(USART3, USART_FLAG_TC);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
//默认为接收模式
RS485_RX_ENABLE;
RS485_TX_DISABLE;
}
因为板子的设计用了两个引脚控制接收和发送,所以需要初始化两个IO端口,串口使用的串口3.
定时器初始化
void timer6_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 9; //
TIM_TimeBaseInitStruct.TIM_Prescaler = 7199; //
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM6,TIM_IT_Update, ENABLE);
TIM_Cmd(TIM6, ENABLE);
}
void timer4_init(u16 arr) //TIM4使能
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler = 7199; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM4,TIM_IT_Update | TIM_IT_Trigger, ENABLE );
TIM_Cmd(TIM4, ENABLE); //使能TIMx外设
}
采用了两个定时器,一个用于发送和接收的转换,一个用于返回数据的判断,具体时序要看modbus协议。
发送指令代码
uint8_t RS485_TX_BUFF[200]; //存放发送指令
uint16_t RS485_TX_CNT=0; //发送计数器
uint8_t RS485_TxFlag=0; //发送一帧结束标记
uint8_t RS485_RX_BUFF[200];//存放接收的数据
uint16_t RS485_RX_CNT=0; //接收计数器
uint8_t RS485_RxFlag=0; //接收一帧结束标记
//主机发送的指令
uint8_t SlaverAddr = 0x01; //从机地址
uint8_t Fuction = 0x03; //功能码
uint16_t StartAddr = 0x0015; //起始地址
uint16_t ValueOrLenth = 0x0002; //数据or长度
uint8_t TX_RX_SET = 0; //发送,接受命令切换。 0 发送模式 1接受模式
uint8_t ComErr = 8; //0代表通讯正常 1代表CRC错误 2代表功能码错误 现在默认的是0x03
//7为通讯超时 8代表未开始
uint16_t ReadReg[100]; //只读寄存器----存储从机返回的数据 功能码 03
uint16_t timeout = 0; //超时判断
uint8_t timerun = 0; //启动定时
volatile uint32_t Distance_int;
volatile uint16_t Distance_point;
用到的标志位和数组给出了定义
void modbus_03(void)
{
uint16_t calCRC;
RS485_TX_BUFF[0] = 01;
RS485_TX_BUFF