8 .看门狗
8.1 看门狗简介
此器件具有两个嵌入式看门狗外设,具有安全性高、定时准确及使用灵活的优点。两个看门狗外设(独立和窗口)均可用于检测并解决由软件错误导致的故障;当计数器达到给定的超时值时,触发一个中断(仅适用于窗口型看门狗)或产生系统复位。注意:独立看门狗没有中断,但是可以触发中断服务函数。
8.2 看门狗的作用
原理图
8.3 看门狗的选择
独立看门狗用于精度低的场合,窗口看门狗用于精度高的场合。
8.4 独立看门狗
8.4.1 时钟
8.4.2 LS时钟
8.4.3 特性
● 自由运行递减计数器;
● 时钟由独立 RC 振荡器提供(可在待机和停止模式下运行);
● 当递减计数器值达到 0x000 时产生复位(如果看门狗已激活);
8.4.4 预分频
8.5 独立看门狗初始化
#include "iwdg.h" // Device header void IWDG_Init(u16 PR,u16 RLR) { RCC_LSICmd(ENABLE); IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //关闭写保护 IWDG_SetPrescaler(PR); //预分频 IWDG_SetReload(RLR); //重装载值 IWDG_Enable(); //启动独立看门狗 IWDG_ReloadCounter(); //启动一次喂狗,写入0xAAAA,重装载的值才会被传递到计计数器 }
8.6 窗口看门狗
8.6.1 特点
窗口看门狗和独立看门狗不一样的地方在于它具备中断;
可编程的自由运行递减计数器;
复位条件:当递减计数器值小于 0x40 时复位(如果看门狗已激活);在窗口之外重载递减计数器时复位(如果看门狗已激活)。
中断触发条件:提前唤醒中断 (EWI):当递减计数器等于 0x40 时触发(如果已使能且看门狗已激活)。
8.6.2 原理
除非递减计数器的值在 T6 位变成 0 前被刷新,看门狗电路在
达到预置的时间周期时,会产生一个 MCU 复位。如果在递减计数器达到窗口寄存器值之前刷新控制寄存器中的 7 位递减计数器值,也会产生 MCU 复位。这意味着必须在限定的时间窗口内刷新计数器。
8.6.3 窗口看门狗初始化
- 时钟
- 使用的函数
- 分频
- 设置窗口值;
- 使能看门狗中断;
- 使能看门狗时,需要设置计数值,但是计数值不能大于窗口值 ;
- 程序初始化
- 在中断中喂狗;
8.6.4 源码程序
#include "wwdg.h" // Device header #include "stdio.h" int n; void WWDG_Init(u8 WindowValue,u8 Counter) { NVIC_InitTypeDef WWDG_NVIC; RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //使能时钟 WWDG_SetPrescaler(WWDG_Prescaler_8); //8分频 WWDG counter clock = (PCLK1/4096)/8 WWDG_SetWindowValue(WindowValue); //设置窗口值 WWDG_NVIC.NVIC_IRQChannel = WWDG_IRQn; WWDG_NVIC.NVIC_IRQChannelPreemptionPriority = 0; //抢占为0 WWDG_NVIC.NVIC_IRQChannelSubPriority = 0;//响应为0 WWDG_NVIC.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&WWDG_NVIC); n=Counter; WWDG_EnableIT(); //使能窗口看门狗中断 WWDG_Enable(Counter); //使能看门狗,设置计数值,传递大于0x40的值,防止初始化后产生一次复位 } //窗口看门狗中断 void WWDG_IRQHandler(void) { WWDG_SetCounter(n); //设置计数值,喂狗 WWDG_ClearFlag(); //EWIF: 提前唤醒中断标志,清中断标志位 //printf("喂狗了\r\n"); //2-3ms }
9. RS485
学习该内容的目的:学会使用485实现一主多从的数据通信控制
9.1 概念
学会使用485实现一主多从的数据通信控制;
9.2 物理(硬件)接口标准
按照电平分类可以分成TTL、RS232、RS422、RS485,下面逐一介绍;
9.2.1 TTL电平
高电平:2.4-5V;51单片机工作电压是5V;IO口输出高电平就是5V;
低电平:0~0.4v;
数字电路中,由TTL电子元器件组成电路使用的电平。电平是个电压范围,规定输出高电平>2.4V,输出低电平<0.4V。在室温下,一般输出高电平是3.5V,输出低电平是0.2V。最小输入高电平和低电平:输入高电平>=2.0V,输入低电平<=0.8V,噪声容限是0.4V。
我们之前使用的USART(UART),默认为TTL电平,我们单片机的I/O输入输出的电平是和TTL兼容;USART(UART)按3.3V来说,正常1米之内。
9.2.2 RS232
高电平:电平为逻辑“ 1”时: -3V~-15V;
低电平:电平为逻辑“ 0”时: +3V~+15V;
传输距离:50英尺,一英尺=0.38米;50英尺=15.24米;
传输速度:20Kbps,一收一发模式
9.2.3 RS422
标准:1收10发;传输距离4000英尺=1219.2米
9.2.4 RS485
RS-485 标准是为弥补 RS-232 通信距离短、速率低等缺点而产生的。 RS-485 标准只规
定了平衡发送器和接收器的电特性,而没有规定接插件、传输电缆和应用层通信协议。
发送器和接收器:
高电平:+2-+6V;低电平:-2--6V;
性能参数:
电缆最大距离:4000*0.3048=1219米;传输速度90Kbps;
RS485芯片:作为一种常用的通讯接口器件, RS-485/RS-422 芯片可以在许多半导体公司的“标准接口器件”栏目中“收发器”类元件中找到对应的型号;比如 Sipex 公司(器件前缀为 SP)、Maxim 公司(器件前缀为 MAX)、 TI 公司(器件前缀为 SN)、 Intersil 公司(器件前缀为 ISL 或LTC)等各大半导体公司。
9.3 一主一从通信
9.3.1 主机接线
引脚连接
发送数据:DE引脚使能为高电平,可以发送,同时把RE接收关闭,输出高电平;
接收数据:RE引脚使能为低电平,可以接收,同时把DE发送关闭,输出低电平
PG8引脚输出:高电平发送数据;低电平接收数据
9.3.2 丛机接线
9.3.3 主从机接线原理
9.4 主机程序
#include "rs485.h" // Device header #define RE_DE PGout(8) //发送/接收器共用一个引脚 void RS485_Init(int bps) { GPIO_InitTypeDef u2_TXRX,RE; USART_InitTypeDef u2; NVIC_InitTypeDef u2_NVIC; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOG,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //PG8-发送/接收器使能 RE.GPIO_Mode =GPIO_Mode_OUT; RE.GPIO_OType = GPIO_OType_PP; RE.GPIO_Pin = GPIO_Pin_8; RE.GPIO_PuPd = GPIO_PuPd_DOWN; //默认下拉 RE.GPIO_Speed = GPIO_High_Speed; GPIO_Init(GPIOG,&RE); //PA9-发送 u2_TXRX.GPIO_Mode =GPIO_Mode_AF; u2_TXRX.GPIO_OType = GPIO_OType_PP; u2_TXRX.GPIO_Pin = GPIO_Pin_2; u2_TXRX.GPIO_PuPd = GPIO_PuPd_UP; //默认下拉 u2_TXRX.GPIO_Speed = GPIO_High_Speed; GPIO_Init(GPIOA,&u2_TXRX); //根据u2_TXRX配置的参数进行初始化 //PA10-接收 u2_TXRX.GPIO_Mode =GPIO_Mode_AF; u2_TXRX.GPIO_OType = GPIO_OType_PP; u2_TXRX.GPIO_Pin = GPIO_Pin_3; u2_TXRX.GPIO_PuPd = GPIO_PuPd_NOPULL; //默认下拉 u2_TXRX.GPIO_Speed = GPIO_High_Speed; GPIO_Init(GPIOA,&u2_TXRX); //USART u2.USART_BaudRate=bps; //波特率 u2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //无硬件流 u2.USART_Mode=USART_Mode_Tx|USART_Mode_Rx; //发送和接收 u2.USART_Parity=USART_Parity_No; //无校验 u2.USART_StopBits=USART_StopBits_1; //停止位 u2.USART_WordLength=USART_WordLength_8b; //数据位 USART_Init(USART2, &u2); //根据u2配置的参数进行初始化 //记得分开写中断 USART_ITConfig(USART2, USART_IT_RXNE,ENABLE); //使能串口接收中断 USART_ITConfig(USART2, USART_IT_IDLE,ENABLE); //使能串口空闲中断 u2_NVIC.NVIC_IRQChannel = USART2_IRQn; u2_NVIC.NVIC_IRQChannelPreemptionPriority = 1; //抢占为1 u2_NVIC.NVIC_IRQChannelSubPriority = 1;//响应为0 u2_NVIC.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&u2_NVIC); USART_Cmd(USART2, ENABLE); //使能串口进行工作 } /******************************************************************** 函数功能:串口1发送1个字节的字符数据 参数1:data:你要发送的数据 参数2:无 返回值:无 作者:xxx ********************************************************************/ void RS485_sendbyte(char data) { RE_DE=1; //使能485发送 while(!(USART_GetFlagStatus(USART2,USART_FLAG_TC))); //等待判断发送寄存器为空 USART_SendData(USART2,data); while(!(USART_GetFlagStatus(USART2,USART_FLAG_TC))); //等待为空,说明发送完毕 RE_DE=0; //关闭发送,默认接收 } /******************************************************************** 函数功能:串口1发送一串字符数据 参数1:data:你要发送的数据 参数2:无 返回值:无 作者:xxx ********************************************************************/ void RS485_sendstring(char *data) { RE_DE=1; //使能485发送 while(*data != '\0')//循环发送,直到遇到\0,停止发送 { while(!(USART_GetFlagStatus(USART2,USART_FLAG_TC))); //等待判断发送寄存器为空 USART_SendData(USART2,*data++); } while(!(USART_GetFlagStatus(USART2,USART_FLAG_TC))); //等待字符串最后一个数据发送完毕 RE_DE=0; //关闭发送,默认接收 } //接收中断+空闲中断 struct U2_DATA rs485={0,0,0}; //定义结构体变量,同时初始化为0 //接收中断:每收到一个字符数据就会执行一次USART2_IRQHandler中断服务函数 //空闲中断:收完数据之后,串口产生空闲,会自动执行一次USART2_IRQHandler中断服务函数 void USART2_IRQHandler(void) { if(USART_GetITStatus(USART2, USART_IT_RXNE)==SET) //接收中断,存储数据 { rs485.buf[rs485.len]=USART_ReceiveData(USART2); //读数据并清除中断标志位 rs485.len++; //自增,为下个数据存储做准备 }else if(USART_GetITStatus(USART2, USART_IT_IDLE)==SET) //空闲中断,接收数据结束 { USART_ClearITPendingBit(USART2, USART_IT_IDLE); //读SR USART_ReceiveData(USART2); //读DR rs485.ok_flag=1; //接收完成 rs485.buf[rs485.len]='\0'; //添加结束符 } }
9.5 从机程序
#include "rs485.h" // Device header #include "delay.h" #define RE_DE PAout(12) void RS485_Init(int bps) { GPIO_InitTypeDef U3_TXRX,re; USART_InitTypeDef U3; NVIC_InitTypeDef NVIC_U3; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); //RCC re.GPIO_Mode=GPIO_Mode_Out_PP; //复用推挽输出模式 GPIO re.GPIO_Pin=GPIO_Pin_12; //引脚9 re.GPIO_Speed=GPIO_Speed_50MHz; //最高速50Mhz GPIO_Init(GPIOA, &re); //根据U1_TXRX配置的参数进行初始化 //PB10发送 U3_TXRX.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出模式 GPIO U3_TXRX.GPIO_Pin=GPIO_Pin_10; //引脚9 U3_TXRX.GPIO_Speed=GPIO_Speed_50MHz; //最高速50Mhz GPIO_Init(GPIOB, &U3_TXRX); //根据U1_TXRX配置的参数进行初始化 //PB11-接收 U3_TXRX.GPIO_Mode=GPIO_Mode_IPU; //上拉输入模式 U3_TXRX.GPIO_Pin=GPIO_Pin_11; //引脚10 U3_TXRX.GPIO_Speed=GPIO_Speed_50MHz; //最高速50Mhz GPIO_Init(GPIOB, &U3_TXRX); //根据U1_TXRX配置的参数进行初始化 //USART U3.USART_BaudRate=bps; //波特率 U3.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //无硬件流 U3.USART_Mode=USART_Mode_Tx|USART_Mode_Rx; //发送和接收 U3.USART_Parity=USART_Parity_No; //无校验 U3.USART_StopBits=USART_StopBits_1; //停止位 U3.USART_WordLength=USART_WordLength_8b; //数据位 USART_Init(USART3, &U3); //根据U1配置的参数进行初始化 NVIC_U3.NVIC_IRQChannel=USART3_IRQn; //设置的NVIC优先级的中断编号 NVIC_U3.NVIC_IRQChannelCmd=ENABLE; NVIC_U3.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级 NVIC_U3.NVIC_IRQChannelSubPriority=0; //响应优先级 NVIC_Init(&NVIC_U3); //记得分开写中断 USART_ITConfig(USART3, USART_IT_RXNE,ENABLE); //使能串口接收中断 USART_ITConfig(USART3, USART_IT_IDLE,ENABLE); //使能串口空闲中断 //NVIC_EnableIRQ(37); //使能RS485中断编号 USART_Cmd(USART3, ENABLE); //使能串口进行工作 RE_DE=0; //默认接收数据 } /******************************************************************** 函数功能:串口1发送1个字节的字符数据 参数1:data:你要发送的数据 参数2:无 返回值:无 作者:xxx ********************************************************************/ void RS485_sendbyte(char data) { RE_DE=1;//使能485发送 while(!(USART_GetFlagStatus(USART3,USART_FLAG_TC))); //等待判断发送寄存器为空 USART_SendData(USART3,data); while(!(USART_GetFlagStatus(USART3,USART_FLAG_TC))); //等待为空,发送完成 RE_DE=0; //关闭发送,默认接收 } /******************************************************************** 函数功能:串口1发送一串字符数据 参数1:data:你要发送的数据 参数2:无 返回值:无 作者:xxx ********************************************************************/ void RS485_sendstring(char *data) { RE_DE=1; //使能485发送 while(*data != '\0')//循环发送,直到遇到\0,停止发送 { while(!(USART_GetFlagStatus(USART3,USART_FLAG_TC))); //等待判断发送寄存器为空 USART_SendData(USART3,*data++); } while(!(USART_GetFlagStatus(USART3,USART_FLAG_TC))); //等待字符串最后一个数据发送完毕 RE_DE=0; //关闭发送,默认接收 } // 原来的 //接收中断+空闲中断 struct U3_DATA rs485={0,0,0}; //定义结构体变量,同时初始化为0 //接收中断:每收到一个字符数据就会执行一次RS485_IRQHandler中断服务函数 //空闲中断:收完数据之后,串口产生空闲,会自动执行一次RS485_IRQHandler中断服务函数 void USART3_IRQHandler(void) { if(USART_GetITStatus(USART3, USART_IT_RXNE)==SET) //接收中断,存储数据 { rs485.buf[rs485.len]=USART_ReceiveData(USART3); //读数据并清除中断标志位 rs485.len++; //自增,为下个数据存储做准备 }else if(USART_GetITStatus(USART3, USART_IT_IDLE)==SET) //空闲中断,接收数据结束 { USART_ClearITPendingBit(USART3, USART_IT_IDLE); //读SR USART_ReceiveData(USART3); //读DR rs485.ok_flag=1; //接收完成 rs485.buf[rs485.len]='\0'; //添加结束符 } }