安富莱电子 STM32模块(一)
/*
*********************************************************************************************************
*
* 模块名称 : 消息处理模块
* 文件名称 : bsp_msg.c
* 版 本 : V1.0
* 说 明 : 消息处理机制。
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2015-03-27 armfly 正式发布
*
* Copyright (C), 2014-2015, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
MSG_FIFO_T g_tMsg;
/*
*********************************************************************************************************
* 函 数 名: bsp_InitMsg
* 功能说明: 初始化消息缓冲区
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitMsg(void)
{
bsp_ClearMsg();
}
/*
*********************************************************************************************************
* 函 数 名: bsp_PutMsg
* 功能说明: 将1个消息压入消息FIFO缓冲区。
* 形 参: _MsgCode : 消息代码
* _pMsgParam : 消息参数,一般指向某个特定的结构体. 或者是0
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_PutMsg(uint16_t _MsgCode, uint32_t _MsgParam)
{
g_tMsg.Buf[g_tMsg.Write].MsgCode = _MsgCode;
g_tMsg.Buf[g_tMsg.Write].MsgParam = _MsgParam;
if (++g_tMsg.Write >= MSG_FIFO_SIZE)
{
g_tMsg.Write = 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_GetMsg
* 功能说明: 从消息FIFO缓冲区读取一个键值。
* 形 参: 无
* 返 回 值: 0 表示无消息; 1表示有消息
*********************************************************************************************************
*/
uint8_t bsp_GetMsg(MSG_T *_pMsg)
{
MSG_T *p;
if (g_tMsg.Read == g_tMsg.Write)
{
return 0;
}
else
{
p = &g_tMsg.Buf[g_tMsg.Read];
if (++g_tMsg.Read >= MSG_FIFO_SIZE)
{
g_tMsg.Read = 0;
}
_pMsg->MsgCode = p->MsgCode;
_pMsg->MsgParam = p->MsgParam;
return 1;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_GetMsg2
* 功能说明: 从消息FIFO缓冲区读取一个键值。使用第2个读指针。可以2个进程同时访问消息区。
* 形 参: 无
* 返 回 值: 0 表示无消息; 1表示有消息
*********************************************************************************************************
*/
uint8_t bsp_GetMsg2(MSG_T *_pMsg)
{
MSG_T *p;
if (g_tMsg.Read2 == g_tMsg.Write)
{
return 0;
}
else
{
p = &g_tMsg.Buf[g_tMsg.Read2];
if (++g_tMsg.Read2 >= MSG_FIFO_SIZE)
{
g_tMsg.Read2 = 0;
}
_pMsg->MsgCode = p->MsgCode;
_pMsg->MsgParam = p->MsgParam;
return 1;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_ClearMsg
* 功能说明: 清空消息FIFO缓冲区
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_ClearMsg(void)
{
g_tMsg.Read = g_tMsg.Write;
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : BSP模块(For STM32F1XX)
* 文件名称 : bsp.c
* 版 本 : V1.0
* 说 明 : 这是硬件底层驱动程序模块的主文件。主要提供 bsp_Init()函数供主程序调用。
? 主程序的每个c文件可以在开
* 头 添加 #include "bsp.h" 来包含所有的外设驱动模块。
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2015-08-30 armfly 正式发布
*
* Copyright (C), 2015-2016, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
#include "modbus_host.h"
#include "sense_app.h"
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
* 全局变量。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/*
由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
*/
/* 优先级分组设置为4 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
bsp_InitSense();
bsp_InitUart(); /* 初始化串口 */
bsp_InitIO(); /* 初始LED指示灯端口 */
bsp_InitTimer(); /* 初始化系统滴答定时器 (此函数会开中断) */
bsp_InitI2C();
}
/*
*********************************************************************************************************
* 函 数 名: bsp_RunPer10ms
* 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求不严格的
* 任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
bsp_KeyScan(); /* 按键扫描 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_RunPer1ms
* 功能说明: 该函数每隔1ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些需要周期性处理的事务
* 可以放在此函数。比如:触摸坐标扫描。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer1ms(void)
{
}
/*
*********************************************************************************************************
* 函 数 名: bsp_Idle
* 功能说明: 空闲时执行的函数。一般主程序在for和while循环程序体中需要插入 CPU_IDLE() 宏来调用本函数。
* 本函数缺省为空操作。用户可以添加喂狗、设置CPU进入休眠模式的功能。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Idle(void)
{
/* --- 喂狗 */
/* --- 让CPU进入休眠,由Systick定时中断唤醒或者其他中断唤醒 */
/* 例如 emWin 图形库,可以插入图形库需要的轮询函数 */
//GUI_Exec();
/* 例如 uIP 协议,可以插入uip轮询函数 */
MODH_Poll();
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : 独立按键驱动模块
* 文件名称 : bsp_key.c
* 版 本 : V1.0
* 说 明 : 扫描独立按键,具有软件滤波机制,具有按键FIFO。可以检测如下事件:
* (1) 按键按下
* (2) 按键弹起
* (3) 长按键
* (4) 长按时自动连发
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2013-02-01 armfly 正式发布
* V1.1 2013-06-29 armfly 增加1个读指针,用于bsp_Idle() 函数读取系统控制组合键(截屏)
* 增加 K1 K2 组合键 和 K2 K3 组合键,用于系统控制
* V1.2 2015-08-08 armfly K1,K2,K3独立按键进行排他判断,修改 IsKeyDown1()等函数
*
* Copyright (C), 2015-2016, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
/*
该程序适用于安富莱STM32-V4开发板
如果用于其它硬件,请修改GPIO定义和 IsKeyDown1 - IsKeyDown8 函数
如果用户的按键个数小于8个,你可以将多余的按键全部定义为和第1个按键一样,并不影响程序功能
#define KEY_COUNT 8 这个在 bsp_key.h 文件中定义
*/
/*
安富莱STM32-V4 按键口线分配:
K1 键 : PC13 (低电平表示按下)
K2 键 : PA0 ( --- 高电平表示按下)
K3 键 : PG8 (低电平表示按下)
摇杆UP键 : PG15 (低电平表示按下)
摇杆DOWN键 : PD3 (低电平表示按下)
摇杆LEFT键 : PG14 (低电平表示按下)
摇杆RIGHT键: PG13 (低电平表示按下)
摇杆OK键 : PG7 (低电平表示按下)
*/
/* 按键口对应的RCC时钟 */
#define RCC_ALL_KEY (RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOG)
#define GPIO_PORT_K1 GPIOC
#define GPIO_PIN_K1 GPIO_Pin_13
#define GPIO_PORT_K2 GPIOA
#define GPIO_PIN_K2 GPIO_Pin_0
#define GPIO_PORT_K3 GPIOG
#define GPIO_PIN_K3 GPIO_Pin_8
#define GPIO_PORT_K4 GPIOG
#define GPIO_PIN_K4 GPIO_Pin_15
#define GPIO_PORT_K5 GPIOA
#define GPIO_PIN_K5 GPIO_Pin_1
#define GPIO_PORT_K6 GPIOG
#define GPIO_PIN_K6 GPIO_Pin_14
#define GPIO_PORT_K7 GPIOG
#define GPIO_PIN_K7 GPIO_Pin_13
#define GPIO_PORT_K8 GPIOG
#define GPIO_PIN_K8 GPIO_Pin_7
static KEY_T s_tBtn[KEY_COUNT];
static KEY_FIFO_T s_tKey; /* 按键FIFO变量,结构体 */
static void bsp_InitKeyVar(void);
static void bsp_InitKeyHard(void);
static void bsp_DetectKey(uint8_t i);
/*
*********************************************************************************************************
* 函 数 名: IsKeyDownX
* 功能说明: 判断按键是否按下
* 形 参: 无
* 返 回 值: 返回值1 表示按下,0表示未按下
*********************************************************************************************************
*/
/* 安富莱 STM32-V4 开发板 */
#if 1 /* 为了区分3个事件: K1单独按下, K2单独按下, K1和K2同时按下 */
static uint8_t IsKeyDown1(void)
{
if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0 && (GPIO_PORT_K2->IDR & GPIO_PIN_K2) == 0
&& (GPIO_PORT_K3->IDR & GPIO_PIN_K3) != 0)
return 1;
else
return 0;
}
static uint8_t IsKeyDown2(void)
{
if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) != 0 && (GPIO_PORT_K2->IDR & GPIO_PIN_K2) != 0
&& (GPIO_PORT_K3->IDR & GPIO_PIN_K3) != 0)
return 1;
else
return 0;
}
static uint8_t IsKeyDown3(void)
{
if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) != 0 && (GPIO_PORT_K2->IDR & GPIO_PIN_K2) == 0
&& (GPIO_PORT_K3->IDR & GPIO_PIN_K3) == 0)
return 1;
else
return 0;
}
static uint8_t IsKeyDown9(void) /* K1 K2组合键 */
{
if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0 && (GPIO_PORT_K2->IDR & GPIO_PIN_K2) != 0
&& (GPIO_PORT_K3->IDR & GPIO_PIN_K3) != 0)
return 1;
else
return 0;
}
static uint8_t IsKeyDown10(void) /* K2 K3组合键 */
{
if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) != 0 && (GPIO_PORT_K2->IDR & GPIO_PIN_K2) != 0
&& (GPIO_PORT_K3->IDR & GPIO_PIN_K3) == 0)
return 1;
else
return 0;
}
#else
static uint8_t IsKeyDown1(void) {if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0) return 1;else return 0;}
static uint8_t IsKeyDown2(void) {if ((GPIO_PORT_K2->IDR & GPIO_PIN_K2) != 0) return 1;else return 0;}
static uint8_t IsKeyDown3(void) {if ((GPIO_PORT_K3->IDR & GPIO_PIN_K3) == 0) return 1;else return 0;}
static uint8_t IsKeyDown9(void) {if (IsKeyDown1() && IsKeyDown2()) return 1;else return 0;} /* K1 K2组合键 */
static uint8_t IsKeyDown10(void) {if (IsKeyDown2() && IsKeyDown3()) return 1;else return 0;} /* K2 K3组合键 */
#endif
/* 5方向摇杆 */
static uint8_t IsKeyDown4(void) {if ((GPIO_PORT_K4->IDR & GPIO_PIN_K4) == 0) return 1;else return 0;}
static uint8_t IsKeyDown5(void) {if ((GPIO_PORT_K5->IDR & GPIO_PIN_K5) == 0) return 1;else return 0;}
static uint8_t IsKeyDown6(void) {if ((GPIO_PORT_K6->IDR & GPIO_PIN_K6) == 0) return 1;else return 0;}
static uint8_t IsKeyDown7(void) {if ((GPIO_PORT_K7->IDR & GPIO_PIN_K7) == 0) return 1;else return 0;}
static uint8_t IsKeyDown8(void) {if ((GPIO_PORT_K8->IDR & GPIO_PIN_K8) == 0) return 1;else return 0;}
/*
*********************************************************************************************************
* 函 数 名: bsp_InitKey
* 功能说明: 初始化按键. 该函数被 bsp_Init() 调用。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitKey(void)
{
bsp_InitKeyVar(); /* 初始化按键变量 */
bsp_InitKeyHard(); /* 初始化按键硬件 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_PutKey
* 功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
* 形 参: _KeyCode : 按键代码
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_PutKey(uint8_t _KeyCode)
{
s_tKey.Buf[s_tKey.Write] = _KeyCode;
if (++s_tKey.Write >= KEY_FIFO_SIZE)
{
s_tKey.Write = 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_GetKey
* 功能说明: 从按键FIFO缓冲区读取一个键值。
* 形 参: 无
* 返 回 值: 按键代码
*********************************************************************************************************
*/
uint8_t bsp_GetKey(void)
{
uint8_t ret;
if (s_tKey.Read == s_tKey.Write)
{
return KEY_NONE;
}
else
{
ret = s_tKey.Buf[s_tKey.Read];
if (++s_tKey.Read >= KEY_FIFO_SIZE)
{
s_tKey.Read = 0;
}
return ret;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_GetKey2
* 功能说明: 从按键FIFO缓冲区读取一个键值。独立的读指针。
* 形 参: 无
* 返 回 值: 按键代码
*********************************************************************************************************
*/
uint8_t bsp_GetKey2(void)
{
uint8_t ret;
if (s_tKey.Read2 == s_tKey.Write)
{
return KEY_NONE;
}
else
{
ret = s_tKey.Buf[s_tKey.Read2];
if (++s_tKey.Read2 >= KEY_FIFO_SIZE)
{
s_tKey.Read2 = 0;
}
return ret;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_GetKeyState
* 功能说明: 读取按键的状态
* 形 参: _ucKeyID : 按键ID,从0开始
* 返 回 值: 1 表示按下, 0 表示未按下
*********************************************************************************************************
*/
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
{
return s_tBtn[_ucKeyID].State;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetKeyParam
* 功能说明: 设置按键参数
* 形 参:_ucKeyID : 按键ID,从0开始
* _LongTime : 长按事件时间
* _RepeatSpeed : 连发速度
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
{
s_tBtn[_ucKeyID].LongTime = _LongTime; /* 长按时间 0 表示不检测长按键事件 */
s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed; /* 按键连发的速度,0表示不支持连发 */
s_tBtn[_ucKeyID].RepeatCount = 0; /* 连发计数器 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_ClearKey
* 功能说明: 清空按键FIFO缓冲区
* 形 参:无
* 返 回 值: 按键代码
*********************************************************************************************************
*/
void bsp_ClearKey(void)
{
s_tKey.Read = s_tKey.Write;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_InitKeyHard
* 功能说明: 配置按键对应的GPIO
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyHard(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 第1步:打开GPIO时钟 */
RCC_APB2PeriphClockCmd(RCC_ALL_KEY, ENABLE);
/* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* 输入浮空模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K1;
GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K2;
GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K3;
GPIO_Init(GPIO_PORT_K3, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K4;
GPIO_Init(GPIO_PORT_K4, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K5;
GPIO_Init(GPIO_PORT_K5, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K6;
GPIO_Init(GPIO_PORT_K6, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K7;
GPIO_Init(GPIO_PORT_K7, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K8;
GPIO_Init(GPIO_PORT_K8, &GPIO_InitStructure);
}
/*
*********************************************************************************************************
* 函 数 名: bsp_InitKeyVar
* 功能说明: 初始化按键变量
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyVar(void)
{
uint8_t i;
/* 对按键FIFO读写指针清零 */
s_tKey.Read = 0;
s_tKey.Write = 0;
s_tKey.Read2 = 0;
/* 给每个按键结构体成员变量赋一组缺省值 */
for (i = 0; i < KEY_COUNT; i++)
{
s_tBtn[i].LongTime = KEY_LONG_TIME; /* 长按时间 0 表示不检测长按键事件 */
s_tBtn[i].Count = KEY_FILTER_TIME / 2; /* 计数器设置为滤波时间的一半 */
s_tBtn[i].State = 0; /* 按键缺省状态,0为未按下 */
//s_tBtn[i].KeyCodeDown = 3 * i + 1; /* 按键按下的键值代码 */
//s_tBtn[i].KeyCodeUp = 3 * i + 2; /* 按键弹起的键值代码 */
//s_tBtn[i].KeyCodeLong = 3 * i + 3; /* 按键被持续按下的键值代码 */
s_tBtn[i].RepeatSpeed = 0; /* 按键连发的速度,0表示不支持连发 */
s_tBtn[i].RepeatCount = 0; /* 连发计数器 */
}
/* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */
/* 比如,我们希望按键1按下超过1秒后,自动重发相同键值 */
s_tBtn[KID_JOY_U].LongTime = 100;
s_tBtn[KID_JOY_U].RepeatSpeed = 5; /* 每隔50ms自动发送键值 */
s_tBtn[KID_JOY_D].LongTime = 100;
s_tBtn[KID_JOY_D].RepeatSpeed = 5; /* 每隔50ms自动发送键值 */
s_tBtn[KID_JOY_L].LongTime = 100;
s_tBtn[KID_JOY_L].RepeatSpeed = 5; /* 每隔50ms自动发送键值 */
s_tBtn[KID_JOY_R].LongTime = 100;
s_tBtn[KID_JOY_R].RepeatSpeed = 5; /* 每隔50ms自动发送键值 */
/* 判断按键按下的函数 */
s_tBtn[0].IsKeyDownFunc = IsKeyDown1;
s_tBtn[1].IsKeyDownFunc = IsKeyDown2;
s_tBtn[2].IsKeyDownFunc = IsKeyDown3;
s_tBtn[3].IsKeyDownFunc = IsKeyDown4;
s_tBtn[4].IsKeyDownFunc = IsKeyDown5;
s_tBtn[5].IsKeyDownFunc = IsKeyDown6;
s_tBtn[6].IsKeyDownFunc = IsKeyDown7;
s_tBtn[7].IsKeyDownFunc = IsKeyDown8;
/* 组合键 */
s_tBtn[8].IsKeyDownFunc = IsKeyDown9;
s_tBtn[9].IsKeyDownFunc = IsKeyDown10;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_DetectKey
* 功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
* 形 参: 按键结构变量指针
* 返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
KEY_T *pBtn;
/*
如果没有初始化按键函数,则报错
if (s_tBtn[i].IsKeyDownFunc == 0)
{
printf("Fault : DetectButton(), s_tBtn[i].IsKeyDownFunc undefine");
}
*/
pBtn = &s_tBtn[i];
if (pBtn->IsKeyDownFunc())
{
if (pBtn->Count < KEY_FILTER_TIME)
{
pBtn->Count = KEY_FILTER_TIME;
}
else if(pBtn->Count < 2 * KEY_FILTER_TIME)
{
pBtn->Count++;
}
else
{
if (pBtn->State == 0)
{
pBtn->State = 1;
/* 发送按钮按下的消息 */
bsp_PutKey((uint8_t)(3 * i + 1));
}
if (pBtn->LongTime > 0)
{
if (pBtn->LongCount < pBtn->LongTime)
{
/* 发送按钮持续按下的消息 */
if (++pBtn->LongCount == pBtn->LongTime)
{
/* 键值放入按键FIFO */
bsp_PutKey((uint8_t)(3 * i + 3));
}
}
else
{
if (pBtn->RepeatSpeed > 0)
{
if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
{
pBtn->RepeatCount = 0;
/* 常按键后,每隔10ms发送1个按键 */
bsp_PutKey((uint8_t)(3 * i + 1));
}
}
}
}
}
}
else
{
if(pBtn->Count > KEY_FILTER_TIME)
{
pBtn->Count = KEY_FILTER_TIME;
}
else if(pBtn->Count != 0)
{
pBtn->Count--;
}
else
{
if (pBtn->State == 1)
{
pBtn->State = 0;
/* 发送按钮弹起的消息 */
bsp_PutKey((uint8_t)(3 * i + 2));
}
}
pBtn->LongCount = 0;
pBtn->RepeatCount = 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_KeyScan
* 功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan(void)
{
uint8_t i;
for (i = 0; i < KEY_COUNT; i++)
{
bsp_DetectKey(i);
}
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : LED指示灯驱动模块
* 文件名称 : bsp_led.c
* 版 本 : V1.0
* 说 明 : 驱动LED指示灯
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2013-02-01 armfly 正式发布
*
* Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
#include "main.h"
u8 refreshledf;
/*********************************************************************************************************
* 函 数 名: bsp_InitLed
* 功能说明: 配置LED指示灯相关的GPIO, 该函数被 bsp_Init() 调用。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 打开GPIO时钟 */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
RCC_APB2Periph_GPIOE, ENABLE);
//输出管脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_12 |
GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_10 |
GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 |
GPIO_Pin_4 | GPIO_Pin_6 |
GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 |
GPIO_Pin_4 | GPIO_Pin_8 |
GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_Init(GPIOE, &GPIO_InitStructure);
//输入管脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_9 |
GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_9 |
GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3 |
GPIO_Pin_5 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_12 |
GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3 |
GPIO_Pin_5 | GPIO_Pin_7 |
GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);
//指示灯输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void refreshled(void)
{
u32 i=1;
GPIOD->ODR ^= GPIO_Pin_9; //PD9系统指示灯,闪烁
if((switchsta&i)!=0) GPIOE->BSRR = GPIO_Pin_4; //LED1
else GPIOE->BRR = GPIO_Pin_4;
i<<=1;
if((switchsta&i)!=0) GPIOE->BSRR = GPIO_Pin_2; //LED2
else GPIOE->BRR = GPIO_Pin_2;
i<<=1;
if((switchsta&i)!=0) GPIOE->BSRR = GPIO_Pin_0; //LED3
else GPIOE->BRR = GPIO_Pin_0;
i<<=1;
if((switchsta&i)!=0) GPIOB->BSRR = GPIO_Pin_8; //LED4
else GPIOB->BRR = GPIO_Pin_8;
i<<=1;
if((switchsta&i)!=0) GPIOD->BSRR = GPIO_Pin_6; //LED5
else GPIOD->BRR = GPIO_Pin_6;
i<<=1;
if((switchsta&i)!=0) GPIOD->BSRR = GPIO_Pin_4; //LED6
else GPIOD->BRR = GPIO_Pin_4;
i<<=1;
if((switchsta&i)!=0) GPIOD->BSRR = GPIO_Pin_2; //LED7
else GPIOD->BRR = GPIO_Pin_2;
i<<=1;
if((switchsta&i)!=0) GPIOD->BSRR = GPIO_Pin_0; //LED8
else GPIOD->BRR = GPIO_Pin_0;
i<<=1;
if((switchsta&i)!=0) GPIOC->BSRR = GPIO_Pin_11; //LED9
else GPIOC->BRR = GPIO_Pin_11;
i<<=1;
if((switchsta&i)!=0) GPIOC->BSRR = GPIO_Pin_10; //LED10
else GPIOC->BRR = GPIO_Pin_10;
i<<=1;
if((switchsta&i)!=0) GPIOA->BSRR = GPIO_Pin_11; //LED11
else GPIOA->BRR = GPIO_Pin_11;
i<<=1;
if((switchsta&i)!=0) GPIOA->BSRR = GPIO_Pin_8; //LED12
else GPIOA->BRR = GPIO_Pin_8;
i<<=1;
if((switchsta&i)!=0) GPIOC->BSRR = GPIO_Pin_8; //LED13
else GPIOC->BRR = GPIO_Pin_8;
i<<=1;
if((switchsta&i)!=0) GPIOC->BSRR = GPIO_Pin_7; //LED14
else GPIOC->BRR = GPIO_Pin_7;
i<<=1;
if((switchsta&i)!=0) GPIOD->BSRR = GPIO_Pin_15; //LED15
else GPIOD->BRR = GPIO_Pin_15;
i<<=1;
if((switchsta&i)!=0) GPIOB->BSRR = GPIO_Pin_13; //LED16
else GPIOB->BRR = GPIO_Pin_13;
i<<=1;
if((switchsta&i)!=0) GPIOB->BSRR = GPIO_Pin_12; //LED17
else GPIOB->BRR = GPIO_Pin_12;
i<<=1;
if((switchsta&i)!=0) GPIOE->BSRR = GPIO_Pin_10; //LED18
else GPIOE->BRR = GPIO_Pin_10;
i<<=1;
if((switchsta&i)!=0) GPIOE->BSRR = GPIO_Pin_8; //LED19
else GPIOE->BRR = GPIO_Pin_8;
i<<=1;
if((switchsta&i)!=0) GPIOC->BSRR = GPIO_Pin_5; //LED20
else GPIOC->BRR = GPIO_Pin_5;
i<<=1;
if((switchsta&i)!=0) GPIOA->BSRR = GPIO_Pin_7; //LED21
else GPIOA->BRR = GPIO_Pin_7;
i<<=1;
if((switchsta&i)!=0) GPIOA->BSRR = GPIO_Pin_5; //LED22
else GPIOA->BRR = GPIO_Pin_5;
// i<<=1;
// if((switchsta&i)!=0) GPIOA->BSRR = GPIO_Pin_4; //LED23
// else GPIOA->BRR = GPIO_Pin_4;
//
// i<<=1;
// if((switchsta&i)!=0) GPIOA->BSRR = GPIO_Pin_2; //LED24
// else GPIOA->BRR = GPIO_Pin_2;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_LedOn
* 功能说明: 点亮指定的LED指示灯。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_LedOn(uint8_t _no)
{
/* _no--;
if (_no == 0)
{
GPIO_PORT_LED1->BRR = GPIO_PIN_LED1;
}
else if (_no == 1)
{
GPIO_PORT_LED2->BRR = GPIO_PIN_LED2;
}
else if (_no == 2)
{
GPIO_PORT_LED3->BRR = GPIO_PIN_LED3;
}
else if (_no == 3)
{
GPIO_PORT_LED4->BRR = GPIO_PIN_LED4;
}
*/
}
/*
*********************************************************************************************************
* 函 数 名: bsp_LedOff
* 功能说明: 熄灭指定的LED指示灯。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_LedOff(uint8_t _no)
{
/* _no--;
if (_no == 0)
{
GPIO_PORT_LED1->BSRR = GPIO_PIN_LED1;
}
else if (_no == 1)
{
GPIO_PORT_LED2->BSRR = GPIO_PIN_LED2;
}
else if (_no == 2)
{
GPIO_PORT_LED3->BSRR = GPIO_PIN_LED3;
}
else if (_no == 3)
{
GPIO_PORT_LED4->BSRR = GPIO_PIN_LED4;
}
*/
}
/*
*********************************************************************************************************
* 函 数 名: bsp_LedToggle
* 功能说明: 翻转指定的LED指示灯。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 按键代码
*********************************************************************************************************
*/
void bsp_LedToggle(uint8_t _no)
{
/* if (_no == 1)
{
GPIO_PORT_LED1->ODR ^= GPIO_PIN_LED1;
}
else if (_no == 2)
{
GPIO_PORT_LED2->ODR ^= GPIO_PIN_LED2;
}
else if (_no == 3)
{
GPIO_PORT_LED3->ODR ^= GPIO_PIN_LED3;
}
else if (_no == 4)
{
GPIO_PORT_LED4->ODR ^= GPIO_PIN_LED4;
}
*/
}
/*
*********************************************************************************************************
* 函 数 名: bsp_IsLedOn
* 功能说明: 判断LED指示灯是否已经点亮。
* 形 参: _no : 指示灯序号,范围 1 - 4
* 返 回 值: 1表示已经点亮,0表示未点亮
*********************************************************************************************************
*/
uint8_t bsp_IsLedOn(uint8_t _no)
{
/* if (_no == 1)
{
if ((GPIO_PORT_LED1->ODR & GPIO_PIN_LED1) == 0)
{
return 1;
}
return 0;
}
else if (_no == 2)
{
if ((GPIO_PORT_LED2->ODR & GPIO_PIN_LED2) == 0)
{
return 1;
}
return 0;
}
else if (_no == 3)
{
if ((GPIO_PORT_LED3->ODR & GPIO_PIN_LED3) == 0)
{
return 1;
}
return 0;
}
else if (_no == 4)
{
if ((GPIO_PORT_LED4->ODR & GPIO_PIN_LED4) == 0)
{
return 1;
}
return 0;
}
return 0;
*/
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : 串口中断+FIFO驱动模块
* 文件名称 : bsp_uart_fifo.c
* 版 本 : V1.0
* 说 明 : 采用串口中断+FIFO模式实现多个串口的同时访问
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2013-02-01 armfly 正式发布
* V1.1 2013-06-09 armfly FiFo结构增加TxCount成员变量,方便判断缓冲区满; 增加 清FiFo的函数
* V1.2 2014-09-29 armfly 增加RS485 MODBUS接口。接收到新字节后,直接执行回调函数。
*
* Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
#include "main.h"
/* 定义每个串口结构体变量 */
#if UART1_FIFO_EN == 1
static UART_T g_tUart1;
static uint8_t g_TxBuf1[UART1_TX_BUF_SIZE]; /* 发送缓冲区 */
static uint8_t g_RxBuf1[UART1_RX_BUF_SIZE]; /* 接收缓冲区 */
#endif
#if UART2_FIFO_EN == 1
static UART_T g_tUart2;
static uint8_t g_TxBuf2[UART2_TX_BUF_SIZE]; /* 发送缓冲区 */
static uint8_t g_RxBuf2[UART2_RX_BUF_SIZE]; /* 接收缓冲区 */
#endif
#if UART3_FIFO_EN == 1
static UART_T g_tUart3;
static uint8_t g_TxBuf3[UART3_TX_BUF_SIZE]; /* 发送缓冲区 */
static uint8_t g_RxBuf3[UART3_RX_BUF_SIZE]; /* 接收缓冲区 */
#endif
#if UART4_FIFO_EN == 1
static UART_T g_tUart4;
static uint8_t g_TxBuf4[UART4_TX_BUF_SIZE]; /* 发送缓冲区 */
static uint8_t g_RxBuf4[UART4_RX_BUF_SIZE]; /* 接收缓冲区 */
#endif
#if UART5_FIFO_EN == 1
static UART_T g_tUart5;
static uint8_t g_TxBuf5[UART5_TX_BUF_SIZE]; /* 发送缓冲区 */
static uint8_t g_RxBuf5[UART5_RX_BUF_SIZE]; /* 接收缓冲区 */
#endif
static void UartVarInit(void);
static void InitHardUart(void);
static void UartSend(UART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen);
static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte);
static void UartIRQ(UART_T *_pUart);
static void ConfigUartNVIC(void);
void RS485_InitTXE(void);
/*
*********************************************************************************************************
* 函 数 名: bsp_InitUart
* 功能说明: 初始化串口硬件,并对全局变量赋初值.
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitUart(void)
{
UartVarInit(); /* 必须先初始化全局变量,再配置硬件 */
InitHardUart(); /* 配置串口的硬件参数(波特率等) */
RS485_InitTXE(); /* 配置RS485芯片的发送使能硬件,配置为推挽输出 */
ConfigUartNVIC(); /* 配置串口中断 */
}
/*
*********************************************************************************************************
* 函 数 名: ComToUart
* 功能说明: 将COM端口号转换为UART指针
* 形 参: _ucPort: 端口号(COM1 - COM6)
* 返 回 值: uart指针
*********************************************************************************************************
*/
UART_T *ComToUart(COM_PORT_E _ucPort)
{
if (_ucPort == COM1)
{
#if UART1_FIFO_EN == 1
return &g_tUart1;
#else
return 0;
#endif
}
else if (_ucPort == COM2)
{
#if UART2_FIFO_EN == 1
return &g_tUart2;
#else
return 0;
#endif
}
else if (_ucPort == COM3)
{
#if UART3_FIFO_EN == 1
return &g_tUart3;
#else
return 0;
#endif
}
else if (_ucPort == COM4)
{
#if UART4_FIFO_EN == 1
return &g_tUart4;
#else
return 0;
#endif
}
else if (_ucPort == COM5)
{
#if UART5_FIFO_EN == 1
return &g_tUart5;
#else
return 0;
#endif
}
else
{
/* 不做任何处理 */
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: comSendBuf
* 功能说明: 向串口发送一组数据。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送
* 形 参: _ucPort: 端口号(COM1 - COM6)
* _ucaBuf: 待发送的数据缓冲区
* _usLen : 数据长度
* 返 回 值: 无
*********************************************************************************************************
*/
void comSendBuf(COM_PORT_E _ucPort, uint8_t *_ucaBuf, uint16_t _usLen)
{
UART_T *pUart;
pUart = ComToUart(_ucPort);
if (pUart == 0)
{
return;
}
/* if (pUart->SendBefor != 0)
{
pUart->SendBefor(); // 如果是RS485通信,可以在这个函数中将RS485设置为发送模式
}
*/
UartSend(pUart, _ucaBuf, _usLen);
}
/*
*********************************************************************************************************
* 函 数 名: comSendChar
* 功能说明: 向串口发送1个字节。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送
* 形 参: _ucPort: 端口号(COM1 - COM6)
* _ucByte: 待发送的数据
* 返 回 值: 无
*********************************************************************************************************
*/
void comSendChar(COM_PORT_E _ucPort, uint8_t _ucByte)
{
comSendBuf(_ucPort, &_ucByte, 1);
}
/*
*********************************************************************************************************
* 函 数 名: comGetChar
* 功能说明: 从串口缓冲区读取1字节,非阻塞。无论有无数据均立即返回
* 形 参: _ucPort: 端口号(COM1 - COM6)
* _pByte: 接收到的数据存放在这个地址
* 返 回 值: 0 表示无数据, 1 表示读取到有效字节
*********************************************************************************************************
*/
uint8_t comGetChar(COM_PORT_E _ucPort, uint8_t *_pByte)
{
UART_T *pUart;
pUart = ComToUart(_ucPort);
if (pUart == 0)
{
return 0;
}
return UartGetChar(pUart, _pByte);
}
/*
*********************************************************************************************************
* 函 数 名: comClearTxFifo
* 功能说明: 清零串口发送缓冲区
* 形 参: _ucPort: 端口号(COM1 - COM6)
* 返 回 值: 无
*********************************************************************************************************
*/
void comClearTxFifo(COM_PORT_E _ucPort)
{
UART_T *pUart;
pUart = ComToUart(_ucPort);
if (pUart == 0)
{
return;
}
pUart->usTxWrite = 0;
pUart->usTxRead = 0;
pUart->usTxCount = 0;
}
/*
*********************************************************************************************************
* 函 数 名: comClearRxFifo
* 功能说明: 清零串口接收缓冲区
* 形 参: _ucPort: 端口号(COM1 - COM6)
* 返 回 值: 无
*********************************************************************************************************
*/
void comClearRxFifo(COM_PORT_E _ucPort)
{
UART_T *pUart;
pUart = ComToUart(_ucPort);
if (pUart == 0)
{
return;
}
pUart->usRxWrite = 0;
pUart->usRxRead = 0;
pUart->usRxCount = 0;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetUart1Baud
* 功能说明: 修改UART1波特率
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetUart1Baud(uint32_t _baud)
{
USART_InitTypeDef USART_InitStructure;
/* 第2步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = _baud; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SetUart2Baud
* 功能说明: 修改UART2波特率
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetUart2Baud(uint32_t _baud)
{
USART_InitTypeDef USART_InitStructure;
/* 第2步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = _baud; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
}
/* 如果是RS485通信,请按如下格式编写函数, 我们仅举了 USART3作为RS485的例子 */
/*
*********************************************************************************************************
* 函 数 名: RS485_InitTXE
* 功能说明: 配置RS485发送使能口线 TXE
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void RS485_InitTXE(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_RS485_TXEN, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = PIN_RS485_TXEN;
GPIO_Init(PORT_RS485_TXEN, &GPIO_InitStructure);
}
/*
*********************************************************************************************************
* 函 数 名: bsp_Set485Baud
* 功能说明: 修改UART3波特率
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Set485Baud(uint32_t _baud)
{
USART_InitTypeDef USART_InitStructure;
/* 第2步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = _baud; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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(USART3, &USART_InitStructure);
}
/*
*********************************************************************************************************
* 函 数 名: RS485_SendBefor
* 功能说明: 发送数据前的准备工作。对于RS485通信,请设置RS485芯片为发送状态,
* 并修改 UartVarInit()中的函数指针等于本函数名,比如 g_tUart2.SendBefor = RS485_SendBefor
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendBefor(void)
{
// RS485_TX_EN(); /* 切换RS485收发芯片为发送模式 */
}
/*
*********************************************************************************************************
* 函 数 名: RS485_SendOver
* 功能说明: 发送一串数据结束后的善后处理。对于RS485通信,请设置RS485芯片为接收状态,
* 并修改 UartVarInit()中的函数指针等于本函数名,比如 g_tUart2.SendOver = RS485_SendOver
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendOver(void)
{
// RS485_RX_EN(); /* 切换RS485收发芯片为接收模式 */
}
/*
*********************************************************************************************************
* 函 数 名: RS485_SendBuf
* 功能说明: 通过RS485芯片发送一串数据。注意,本函数不等待发送完毕。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendBuf(uint8_t *_ucaBuf, uint16_t _usLen)
{
comSendBuf(COM3, _ucaBuf, _usLen);
}
/*
*********************************************************************************************************
* 函 数 名: RS485_SendStr
* 功能说明: 向485总线发送一个字符串
* 形 参: _pBuf 数据缓冲区
* _ucLen 数据长度
* 返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendStr(char *_pBuf)
{
RS485_SendBuf((uint8_t *)_pBuf, strlen(_pBuf));
}
/*
*********************************************************************************************************
* 函 数 名: RS485_ReciveNew
* 功能说明: 接收到新的数据
* 形 参: _byte 接收到的新数据
* 返 回 值: 无
*********************************************************************************************************
*/
extern void MODH_ReciveNew(uint8_t _data);
void RS485_ReciveNew(uint8_t _byte)
{
MODH_ReciveNew(_byte);
}
/*
*********************************************************************************************************
* 函 数 名: UartVarInit
* 功能说明: 初始化串口相关的变量
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void UartVarInit(void)
{
#if UART1_FIFO_EN == 1
g_tUart1.uart = USART1; /* STM32 串口设备 */
g_tUart1.pTxBuf = g_TxBuf1; /* 发送缓冲区指针 */
g_tUart1.pRxBuf = g_RxBuf1; /* 接收缓冲区指针 */
g_tUart1.usTxBufSize = UART1_TX_BUF_SIZE; /* 发送缓冲区大小 */
g_tUart1.usRxBufSize = UART1_RX_BUF_SIZE; /* 接收缓冲区大小 */
g_tUart1.usTxWrite = 0; /* 发送FIFO写索引 */
g_tUart1.usTxRead = 0; /* 发送FIFO读索引 */
g_tUart1.usRxWrite = 0; /* 接收FIFO写索引 */
g_tUart1.usRxRead = 0; /* 接收FIFO读索引 */
g_tUart1.usRxCount = 0; /* 接收到的新数据个数 */
g_tUart1.usTxCount = 0; /* 待发送的数据个数 */
g_tUart1.SendBefor = 0; /* 发送数据前的回调函数 */
g_tUart1.SendOver = 0; /* 发送完毕后的回调函数 */
g_tUart1.ReciveNew = 0; /* 接收到新数据后的回调函数 */
#endif
#if UART2_FIFO_EN == 1
g_tUart2.uart = USART2; /* STM32 串口设备 */
g_tUart2.pTxBuf = g_TxBuf2; /* 发送缓冲区指针 */
g_tUart2.pRxBuf = g_RxBuf2; /* 接收缓冲区指针 */
g_tUart2.usTxBufSize = UART2_TX_BUF_SIZE; /* 发送缓冲区大小 */
g_tUart2.usRxBufSize = UART2_RX_BUF_SIZE; /* 接收缓冲区大小 */
g_tUart2.usTxWrite = 0; /* 发送FIFO写索引 */
g_tUart2.usTxRead = 0; /* 发送FIFO读索引 */
g_tUart2.usRxWrite = 0; /* 接收FIFO写索引 */
g_tUart2.usRxRead = 0; /* 接收FIFO读索引 */
g_tUart2.usRxCount = 0; /* 接收到的新数据个数 */
g_tUart2.usTxCount = 0; /* 待发送的数据个数 */
g_tUart2.SendBefor = 0; /* 发送数据前的回调函数 */
g_tUart2.SendOver = 0; /* 发送完毕后的回调函数 */
g_tUart2.ReciveNew = 0; /* 接收到新数据后的回调函数 */
#endif
#if UART3_FIFO_EN == 1
g_tUart3.uart = USART3; /* STM32 串口设备 */
g_tUart3.pTxBuf = g_TxBuf3; /* 发送缓冲区指针 */
g_tUart3.pRxBuf = g_RxBuf3; /* 接收缓冲区指针 */
g_tUart3.usTxBufSize = UART3_TX_BUF_SIZE; /* 发送缓冲区大小 */
g_tUart3.usRxBufSize = UART3_RX_BUF_SIZE; /* 接收缓冲区大小 */
g_tUart3.usTxWrite = 0; /* 发送FIFO写索引 */
g_tUart3.usTxRead = 0; /* 发送FIFO读索引 */
g_tUart3.usRxWrite = 0; /* 接收FIFO写索引 */
g_tUart3.usRxRead = 0; /* 接收FIFO读索引 */
g_tUart3.usRxCount = 0; /* 接收到的新数据个数 */
g_tUart3.usTxCount = 0; /* 待发送的数据个数 */
g_tUart3.SendBefor = RS485_SendBefor; /* 发送数据前的回调函数 */
g_tUart3.SendOver = RS485_SendOver; /* 发送完毕后的回调函数 */
g_tUart3.ReciveNew = RS485_ReciveNew; /* 接收到新数据后的回调函数 */
#endif
#if UART4_FIFO_EN == 1
g_tUart4.uart = UART4; /* STM32 串口设备 */
g_tUart4.pTxBuf = g_TxBuf4; /* 发送缓冲区指针 */
g_tUart4.pRxBuf = g_RxBuf4; /* 接收缓冲区指针 */
g_tUart4.usTxBufSize = UART4_TX_BUF_SIZE; /* 发送缓冲区大小 */
g_tUart4.usRxBufSize = UART4_RX_BUF_SIZE; /* 接收缓冲区大小 */
g_tUart4.usTxWrite = 0; /* 发送FIFO写索引 */
g_tUart4.usTxRead = 0; /* 发送FIFO读索引 */
g_tUart4.usRxWrite = 0; /* 接收FIFO写索引 */
g_tUart4.usRxRead = 0; /* 接收FIFO读索引 */
g_tUart4.usRxCount = 0; /* 接收到的新数据个数 */
g_tUart4.usTxCount = 0; /* 待发送的数据个数 */
g_tUart4.SendBefor = 0; /* 发送数据前的回调函数 */
g_tUart4.SendOver = 0; /* 发送完毕后的回调函数 */
g_tUart4.ReciveNew = 0; /* 接收到新数据后的回调函数 */
#endif
#if UART5_FIFO_EN == 1
g_tUart5.uart = UART5; /* STM32 串口设备 */
g_tUart5.pTxBuf = g_TxBuf5; /* 发送缓冲区指针 */
g_tUart5.pRxBuf = g_RxBuf5; /* 接收缓冲区指针 */
g_tUart5.usTxBufSize = UART5_TX_BUF_SIZE; /* 发送缓冲区大小 */
g_tUart5.usRxBufSize = UART5_RX_BUF_SIZE; /* 接收缓冲区大小 */
g_tUart5.usTxWrite = 0; /* 发送FIFO写索引 */
g_tUart5.usTxRead = 0; /* 发送FIFO读索引 */
g_tUart5.usRxWrite = 0; /* 接收FIFO写索引 */
g_tUart5.usRxRead = 0; /* 接收FIFO读索引 */
g_tUart5.usRxCount = 0; /* 接收到的新数据个数 */
g_tUart5.usTxCount = 0; /* 待发送的数据个数 */
g_tUart5.SendBefor = 0; /* 发送数据前的回调函数 */
g_tUart5.SendOver = 0; /* 发送完毕后的回调函数 */
g_tUart5.ReciveNew = 0; /* 接收到新数据后的回调函数 */
#endif
#if UART6_FIFO_EN == 1
g_tUart6.uart = USART6; /* STM32 串口设备 */
g_tUart6.pTxBuf = g_TxBuf6; /* 发送缓冲区指针 */
g_tUart6.pRxBuf = g_RxBuf6; /* 接收缓冲区指针 */
g_tUart6.usTxBufSize = UART6_TX_BUF_SIZE; /* 发送缓冲区大小 */
g_tUart6.usRxBufSize = UART6_RX_BUF_SIZE; /* 接收缓冲区大小 */
g_tUart6.usTxWrite = 0; /* 发送FIFO写索引 */
g_tUart6.usTxRead = 0; /* 发送FIFO读索引 */
g_tUart6.usRxWrite = 0; /* 接收FIFO写索引 */
g_tUart6.usRxRead = 0; /* 接收FIFO读索引 */
g_tUart6.usRxCount = 0; /* 接收到的新数据个数 */
g_tUart6.usTxCount = 0; /* 待发送的数据个数 */
g_tUart6.SendBefor = 0; /* 发送数据前的回调函数 */
g_tUart6.SendOver = 0; /* 发送完毕后的回调函数 */
g_tUart6.ReciveNew = 0; /* 接收到新数据后的回调函数 */
#endif
}
/*
*********************************************************************************************************
* 函 数 名: InitHardUart
* 功能说明: 配置串口的硬件参数(波特率,数据位,停止位,起始位,校验位,中断使能)适合于STM32-F4开发板
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void InitHardUart(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
#if UART1_FIFO_EN == 1 /* 串口1 TX = PA9 RX = PA10 或 TX = PB6 RX = PB7*/
/* 第1步:打开GPIO和USART部件的时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* 第2步:将USART Tx的GPIO配置为推挽复用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第3步:将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第4步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = UART1_BAUD; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); /* 使能接收中断 */
/*
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
注意: 不要在此处打开发送中断
发送中断使能在SendUart()函数打开
*/
USART_Cmd(USART1, ENABLE); /* 使能串口 */
/* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
如下语句解决第1个字节无法正确发送出去的问题 */
USART_ClearFlag(USART1, USART_FLAG_TC); /* 清发送完成标志,Transmission Complete flag */
#endif
#if UART2_FIFO_EN == 1 /* 串口2 TX = PA2, RX = PA3 */
/* 第1步:打开GPIO和USART部件的时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* 第2步:将USART Tx的GPIO配置为推挽复用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第3步:将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第3步已经做了,因此这步可以不做
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
*/
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第4步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = UART2_BAUD; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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_Tx | USART_Mode_Rx; /* 仅选择接收模式 */
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); /* 使能接收中断 */
/*
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
注意: 不要在此处打开发送中断
发送中断使能在SendUart()函数打开
*/
USART_Cmd(USART2, ENABLE); /* 使能串口 */
/* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
如下语句解决第1个字节无法正确发送出去的问题 */
USART_ClearFlag(USART2, USART_FLAG_TC); /* 清发送完成标志,Transmission Complete flag */
#endif
#if UART3_FIFO_EN == 1 /* 串口3 TX = PB10 RX = PB11 */
/* 配置 PB2为推挽输出,用于切换 RS485芯片的收发状态 */
/* {
RCC_APB2PeriphClockCmd(RCC_RS485_TXEN, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = PIN_RS485_TXEN;
GPIO_Init(PORT_RS485_TXEN, &GPIO_InitStructure);
}
*/
/* 第1步: 开启GPIO和UART时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
/* 第2步:将USART Tx的GPIO配置为推挽复用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 第3步:将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 第3步已经做了,因此这步可以不做
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
*/
GPIO_Init(GPIOB, &GPIO_InitStructure);
//电能表使用的是2400 1bit起始位 8bit数据 1od效验 1停止。
/* 第4步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = 2400; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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(USART3, &USART_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); /* 使能接收中断 */
/*
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
注意: 不要在此处打开发送中断
发送中断使能在SendUart()函数打开
*/
USART_Cmd(USART3, ENABLE); /* 使能串口 */
/* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
如下语句解决第1个字节无法正确发送出去的问题 */
USART_ClearFlag(USART3, USART_FLAG_TC); /* 清发送完成标志,Transmission Complete flag */
#endif
#if UART4_FIFO_EN == 1 /* 串口4 TX = PC10 RX = PC11 */
/* 第1步: 开启GPIO和UART时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
/* 第2步:将USART Tx的GPIO配置为推挽复用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* 第3步:将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* 第4步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = UART4_BAUD; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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(UART4, &USART_InitStructure);
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE); /* 使能接收中断 */
/*
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
注意: 不要在此处打开发送中断
发送中断使能在SendUart()函数打开
*/
USART_Cmd(UART4, ENABLE); /* 使能串口 */
/* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
如下语句解决第1个字节无法正确发送出去的问题 */
USART_ClearFlag(UART4, USART_FLAG_TC); /* 清发送完成标志,Transmission Complete flag */
#endif
#if UART5_FIFO_EN == 1 /* 串口5 TX = PC12 RX = PD2 */
/* 第1步: 开启GPIO和UART时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
/* 第2步:将USART Tx的GPIO配置为推挽复用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* 第3步:将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* 第4步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = UART5_BAUD; /* 波特率 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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(UART5, &USART_InitStructure);
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE); /* 使能接收中断 */
/*
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
注意: 不要在此处打开发送中断
发送中断使能在SendUart()函数打开
*/
USART_Cmd(UART5, ENABLE); /* 使能串口 */
/* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
如下语句解决第1个字节无法正确发送出去的问题 */
USART_ClearFlag(UART5, USART_FLAG_TC); /* 清发送完成标志,Transmission Complete flag */
#endif
}
/*
*********************************************************************************************************
* 函 数 名: ConfigUartNVIC
* 功能说明: 配置串口硬件中断.
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void ConfigUartNVIC(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure the NVIC Preemption Priority Bits */
/* NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); --- 在 bsp.c 中 bsp_Init() 中配置中断优先级组 */
#if UART1_FIFO_EN == 1
/* 使能串口1中断 */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
#if UART2_FIFO_EN == 1
/* 使能串口2中断 */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
#if UART3_FIFO_EN == 1
/* 使能串口3中断t */
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
#if UART4_FIFO_EN == 1
/* 使能串口4中断t */
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
#if UART5_FIFO_EN == 1
/* 使能串口5中断t */
NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
#if UART6_FIFO_EN == 1
/* 使能串口6中断t */
NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
}
/*
*********************************************************************************************************
* 函 数 名: UartSend
* 功能说明: 填写数据到UART发送缓冲区,并启动发送中断。中断处理函数发送完毕后,自动关闭发送中断
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void UartSend(UART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen)
{
uint16_t i;
for (i = 0; i < _usLen; i++)
{
/* 如果发送缓冲区已经满了,则等待缓冲区空 */
#if 0
/*
在调试GPRS例程时,下面的代码出现死机,while 死循环
原因: 发送第1个字节时 _pUart->usTxWrite = 1;_pUart->usTxRead = 0;
将导致while(1) 无法退出
*/
while (1)
{
uint16_t usRead;
DISABLE_INT();
usRead = _pUart->usTxRead;
ENABLE_INT();
if (++usRead >= _pUart->usTxBufSize)
{
usRead = 0;
}
if (usRead != _pUart->usTxWrite)
{
break;
}
}
#else
/* 当 _pUart->usTxBufSize == 1 时, 下面的函数会死掉(待完善) */
while (1)
{
__IO uint16_t usCount;
DISABLE_INT();
usCount = _pUart->usTxCount;
ENABLE_INT();
if (usCount < _pUart->usTxBufSize)
{
break;
}
}
#endif
/* 将新数据填入发送缓冲区 */
_pUart->pTxBuf[_pUart->usTxWrite] = _ucaBuf[i];
DISABLE_INT();
if (++_pUart->usTxWrite >= _pUart->usTxBufSize)
{
_pUart->usTxWrite = 0;
}
_pUart->usTxCount++;
ENABLE_INT();
}
USART_ITConfig(_pUart->uart, USART_IT_TXE, ENABLE);
}
/*
*********************************************************************************************************
* 函 数 名: UartGetChar
* 功能说明: 从串口接收缓冲区读取1字节数据 (用于主程序调用)
* 形 参: _pUart : 串口设备
* _pByte : 存放读取数据的指针
* 返 回 值: 0 表示无数据 1表示读取到数据
*********************************************************************************************************
*/
static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte)
{
uint16_t usCount;
/* usRxWrite 变量在中断函数中被改写,主程序读取该变量时,必须进行临界区保护 */
DISABLE_INT();
usCount = _pUart->usRxCount;
ENABLE_INT();
/* 如果读和写索引相同,则返回0 */
//if (_pUart->usRxRead == usRxWrite)
if (usCount == 0) /* 已经没有数据 */
{
return 0;
}
else
{
*_pByte = _pUart->pRxBuf[_pUart->usRxRead]; /* 从串口接收FIFO取1个数据 */
/* 改写FIFO读索引 */
DISABLE_INT();
if (++_pUart->usRxRead >= _pUart->usRxBufSize)
{
_pUart->usRxRead = 0;
}
_pUart->usRxCount--;
ENABLE_INT();
return 1;
}
}
/*
*********************************************************************************************************
* 函 数 名: UartIRQ
* 功能说明: 供中断服务程序调用,通用串口中断处理函数
* 形 参: _pUart : 串口设备
* 返 回 值: 无
*********************************************************************************************************
*/
static void UartIRQ(UART_T *_pUart)
{
/* 处理接收中断 */
if (USART_GetITStatus(_pUart->uart, USART_IT_RXNE) != RESET)
{
/* 从串口接收数据寄存器读取数据存放到接收FIFO */
uint8_t ch;
ch = USART_ReceiveData(_pUart->uart);
_pUart->pRxBuf[_pUart->usRxWrite] = ch;
if (++_pUart->usRxWrite >= _pUart->usRxBufSize)
{
_pUart->usRxWrite = 0;
}
if (_pUart->usRxCount < _pUart->usRxBufSize)
{
_pUart->usRxCount++;
}
/* 回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记 */
//if (_pUart->usRxWrite == _pUart->usRxRead)
//if (_pUart->usRxCount == 1)
{
if (_pUart->ReciveNew)
{
_pUart->ReciveNew(ch);
}
}
}
/* 处理发送缓冲区空中断 */
if (USART_GetITStatus(_pUart->uart, USART_IT_TXE) != RESET)
{
//if (_pUart->usTxRead == _pUart->usTxWrite)
if (_pUart->usTxCount == 0)
{
/* 发送缓冲区的数据已取完时, 禁止发送缓冲区空中断 (注意:此时最后1个数据还未真正发送完毕)*/
USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);
/* 使能数据发送完毕中断 */
USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);
}
else
{
/* 从发送FIFO取1个字节写入串口发送数据寄存器 */
USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
if (++_pUart->usTxRead >= _pUart->usTxBufSize)
{
_pUart->usTxRead = 0;
}
_pUart->usTxCount--;
}
}
/* 数据bit位全部发送完毕的中断 */
else if (USART_GetITStatus(_pUart->uart, USART_IT_TC) != RESET)
{
//if (_pUart->usTxRead == _pUart->usTxWrite)
if (_pUart->usTxCount == 0)
{
/* 如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断 */
USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE);
/* 回调函数, 一般用来处理RS485通信,将RS485芯片设置为接收模式,避免抢占总线 */
if (_pUart->SendOver)
{
_pUart->SendOver();
}
}
else
{
/* 正常情况下,不会进入此分支 */
/* 如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器 */
USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
if (++_pUart->usTxRead >= _pUart->usTxBufSize)
{
_pUart->usTxRead = 0;
}
_pUart->usTxCount--;
}
}
}
/*
*********************************************************************************************************
* 函 数 名: USART1_IRQHandler USART2_IRQHandler USART3_IRQHandler UART4_IRQHandler UART5_IRQHandler
* 功能说明: USART中断服务程序
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
#if UART1_FIFO_EN == 1
u8 uartrx_buffer1[64];
u16 uartrx_counter1;
u8 uartrxdata1;
u8 uarttx_buffer1[64];
u16 curtxlen1,curindex1; //当前发送的
//和校验,所用到的累加函数,参数数组的首地址,长度,返回值 u8型的和
u8 getsum(u8 *buff,u16 length)
{
u8 sum=0;
u16 i;
for(i=0;i<length;i++)
{
sum+=*(buff+i);
}
return sum;
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uartrxdata1=USART1->DR;
uartrx_buffer1[uartrx_counter1]=uartrxdata1;
uartrx_counter1++;
if(uartrx_counter1==2) //滤帧头 A5 5A
{
if(((uartrx_buffer1[0]!=0xa5)||(uartrx_buffer1[1]!=0x5a)))
{
if(uartrx_buffer1[1]==0xa5)
{
uartrx_buffer1[0]=0xa5; uartrx_counter1=1;
}
else
{
uartrx_counter1=0;
}
}
}
if(uartrx_counter1>=10) //对于大于6的长度的 才去检查通信的完整性 ,结尾是 CC 33 C3 3C
{
if(uartrx_buffer1[uartrx_counter1-1]==0x3c) //查尾
{
if((uartrx_buffer1[uartrx_counter1-2]==0xc3)&&(uartrx_buffer1[uartrx_counter1-3]==0x33)&&(uartrx_buffer1[uartrx_counter1-4]==0xcc))
{
//如果通过了和校验,才进入处理 //20120519 界面切换期间,不通信,因为共用了uartrxmry1[group]
// if((uartrx_buffer1[uartrx_counter1-6]==getsum(uartrx_buffer1,(uartrx_counter1-6)))&&(uartrx_buffer1[uartrx_counter1-5]==((uartrx_counter1-6)&0xff)))
// {
// //处理
dtureceivetask();
uartrx_counter1=0;
// }
// else
// {
// uartrx_counter1=0;
// }
}
}
}
}
else if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
{
curindex1++;
if(curindex1<curtxlen1)
{
while (!(USART1->SR & USART_FLAG_TXE));
USART_SendData(USART1, uarttx_buffer1[curindex1]);
}
else
{
curindex1=0; curtxlen1=0; //发送完成本帧,首先将长度清零
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
}
void dtusendtask(void)
{
if(dtusta==0) //发送注册帧
{
uarttx_buffer1[0]=0xa5; uarttx_buffer1[1]=0x5a; uarttx_buffer1[2]=0x01;
uarttx_buffer1[3]=devicenum>>24; uarttx_buffer1[4]=devicenum>>16;
uarttx_buffer1[5]=devicenum>>8; uarttx_buffer1[6]=devicenum;
uarttx_buffer1[7]=getsum(uarttx_buffer1,7);
uarttx_buffer1[8]=7;
uarttx_buffer1[9]=0xcc; uarttx_buffer1[10]=0x33;
uarttx_buffer1[11]=0xc3; uarttx_buffer1[12]=0x3c;
//中断发送出去
while (!(USART1->SR & USART_FLAG_TXE));
USART_SendData(USART1, uarttx_buffer1[0]);
curtxlen1=13; curindex1=0;
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
else if(dtusta==1) //发送数据帧
{
uarttx_buffer1[0]=0xa5; uarttx_buffer1[1]=0x5a; uarttx_buffer1[2]=0x03;
uarttx_buffer1[3]=devicenum>>24; uarttx_buffer1[4]=devicenum>>16;
uarttx_buffer1[5]=devicenum>>8; uarttx_buffer1[6]=devicenum;
uarttx_buffer1[7]=g_tRTC.Year-2000;
uarttx_buffer1[8]=g_tRTC.Mon;
uarttx_buffer1[9]=g_tRTC.Day;
uarttx_buffer1[10]=g_tRTC.Hour;
uarttx_buffer1[11]=g_tRTC.Min;
uarttx_buffer1[12]=g_tRTC.Sec;
uarttx_buffer1[13]=(u16)(UA*10)>>8; uarttx_buffer1[14]=(u16)(UA*10);
uarttx_buffer1[15]=(u16)(UB*10)>>8; uarttx_buffer1[16]=(u16)(UB*10);
uarttx_buffer1[17]=(u16)(UC*10)>>8; uarttx_buffer1[18]=(u16)(UC*10);
uarttx_buffer1[19]=(u16)(IA*10)>>8; uarttx_buffer1[20]=(u16)(IA*10);
uarttx_buffer1[21]=(u16)(IB*10)>>8; uarttx_buffer1[22]=(u16)(IB*10);
uarttx_buffer1[23]=(u16)(IC*10)>>8; uarttx_buffer1[24]=(u16)(IC*10);
uarttx_buffer1[25]=(u16)P1>>24; uarttx_buffer1[26]=(u16)P1>>16;
uarttx_buffer1[27]=(u16)P1>>8; uarttx_buffer1[28]=(u16)P1>>0;
uarttx_buffer1[29]=(u16)P2>>24; uarttx_buffer1[30]=(u16)P2>>16;
uarttx_buffer1[31]=(u16)P2>>8; uarttx_buffer1[32]=(u16)P2>>0;
uarttx_buffer1[33]=(u16)P3>>24; uarttx_buffer1[34]=(u16)P3>>16;
uarttx_buffer1[35]=(u16)P3>>8; uarttx_buffer1[36]=(u16)P3>>0;
uarttx_buffer1[37]=(u16)(PF*100)>>8; uarttx_buffer1[38]=(u16)(PF*100);
uarttx_buffer1[39]=(u16)(FREQ*100)>>8; uarttx_buffer1[40]=(u16)(FREQ*100);
uarttx_buffer1[41]=switchsta>>24; uarttx_buffer1[42]=switchsta>>16;
uarttx_buffer1[43]=switchsta>>8; uarttx_buffer1[44]=switchsta;
uarttx_buffer1[45]=(u32)POWER1>>24; uarttx_buffer1[46]=(u32)POWER1>>16;
uarttx_buffer1[47]=(u32)POWER1>>8; uarttx_buffer1[48]=(u32)POWER1>>0;
uarttx_buffer1[49]=(u32)POWER2>>24; uarttx_buffer1[50]=(u32)POWER2>>16;
uarttx_buffer1[51]=(u32)POWER2>>8; uarttx_buffer1[52]=(u32)POWER2>>0;
uarttx_buffer1[53]=getsum(uarttx_buffer1,53);
uarttx_buffer1[54]=53;
uarttx_buffer1[55]=0xcc; uarttx_buffer1[56]=0x33;
uarttx_buffer1[57]=0xc3; uarttx_buffer1[58]=0x3c;
//中断发送出去
while (!(USART1->SR & USART_FLAG_TXE));
USART_SendData(USART1, uarttx_buffer1[0]);
curtxlen1=59; curindex1=0;
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
}
void dtureceivetask(void)
{
switch(uartrx_buffer1[2])
{
case 0x25: //系统主机编号设置 保存主机编号和直流偏置ad值
{
u32 getdevicenum;
getdevicenum=0;
getdevicenum+=uartrx_buffer1[3]; getdevicenum<<=8;
getdevicenum+=uartrx_buffer1[4]; getdevicenum<<=8;
getdevicenum+=uartrx_buffer1[5]; getdevicenum<<=8;
getdevicenum+=uartrx_buffer1[6];
if(1) //对设备编码的合法性进行一个初步判断
{
u8 buff[6];
devicenum=getdevicenum; //1b保存id号码
//保存到eeprom
buff[0]=devicenum>>24; buff[1]=devicenum>>16;
buff[2]=devicenum>>8; buff[3]=devicenum>>0;
ee_WriteBytes(buff,4,4);
}
break;
}
case 0x02: //服务器下行注册许可
{
u32 getdevicenum;
getdevicenum=0;
getdevicenum+=uartrx_buffer1[3]; getdevicenum<<=8;
getdevicenum+=uartrx_buffer1[4]; getdevicenum<<=8;
getdevicenum+=uartrx_buffer1[5]; getdevicenum<<=8;
getdevicenum+=uartrx_buffer1[6];
if(devicenum==getdevicenum)
{
if(uartrx_buffer1[7]>0) //如果注册通过 0未通过 1通过
{
dtusta=1;
LOADUPTM=uartrx_buffer1[14]; //使用系统下发的周期
//rtc的时间设置
RTC_WriteClock((uartrx_buffer1[8]+2000), uartrx_buffer1[9], uartrx_buffer1[10],
uartrx_buffer1[11], uartrx_buffer1[12], uartrx_buffer1[13]);
}
}
break;
}
}
}
#endif
#if UART2_FIFO_EN == 1
void USART2_IRQHandler(void)
{
UartIRQ(&g_tUart2);
}
#endif
#if UART3_FIFO_EN == 1
void USART3_IRQHandler(void)
{
UartIRQ(&g_tUart3);
}
#endif
#if UART4_FIFO_EN == 1
void UART4_IRQHandler(void)
{
UartIRQ(&g_tUart4);
}
#endif
#if UART5_FIFO_EN == 1
void UART5_IRQHandler(void)
{
UartIRQ(&g_tUart5);
}
#endif
#if UART6_FIFO_EN == 1
void USART6_IRQHandler(void)
{
UartIRQ(&g_tUart6);
}
#endif
/*
*********************************************************************************************************
*
* 模块名称 : 定时器模块
* 文件名称 : bsp_timer.c
* 版 本 : V1.3
* 说 明 : 配置systick定时器作为系统滴答定时器。缺省定时周期为1ms。
*
* 实现了多个软件定时器供主程序使用(精度1ms), 可以通过修改 TMR_COUNT 增减定时器个数
* 实现了ms级别延迟函数(精度1ms) 和us级延迟函数
* 实现了系统运行时间函数(1ms单位)
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2013-02-01 armfly 正式发布
* V1.1 2013-06-21 armfly 增加us级延迟函数 bsp_DelayUS
* V1.2 2014-09-07 armfly 增加TIM4 硬件定时中断,实现us级别定时.20us - 16秒
* V1.3 2015-04-06 armfly 增加 bsp_CheckRunTime(int32_t _LastTime) 用来计算时间差值
* V1.4 2015-05-22 armfly 完善 bsp_InitHardTimer() ,增加条件编译选择TIM2-5
*
* Copyright (C), 2015-2016, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
#include "main.h"
/*
定义用于硬件定时器的TIM, 可以使 TIM2 - TIM5
TIM3 和TIM4 是16位
TIM2 和TIM5 是16位 (103是16位, 407是32位)
*/
#define USE_TIM2
//#define USE_TIM3
//#define USE_TIM4
//#define USE_TIM5
#ifdef USE_TIM2
#define TIM_HARD TIM2
#define TIM_HARD_IRQn TIM2_IRQn
#define TIM_HARD_RCC RCC_APB1Periph_TIM2
#endif
#ifdef USE_TIM3
#define TIM_HARD TIM3
#define TIM_HARD_IRQn TIM3_IRQn
#define TIM_HARD_RCC RCC_APB1Periph_TIM3
#endif
#ifdef USE_TIM4
#define TIM_HARD TIM4
#define TIM_HARD_IRQn TIM4_IRQn
#define TIM_HARD_RCC RCC_APB1Periph_TIM4
#endif
#ifdef USE_TIM5
#define TIM_HARD TIM5
#define TIM_HARD_IRQn TIM5_IRQn
#define TIM_HARD_RCC RCC_APB1Periph_TIM5
#endif
/* 这2个全局变量转用于 bsp_DelayMS() 函数 */
static volatile uint32_t s_uiDelayCount = 0;
static volatile uint8_t s_ucTimeOutFlag = 0;
/* 定于软件定时器结构体变量 */
static SOFT_TMR s_tTmr[TMR_COUNT];
/*
全局运行时间,单位1ms
最长可以表示 24.85天,如果你的产品连续运行时间超过这个数,则必须考虑溢出问题
*/
__IO int32_t g_iRunTime = 0;
static void bsp_SoftTimerDec(SOFT_TMR *_tmr);
/* 保存 TIM定时中断到后执行的回调函数指针 */
static void (*s_TIM_CallBack1)(void);
static void (*s_TIM_CallBack2)(void);
static void (*s_TIM_CallBack3)(void);
static void (*s_TIM_CallBack4)(void);
/*
*********************************************************************************************************
* 函 数 名: bsp_InitTimer
* 功能说明: 配置systick中断,并初始化软件定时器变量
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitTimer(void)
{
uint8_t i;
/* 清零所有的软件定时器 */
for (i = 0; i < TMR_COUNT; i++)
{
s_tTmr[i].Count = 0;
s_tTmr[i].PreLoad = 0;
s_tTmr[i].Flag = 0;
s_tTmr[i].Mode = TMR_ONCE_MODE; /* 缺省是1次性工作模式 */
}
/*
配置systic中断周期为1ms,并启动systick中断。
SystemCoreClock 是固件中定义的系统内核时钟,对于STM32F4XX,一般为 168MHz
SysTick_Config() 函数的形参表示内核时钟多少个周期后触发一次Systick定时中断.
-- SystemCoreClock / 1000 表示定时频率为 1000Hz, 也就是定时周期为 1ms
-- SystemCoreClock / 500 表示定时频率为 500Hz, 也就是定时周期为 2ms
-- SystemCoreClock / 2000 表示定时频率为 2000Hz, 也就是定时周期为 500us
对于常规的应用,我们一般取定时周期1ms。对于低速CPU或者低功耗应用,可以设置定时周期为 10ms
*/
SysTick_Config(SystemCoreClock / 1000);
#if defined (USE_TIM2) || defined (USE_TIM3) || defined (USE_TIM4) || defined (USE_TIM5)
bsp_InitHardTimer();
#endif
}
/*
*********************************************************************************************************
* 函 数 名: SysTick_ISR
* 功能说明: SysTick中断服务程序,每隔1ms进入1次
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
extern void bsp_RunPer1ms(void);
extern void bsp_RunPer10ms(void);
void SysTick_ISR(void)
{
static uint8_t s_count = 0;
uint8_t i;
static u16 sectm; //秒计数器
/* 每隔1ms进来1次 (仅用于 bsp_DelayMS) */
if (s_uiDelayCount > 0)
{
if (--s_uiDelayCount == 0)
{
s_ucTimeOutFlag = 1;
}
}
/* 每隔1ms,对软件定时器的计数器进行减一操作 */
for (i = 0; i < TMR_COUNT; i++)
{
bsp_SoftTimerDec(&s_tTmr[i]);
}
/* 全局运行时间每1ms增1 */
g_iRunTime++;
if (g_iRunTime == 0x7FFFFFFF) /* 这个变量是 int32_t 类型,最大数为 0x7FFFFFFF */
{
g_iRunTime = 0;
}
bsp_RunPer1ms(); /* 每隔1ms调用一次此函数,此函数在 bsp.c */
if (++s_count >= 10)
{
s_count = 0;
// bsp_RunPer10ms(); /* 每隔10ms调用一次此函数,此函数在 bsp.c */
}
sectm++;
if(sectm>1000) //秒计数器
{
sectm=0;
modebustxtm++;
dtutxtm++;
}
senseflag=1;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_SoftTimerDec
* 功能说明: 每隔1ms对所有定时器变量减1。必须被SysTick_ISR周期性调用。
* 形 参: _tmr : 定时器变量指针
* 返 回 值: 无
*********************************************************************************************************
*/
static void bsp_SoftTimerDec(SOFT_TMR *_tmr)
{
if (_tmr->Count > 0)
{
/* 如果定时器变量减到1则设置定时器到达标志 */
if (--_tmr->Count == 0)
{
_tmr->Flag = 1;
/* 如果是自动模式,则自动重装计数器 */
if(_tmr->Mode == TMR_AUTO_MODE)
{
_tmr->Count = _tmr->PreLoad;
}
}
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_DelayMS
* 功能说明: ms级延迟,延迟精度为正负1ms
* 形 参: n : 延迟长度,单位1 ms。 n 应大于2
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_DelayMS(uint32_t n)
{
if (n == 0)
{
return;
}
else if (n == 1)
{
n = 2;
}
DISABLE_INT(); /* 关中断 */
s_uiDelayCount = n;
s_ucTimeOutFlag = 0;
ENABLE_INT(); /* 开中断 */
while (1)
{
bsp_Idle(); /* CPU空闲执行的操作, 见 bsp.c 和 bsp.h 文件 */
/*
等待延迟时间到
注意:编译器认为 s_ucTimeOutFlag = 0,所以可能优化错误,因此 s_ucTimeOutFlag 变量必须申明为 volatile
*/
if (s_ucTimeOutFlag == 1)
{
break;
}
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_DelayUS
* 功能说明: us级延迟。 必须在systick定时器启动后才能调用此函数。
* 形 参: n : 延迟长度,单位1 us
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_DelayUS(uint32_t n)
{
uint32_t ticks;
uint32_t told;
uint32_t tnow;
uint32_t tcnt = 0;
uint32_t reload;
reload = SysTick->LOAD;
ticks = n * (SystemCoreClock / 1000000); /* 需要的节拍数 */
tcnt = 0;
told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
/* SYSTICK是一个递减的计数器 */
if (tnow < told)
{
tcnt += told - tnow;
}
/* 重新装载递减 */
else
{
tcnt += reload - tnow + told;
}
told = tnow;
/* 时间超过/等于要延迟的时间,则退出 */
if (tcnt >= ticks)
{
break;
}
}
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_StartTimer
* 功能说明: 启动一个定时器,并设置定时周期。
* 形 参: _id : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
* _period : 定时周期,单位1ms
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_StartTimer(uint8_t _id, uint32_t _period)
{
if (_id >= TMR_COUNT)
{
/* 打印出错的源代码文件名、函数名称 */
BSP_Printf("Error: file %s, function %s()\r\n", __FILE__, __FUNCTION__);
while(1); /* 参数异常,死机等待看门狗复位 */
}
DISABLE_INT(); /* 关中断 */
s_tTmr[_id].Count = _period; /* 实时计数器初值 */
s_tTmr[_id].PreLoad = _period; /* 计数器自动重装值,仅自动模式起作用 */
s_tTmr[_id].Flag = 0; /* 定时时间到标志 */
s_tTmr[_id].Mode = TMR_ONCE_MODE; /* 1次性工作模式 */
ENABLE_INT(); /* 开中断 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_StartAutoTimer
* 功能说明: 启动一个自动定时器,并设置定时周期。
* 形 参: _id : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
* _period : 定时周期,单位10ms
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_StartAutoTimer(uint8_t _id, uint32_t _period)
{
if (_id >= TMR_COUNT)
{
/* 打印出错的源代码文件名、函数名称 */
BSP_Printf("Error: file %s, function %s()\r\n", __FILE__, __FUNCTION__);
while(1); /* 参数异常,死机等待看门狗复位 */
}
DISABLE_INT(); /* 关中断 */
s_tTmr[_id].Count = _period; /* 实时计数器初值 */
s_tTmr[_id].PreLoad = _period; /* 计数器自动重装值,仅自动模式起作用 */
s_tTmr[_id].Flag = 0; /* 定时时间到标志 */
s_tTmr[_id].Mode = TMR_AUTO_MODE; /* 自动工作模式 */
ENABLE_INT(); /* 开中断 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_StopTimer
* 功能说明: 停止一个定时器
* 形 参: _id : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_StopTimer(uint8_t _id)
{
if (_id >= TMR_COUNT)
{
/* 打印出错的源代码文件名、函数名称 */
BSP_Printf("Error: file %s, function %s()\r\n", __FILE__, __FUNCTION__);
while(1); /* 参数异常,死机等待看门狗复位 */
}
DISABLE_INT(); /* 关中断 */
s_tTmr[_id].Count = 0; /* 实时计数器初值 */
s_tTmr[_id].Flag = 0; /* 定时时间到标志 */
s_tTmr[_id].Mode = TMR_ONCE_MODE; /* 自动工作模式 */
ENABLE_INT(); /* 开中断 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_CheckTimer
* 功能说明: 检测定时器是否超时
* 形 参: _id : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
* _period : 定时周期,单位1ms
* 返 回 值: 返回 0 表示定时未到, 1表示定时到
*********************************************************************************************************
*/
uint8_t bsp_CheckTimer(uint8_t _id)
{
if (_id >= TMR_COUNT)
{
return 0;
}
if (s_tTmr[_id].Flag == 1)
{
s_tTmr[_id].Flag = 0;
return 1;
}
else
{
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_GetRunTime
* 功能说明: 获取CPU运行时间,单位1ms。最长可以表示 24.85天,如果你的产品连续运行时间超过这个数,则必须考虑溢出问题
* 形 参: 无
* 返 回 值: CPU运行时间,单位1ms
*********************************************************************************************************
*/
int32_t bsp_GetRunTime(void)
{
int32_t runtime;
DISABLE_INT(); /* 关中断 */
runtime = g_iRunTime; /* 这个变量在Systick中断中被改写,因此需要关中断进行保护 */
ENABLE_INT(); /* 开中断 */
return runtime;
}
/*
*********************************************************************************************************
* 函 数 名: bsp_CheckRunTime
* 功能说明: 计算当前运行时间和给定时刻之间的差值。处理了计数器循环。
* 形 参: _LastTime 上个时刻
* 返 回 值: 当前时间和过去时间的差值,单位1ms
*********************************************************************************************************
*/
int32_t bsp_CheckRunTime(int32_t _LastTime)
{
int32_t now_time;
int32_t time_diff;
DISABLE_INT(); /* 关中断 */
now_time = g_iRunTime; /* 这个变量在Systick中断中被改写,因此需要关中断进行保护 */
ENABLE_INT(); /* 开中断 */
if (now_time >= _LastTime)
{
time_diff = now_time - _LastTime;
}
else
{
time_diff = 0x7FFFFFFF - _LastTime + now_time;
}
return time_diff;
}
/*
*********************************************************************************************************
* 函 数 名: SysTick_Handler
* 功能说明: 系统嘀嗒定时器中断服务程序。启动文件中引用了该函数。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void SysTick_Handler(void)
{
SysTick_ISR();
}
/*
*********************************************************************************************************
* 函 数 名: bsp_InitHardTimer
* 功能说明: 配置 TIMx,用于us级别硬件定时。TIMx将自由运行,永不停止.
* TIMx可以用TIM2 - TIM5 之间的TIM, 这些TIM有4个通道, 挂在 APB1 上,输入时钟=SystemCoreClock / 2
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
#if defined (USE_TIM2) || defined (USE_TIM3) || defined (USE_TIM4) || defined (USE_TIM5)
void bsp_InitHardTimer(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
uint32_t usPeriod;
uint16_t usPrescaler;
uint32_t uiTIMxCLK;
/* 使能TIM时钟 */
RCC_APB1PeriphClockCmd(TIM_HARD_RCC, ENABLE);
/*-----------------------------------------------------------------------
system_stm32f4xx.c 文件中 void SetSysClock(void) 函数对时钟的配置如下:
HCLK = SYSCLK / 1 (AHB1Periph)
PCLK2 = HCLK / 2 (APB2Periph)
PCLK1 = HCLK / 4 (APB1Periph)
因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = PCLK1 x 2 = SystemCoreClock / 2;
因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = PCLK2 x 2 = SystemCoreClock;
APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14
APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
----------------------------------------------------------------------- */
uiTIMxCLK = SystemCoreClock / 2;
usPrescaler = uiTIMxCLK / 1000000 ; /* 分频到周期 1us */
#if defined (USE_TIM2) || defined (USE_TIM5)
//usPeriod = 0xFFFFFFFF; /* 407支持32位定时器 */
usPeriod = 0xFFFF; /* 103支持16位 */
#else
usPeriod = 0xFFFF;
#endif
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = usPeriod;
TIM_TimeBaseStructure.TIM_Prescaler = usPrescaler;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM_HARD, &TIM_TimeBaseStructure);
//TIM_ARRPreloadConfig(TIMx, ENABLE);
/* TIMx enable counter */
TIM_Cmd(TIM_HARD, ENABLE);
/* 配置TIM定时中断 (Update) */
{
NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
NVIC_InitStructure.NVIC_IRQChannel = TIM_HARD_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4; /* 比串口优先级低 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_StartHardTimer
* 功能说明: 使用TIM2-5做单次定时器使用, 定时时间到后执行回调函数。可以同时启动4个定时器,互不干扰。
* 定时精度正负10us (主要耗费在调用本函数的执行时间,函数内部进行了补偿减小误差)
* TIM2和TIM5 是16位定时器。
* TIM3和TIM4 是16位定时器。
* 形 参: _CC : 捕获通道几,1,2,3, 4
* _uiTimeOut : 超时时间, 单位 1us. 对于16位定时器,最大 65.5ms; 对于32位定时器,最大 4294秒
* _pCallBack : 定时时间到后,被执行的函数
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_StartHardTimer(uint8_t _CC, uint32_t _uiTimeOut, void * _pCallBack)
{
uint32_t cnt_now;
uint32_t cnt_tar;
/*
执行下面这个语句,时长 = 18us (通过逻辑分析仪测量IO翻转)
bsp_StartTimer2(3, 500, (void *)test1);
*/
if (_uiTimeOut < 5)
{
;
}
else
{
_uiTimeOut -= 5;
}
cnt_now = TIM_GetCounter(TIM_HARD); /* 读取当前的计数器值 */
cnt_tar = cnt_now + _uiTimeOut; /* 计算捕获的计数器值 */
if (_CC == 1)
{
s_TIM_CallBack1 = (void (*)(void))_pCallBack;
TIM_SetCompare1(TIM_HARD, cnt_tar); /* 设置捕获比较计数器CC1 */
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC1);
TIM_ITConfig(TIM_HARD, TIM_IT_CC1, ENABLE); /* 使能CC1中断 */
}
else if (_CC == 2)
{
s_TIM_CallBack2 = (void (*)(void))_pCallBack;
TIM_SetCompare2(TIM_HARD, cnt_tar); /* 设置捕获比较计数器CC2 */
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC2);
TIM_ITConfig(TIM_HARD, TIM_IT_CC2, ENABLE); /* 使能CC2中断 */
}
else if (_CC == 3)
{
s_TIM_CallBack3 = (void (*)(void))_pCallBack;
TIM_SetCompare3(TIM_HARD, cnt_tar); /* 设置捕获比较计数器CC3 */
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC3);
TIM_ITConfig(TIM_HARD, TIM_IT_CC3, ENABLE); /* 使能CC3中断 */
}
else if (_CC == 4)
{
s_TIM_CallBack4 = (void (*)(void))_pCallBack;
TIM_SetCompare4(TIM_HARD, cnt_tar); /* 设置捕获比较计数器CC4 */
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC4);
TIM_ITConfig(TIM_HARD, TIM_IT_CC4, ENABLE); /* 使能CC4中断 */
}
else
{
return;
}
}
#endif
/*
*********************************************************************************************************
* 函 数 名: TIMx_IRQHandler
* 功能说明: TIM 中断服务程序
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
#ifdef USE_TIM2
void TIM2_IRQHandler(void)
#endif
#ifdef USE_TIM3
void TIM3_IRQHandler(void)
#endif
#ifdef USE_TIM4
void TIM4_IRQHandler(void)
#endif
#ifdef USE_TIM5
void TIM5_IRQHandler(void)
#endif
{
if (TIM_GetITStatus(TIM_HARD, TIM_IT_CC1))
{
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC1);
TIM_ITConfig(TIM_HARD, TIM_IT_CC1, DISABLE); /* 禁能CC1中断 */
/* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
s_TIM_CallBack1();
}
if (TIM_GetITStatus(TIM_HARD, TIM_IT_CC2))
{
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC2);
TIM_ITConfig(TIM_HARD, TIM_IT_CC2, DISABLE); /* 禁能CC2中断 */
/* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
s_TIM_CallBack2();
}
if (TIM_GetITStatus(TIM_HARD, TIM_IT_CC3))
{
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC3);
TIM_ITConfig(TIM_HARD, TIM_IT_CC3, DISABLE); /* 禁能CC3中断 */
/* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
s_TIM_CallBack3();
}
if (TIM_GetITStatus(TIM_HARD, TIM_IT_CC4))
{
TIM_ClearITPendingBit(TIM_HARD, TIM_IT_CC4);
TIM_ITConfig(TIM_HARD, TIM_IT_CC4, DISABLE); /* 禁能CC4中断 */
/* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
s_TIM_CallBack4();
}
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : 断言模块。
* 文件名称 : stm32f10x_assert.c
* 版 本 : V1.0
* 说 明 : 提供断言函数,主要用于程序调试。ST固件库中的函数均可以对输入参数进行检查,提高程序的健壮性。
* 这个文件是安富莱电子创建的文件,不属于标准库的文件,ST固件库的范例将这些函数放在main.c文件。
* 我们认为这个和用户的具体应用无关,因此将其独立出来,使main.c文件看起来更加简洁一些。
* 修改记录 :
* 版本号 日期 作者 说明
* v1.0 2011-08-27 armfly ST固件库V3.4.0版本。
* v2.0 2011-10-16 armfly ST固件库V3.5.0版本。
*
* Copyright (C), 2010-2011, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "stm32f10x.h" /* 这个文件包含了stm32f10x_conf.h, stm32f10x_conf.h文件定义了USE_FULL_ASSERT */
#include <stdio.h>
/*
ST库函数使用了C编译器的断言功能,如果定义了USE_FULL_ASSERT,那么所有的ST库函数将检查函数形参
是否正确。如果不正确将调用 assert_failed() 函数,这个函数是一个死循环,便于用户检查代码。
关键字 __LINE__ 表示源代码行号。
关键字__FILE__表示源代码文件名。
断言功能使能后将增大代码大小,推荐用户仅在调试时使能,在正式发布软件是禁止。
用户可以选择是否使能ST固件库的断言供能。使能断言的方法有两种:
(1) 在C编译器的预定义宏选项中定义USE_FULL_ASSERT。
(2) 在本文件取消"#define USE_FULL_ASSERT 1"行的注释。
*/
#ifdef USE_FULL_ASSERT
/*
*********************************************************************************************************
* 函 数 名: assert_failed
* 形 参:file : 源代码文件名称。关键字__FILE__表示源代码文件名。
* line :代码行号。关键字 __LINE__ 表示源代码行号
* 返 回 值: 无
*********************************************************************************************************
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/*
用户可以添加自己的代码报告源代码文件名和代码行号,比如将错误文件和行号打印到串口
printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
*/
/* 这是一个死循环,断言失败时程序会在此处死机,以便于用户查错 */
while (1)
{
}
}
#endif
/*
*********************************************************************************************************
*
* 模块名称 : 中断模块
* 文件名称 : stm32f10x_it.c
* 版 本 : V2.0
* 说 明 : 本文件存放所有的中断服务函数。为了便于他人了解程序用到的中断,我们不建议将中断函数移到其他
* 的文件。
*
* 我们只需要添加需要的中断函数即可。一般中断函数名是固定的,除非您修改了启动文件:
* Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm\startup_stm32f10x_hd.s
*
* 启动文件是汇编语言文件,定了每个中断的服务函数,这些函数使用了WEAK 关键字,表示弱定义,因此如
* 果我们在c文件中重定义了该服务函数(必须和它同名),那么启动文件的中断函数将自动无效。这也就
* 函数重定义的概念,这和C++中的函数重载的意义类似。
*
* 修改记录 :
* 版本号 日期 作者 说明
* v0.1 2009-12-27 armfly 创建该文件,ST固件库版本为V3.1.2
* v1.0 2011-01-11 armfly ST固件库升级到V3.4.0版本。
* v2.0 2011-10-16 armfly ST固件库升级到V3.5.0版本。
* V2.1 2015-08-07 armfly 在异常中断那服务器程序中驱动蜂鸣器发声,提示有问题了
*
* Copyright (C), 2010-2011, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "stm32f10x_it.h"
#include "bsp.h"
#define ERR_INFO "\r\nEnter HardFault_Handler, System Halt.\r\n"
/*
*********************************************************************************************************
* Cortex-M3 内核异常中断服务程序
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函 数 名: NMI_Handler
* 功能说明: 不可屏蔽中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void NMI_Handler(void)
{
}
/*
*********************************************************************************************************
* 函 数 名: HardFault_Handler
* 功能说明: 硬件失效中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void HardFault_Handler(void)
{
#if 0
const char *pError = ERR_INFO;
uint8_t i;
for (i = 0; i < sizeof(ERR_INFO); i++)
{
USART1->DR = pError[i];
/* 等待发送结束 */
while ((USART1->SR & USART_FLAG_TC) == (uint16_t)RESET);
}
#endif
#if 1 /* 出现异常时,驱动蜂鸣器发声 */
while(1)
{
uint16_t m;
GPIOA->ODR ^= GPIO_Pin_8;
for (m = 0; m < 10000; m++);
}
#else
/* 当硬件失效异常发生时进入死循环 */
while (1)
{
}
#endif
}
/*
*********************************************************************************************************
* 函 数 名: MemManage_Handler
* 功能说明: 内存管理异常中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void MemManage_Handler(void)
{
/* 当内存管理异常发生时进入死循环 */
while (1)
{
}
}
/*
*********************************************************************************************************
* 函 数 名: BusFault_Handler
* 功能说明: 总线访问异常中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void BusFault_Handler(void)
{
/* 当总线异常时进入死循环 */
while (1)
{
}
}
/*
*********************************************************************************************************
* 函 数 名: UsageFault_Handler
* 功能说明: 未定义的指令或非法状态中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void UsageFault_Handler(void)
{
/* 当用法异常时进入死循环 */
while (1)
{
}
}
/*
*********************************************************************************************************
* 函 数 名: SVC_Handler
* 功能说明: 通过SWI指令的系统服务调用中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void SVC_Handler(void)
{
}
/*
*********************************************************************************************************
* 函 数 名: DebugMon_Handler
* 功能说明: 调试监视器中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void DebugMon_Handler(void)
{
}
/*
*********************************************************************************************************
* 函 数 名: PendSV_Handler
* 功能说明: 可挂起的系统服务调用中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void PendSV_Handler(void)
{
}
/*
*********************************************************************************************************
* STM32F10x内部外设中断服务程序
* 用户在此添加用到外设中断服务函数。有效的中断服务函数名请参考启动文件(startup_stm32f10x_xx.s)
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函 数 名: PPP_IRQHandler
* 功能说明: 外设中断服务程序。
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
/*
因为中断服务程序往往和具体的应用有关,会用到用户功能模块的变量、函数。如果在本文件展开,会增加大量的
外部变量声明或者include语句。
因此,我们推荐这个地方只写一个调用语句,中断服务函数的本体放到对应的用户功能模块中。
增加一层调用会降低代码的执行效率,不过我们宁愿损失这个效率,从而增强程序的模块化特性。
增加extern关键字,直接引用用到的外部函数,避免在文件头include其他模块的头文件
extern void ppp_ISR(void);
void PPP_IRQHandler(void)
{
ppp_ISR();
}
*/
#if 0
extern void can_ISR(void);
extern void USB_Istr(void);
#endif
void USB_LP_CAN1_RX0_IRQHandler(void)
{
#if 0
/* 判断CAN1的时钟是否打开 */
if (RCC->APB1ENR & RCC_APB1Periph_CAN1)
{
can_ISR();
}
else
{
USB_Istr();
}
#endif
}
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_ReadClock(); //读一次时钟
refreshledf=1; //1秒更新一次LED
RTC_WaitForLastTask();
}
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : 字符串操作\数值转换
* 文件名称 : bsp_user_lib.c
* 版 本 : V1.3a
* 说 明 : 提供一些常用的sting、mem操作函数以及Modbus CRC16函数
* 修改记录 :
* V1.0 2013-12-05 首版
* V1.1 2014-06-20 增加大小端整数转换函数
* V1.2 2015-04-06 增加 BEBufToUint32()和 LEBufToUint32()
* V1.3 2015-10-09 增加 BcdToChar(), HexToAscll()和 AsciiToUint32()
* V1.3a 2015-10-09 解决 HexToAscll() 函数末尾补0的BUG
*
*********************************************************************************************************
*/
#include "bsp.h"
// CRC 高位字节值表
static const uint8_t s_CRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
} ;
// CRC 低位字节值表
const uint8_t s_CRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
/*
*********************************************************************************************************
* 函 数 名: str_len
* 功能说明: 计算字符串长度.0是结束符
* 形 参: _str : 缓冲区
* 返 回 值: 无
*********************************************************************************************************
*/
int str_len(char *_str)
{
int len = 0;
while (*_str++) len++;
return len;
}
/*
*********************************************************************************************************
* 函 数 名: str_cpy
* 功能说明: 复制字符串
* 形 参: tar : 目标缓冲区
* src : 源缓冲区
* 返 回 值: 无
*********************************************************************************************************
*/
void str_cpy(char *_tar, char *_src)
{
do
{
*_tar++ = *_src;
}
while (*_src++);
}
/*
*********************************************************************************************************
* 函 数 名: str_cmp
* 功能说明: 字符串比较
* 形 参: s1 : 字符串1
* s2 : 字符串2
* 返 回 值: 0 表示相等 非0表示不等
*********************************************************************************************************
*/
int str_cmp(char * s1, char * s2)
{
while ((*s1!=0) && (*s2!=0) && (*s1==*s2))
{
s1++;
s2++;
}
return *s1 - *s2;
}
/*
*********************************************************************************************************
* 函 数 名: str_copy
* 功能说明: 复制字符串
* 形 参: tar : 目标缓冲区
* src : 源缓冲区
* 返 回 值: 无
*********************************************************************************************************
*/
void mem_set(char *_tar, char _data, int _len)
{
while (_len--)
{
*_tar++ = _data;
}
}
/*
*********************************************************************************************************
* 函 数 名: int_to_ascii
* 功能说明: 将整数转换为ASCII数组。支持负数。
* 形 参: _Number : 整数
* _pBuf : 目标缓冲区, 存放转换后的结果。以0结束的字符串。
* _len : ASCII字符个数, 字符串长度
* 返 回 值: 无
*********************************************************************************************************
*/
void int_to_str(int _iNumber, char *_pBuf, unsigned char _len)
{
unsigned char i;
int iTemp;
if (_iNumber < 0) /* 负数 */
{
iTemp = -_iNumber; /* 转为正数 */
}
else
{
iTemp = _iNumber;
}
mem_set(_pBuf, ' ',_len);
/* 将整数转换为ASCII字符串 */
for (i = 0; i < _len; i++)
{
_pBuf[_len - 1 - i] = (iTemp % 10) + '0';
iTemp = iTemp / 10;
if (iTemp == 0)
{
break;
}
}
_pBuf[_len] = 0;
if (_iNumber < 0) /* 负数 */
{
for (i = 0; i < _len; i++)
{
if ((_pBuf[i] == ' ') && (_pBuf[i + 1] != ' '))
{
_pBuf[i] = '-';
break;
}
}
}
}
/*
*********************************************************************************************************
* 函 数 名: str_to_int
* 功能说明: 将ASCII码字符串转换成整数。 遇到小数点自动越过。
* 形 参: _pStr :待转换的ASCII码串. 可以以逗号,#或0结束。 2014-06-20 修改为非0-9的字符。
* 返 回 值: 二进制整数值
*********************************************************************************************************
*/
int str_to_int(char *_pStr)
{
unsigned char flag;
char *p;
int ulInt;
unsigned char i;
unsigned char ucTemp;
p = _pStr;
if (*p == '-')
{
flag = 1; /* 负数 */
p++;
}
else
{
flag = 0;
}
ulInt = 0;
for (i = 0; i < 15; i++)
{
ucTemp = *p;
if (ucTemp == '.') /* 遇到小数点,自动跳过1个字节 */
{
p++;
ucTemp = *p;
}
if ((ucTemp >= '0') && (ucTemp <= '9'))
{
ulInt = ulInt * 10 + (ucTemp - '0');
p++;
}
else
{
break;
}
}
if (flag == 1)
{
return -ulInt;
}
return ulInt;
}
/*
*********************************************************************************************************
* 函 数 名: BEBufToUint16
* 功能说明: 将2字节数组(大端Big Endian次序,高字节在前)转换为16位整数
* 形 参: _pBuf : 数组
* 返 回 值: 16位整数值
*
* 大端(Big Endian)与小端(Little Endian)
*********************************************************************************************************
*/
uint16_t BEBufToUint16(uint8_t *_pBuf)
{
return (((uint16_t)_pBuf[0] << 8) | _pBuf[1]);
}
/*
*********************************************************************************************************
* 函 数 名: LEBufToUint16
* 功能说明: 将2字节数组(小端Little Endian,低字节在前)转换为16位整数
* 形 参: _pBuf : 数组
* 返 回 值: 16位整数值
*********************************************************************************************************
*/
uint16_t LEBufToUint16(uint8_t *_pBuf)
{
return (((uint16_t)_pBuf[1] << 8) | _pBuf[0]);
}
/*
*********************************************************************************************************
* 函 数 名: BEBufToUint32
* 功能说明: 将4字节数组(大端Big Endian次序,高字节在前)转换为16位整数
* 形 参: _pBuf : 数组
* 返 回 值: 16位整数值
*
* 大端(Big Endian)与小端(Little Endian)
*********************************************************************************************************
*/
uint32_t BEBufToUint32(uint8_t *_pBuf)
{
return (((uint32_t)_pBuf[0] << 24) | ((uint32_t)_pBuf[1] << 16) | ((uint32_t)_pBuf[2] << 8) | _pBuf[3]);
}
/*
*********************************************************************************************************
* 函 数 名: LEBufToUint32
* 功能说明: 将4字节数组(小端Little Endian,低字节在前)转换为16位整数
* 形 参: _pBuf : 数组
* 返 回 值: 16位整数值
*********************************************************************************************************
*/
uint32_t LEBufToUint32(uint8_t *_pBuf)
{
return (((uint32_t)_pBuf[3] << 24) | ((uint32_t)_pBuf[2] << 16) | ((uint32_t)_pBuf[1] << 8) | _pBuf[0]);
}
/*
*********************************************************************************************************
* 函 数 名: CRC16_Modbus
* 功能说明: 计算CRC。 用于Modbus协议。
* 形 参: _pBuf : 参与校验的数据
* _usLen : 数据长度
* 返 回 值: 16位整数值。 对于Modbus ,此结果高字节先传送,低字节后传送。
*
* 所有可能的CRC值都被预装在两个数组当中,当计算报文内容时可以简单的索引即可;
* 一个数组包含有16位CRC域的所有256个可能的高位字节,另一个数组含有低位字节的值;
* 这种索引访问CRC的方式提供了比对报文缓冲区的每一个新字符都计算新的CRC更快的方法;
*
* 注意:此程序内部执行高/低CRC字节的交换。此函数返回的是已经经过交换的CRC值;也就是说,该函数的返回值可以直接放置
* 于报文用于发送;
*********************************************************************************************************
*/
uint16_t CRC16_Modbus(uint8_t *_pBuf, uint16_t _usLen)
{
uint8_t ucCRCHi = 0xFF; /* 高CRC字节初始化 */
uint8_t ucCRCLo = 0xFF; /* 低CRC 字节初始化 */
uint16_t usIndex; /* CRC循环中的索引 */
while (_usLen--)
{
usIndex = ucCRCHi ^ *_pBuf++; /* 计算CRC */
ucCRCHi = ucCRCLo ^ s_CRCHi[usIndex];
ucCRCLo = s_CRCLo[usIndex];
}
return ((uint16_t)ucCRCHi << 8 | ucCRCLo);
}
/*
*********************************************************************************************************
* 函 数 名: CaculTwoPoint
* 功能说明: 根据2点直线方程,计算Y值
* 形 参: 2个点的坐标和x输入量
* 返 回 值: x对应的y值
*********************************************************************************************************
*/
int32_t CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x)
{
return y1 + ((int64_t)(y2 - y1) * (x - x1)) / (x2 - x1);
}
/*
*********************************************************************************************************
* 函 数 名: BcdToChar
* 功能说明: 将BCD码转为ASCII字符。 比如 0x0A ==> 'A'
* 形 参: _bcd :输入的二进制数。必须小于16
* 返 回 值: 转换结果
*********************************************************************************************************
*/
char BcdToChar(uint8_t _bcd)
{
if (_bcd < 10)
{
return _bcd + '0';
}
else if (_bcd < 16)
{
return _bcd + 'A';
}
else
{
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: HexToAscll
* 功能说明: 将二进制数组转换为16进制格式的ASCII字符串。每个2个ASCII字符后保留1个空格。
* 0x12 0x34 转化为 0x31 0x32 0x20 0x33 0x34 0x00 即 "1234"
* 形 参: _pHex :输入的数据,二进制数组
* _pAscii :存放转换结果, ASCII字符串,0结束。1个二进制对应2个ASCII字符.
* 返 回 值: 转换得到的整数
*********************************************************************************************************
*/
void HexToAscll(uint8_t * _pHex, char *_pAscii, uint16_t _BinBytes)
{
uint16_t i;
if (_BinBytes == 0)
{
_pAscii[0] = 0;
}
else
{
for (i = 0; i < _BinBytes; i++)
{
_pAscii[3 * i] = BcdToChar(_pHex[i] >> 4);
_pAscii[3 * i + 1] = BcdToChar(_pHex[i] & 0x0F);
_pAscii[3 * i + 2] = ' ';
}
_pAscii[3 * (i - 1) + 2] = 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: AsciiToUint32
* 功能说明: 变长的 ASCII 字符转换为32位整数 ASCII 字符以空格或者0结束 。 支持16进制和10进制输入
* 形 参: *pAscii :要转换的ASCII码
* 返 回 值: 转换得到的整数
*********************************************************************************************************
*/
uint32_t AsciiToUint32(char *pAscii)
{
char i;
char bTemp;
char bIsHex;
char bLen;
char bZeroLen;
uint32_t lResult;
uint32_t lBitValue;
/* 判断是否是16进制数 */
bIsHex = 0;
if ((pAscii[0] == '0') && ((pAscii[1] == 'x') || (pAscii[1] == 'X')))
{
bIsHex=1;
}
lResult=0;
// 最大数值为 4294967295, 10位+2字符"0x" //
if (bIsHex == 0)
{ // 十进制 //
// 求长度 //
lBitValue=1;
/* 前导去0 */
for (i = 0; i < 8; i++)
{
bTemp = pAscii[i];
if (bTemp != '0')
break;
}
bZeroLen = i;
for (i = 0; i < 10; i++)
{
if ((pAscii[i] < '0') || (pAscii[i] > '9'))
break;
lBitValue = lBitValue * 10;
}
bLen = i;
lBitValue = lBitValue / 10;
if (lBitValue == 0)
lBitValue=1;
for (i = bZeroLen; i < bLen; i++)
{
lResult += (pAscii[i] - '0') * lBitValue;
lBitValue /= 10;
}
}
else
{ /* 16进制 */
/* 求长度 */
lBitValue=1;
/* 前导去0 */
for (i = 0; i < 8; i++)
{
bTemp = pAscii[i + 2];
if(bTemp!='0')
break;
}
bZeroLen = i;
for (; i < 8; i++)
{
bTemp=pAscii[i+2];
if (((bTemp >= 'A') && (bTemp <= 'F')) ||
((bTemp>='a')&&(bTemp<='f')) ||
((bTemp>='0')&&(bTemp<='9')) )
{
lBitValue=lBitValue * 16;
}
else
{
break;
}
}
lBitValue = lBitValue / 16;
if (lBitValue == 0)
lBitValue = 1;
bLen = i;
for (i = bZeroLen; i < bLen; i++)
{
bTemp = pAscii[i + 2];
if ((bTemp >= 'A') && (bTemp <= 'F'))
{
bTemp -= 0x37;
}
else if ((bTemp >= 'a') && (bTemp <= 'f'))
{
bTemp -= 0x57;
}
else if ((bTemp >= '0') && (bTemp <= '9'))
{
bTemp -= '0';
}
lResult += bTemp*lBitValue;
lBitValue /= 16;
}
}
return lResult;
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : 串行EEPROM 24xx驱动模块
* 文件名称 : bsp_eeprom_24xx.c
* 版 本 : V1.0
* 说 明 : 实现24xx系列EEPROM的读写操作。写操作采用页写模式提高写入效率。
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2013-02-01 armfly 正式发布
*
* Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
/*
应用说明:访问串行EEPROM前,请先调用一次 bsp_InitI2C()函数配置好I2C相关的GPIO.
*/
#include "bsp.h"
/*
*********************************************************************************************************
* 函 数 名: ee_CheckOk
* 功能说明: 判断串行EERPOM是否正常
* 形 参: 无
* 返 回 值: 1 表示正常, 0 表示不正常
*********************************************************************************************************
*/
uint8_t ee_CheckOk(void)
{
if (i2c_CheckDevice(EE_DEV_ADDR) == 0)
{
return 1;
}
else
{
/* 失败后,切记发送I2C总线停止信号 */
i2c_Stop();
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: ee_ReadBytes
* 功能说明: 从串行EEPROM指定地址处开始读取若干数据
* 形 参: _usAddress : 起始地址
* _usSize : 数据长度,单位为字节
* _pReadBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
uint16_t i;
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
i2c_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(EE_DEV_ADDR | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
if (EE_ADDR_BYTES == 1)
{
i2c_SendByte((uint8_t)_usAddress);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
}
else
{
i2c_SendByte(_usAddress >> 8);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
i2c_SendByte(_usAddress);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
}
/* 第6步:重新启动I2C总线。下面开始读取数据 */
i2c_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(EE_DEV_ADDR | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第9步:循环读取数据 */
for (i = 0; i < _usSize; i++)
{
_pReadBuf[i] = i2c_ReadByte(); /* 读1个字节 */
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
if (i != _usSize - 1)
{
i2c_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
}
else
{
i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
}
}
/* 发送I2C总线停止信号 */
i2c_Stop();
return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: ee_WriteBytes
* 功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
* 形 参: _usAddress : 起始地址
* _usSize : 数据长度,单位为字节
* _pWriteBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{
uint16_t i,m;
uint16_t usAddr;
/*
写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
对于24xx02,page size = 8
简单的处理方法为:按字节写操作模式,每写1个字节,都发送地址
为了提高连续写的效率: 本函数采用page wirte操作。
*/
usAddr = _usAddress;
for (i = 0; i < _usSize; i++)
{
/* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
if ((i == 0) || (usAddr & (EE_PAGE_SIZE - 1)) == 0)
{
/* 第0步:发停止信号,启动内部写操作 */
i2c_Stop();
/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms
CLK频率为200KHz时,查询次数为30次左右
*/
for (m = 0; m < 1000; m++)
{
/* 第1步:发起I2C总线启动信号 */
i2c_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(EE_DEV_ADDR | I2C_WR); /* 此处是写指令 */
/* 第3步:发送一个时钟,判断器件是否正确应答 */
if (i2c_WaitAck() == 0)
{
break;
}
}
if (m == 1000)
{
goto cmd_fail; /* EEPROM器件写超时 */
}
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
if (EE_ADDR_BYTES == 1)
{
i2c_SendByte((uint8_t)usAddr);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
}
else
{
i2c_SendByte(usAddr >> 8);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
i2c_SendByte(usAddr);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
}
}
/* 第6步:开始写入数据 */
i2c_SendByte(_pWriteBuf[i]);
/* 第7步:发送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
usAddr++; /* 地址增1 */
}
/* 命令执行成功,发送I2C总线停止信号 */
i2c_Stop();
return 1;
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();
return 0;
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : I2C总线驱动模块
* 文件名称 : bsp_i2c_gpio.c
* 版 本 : V1.0
* 说 明 : 用gpio模拟i2c总线, 适用于STM32F4系列CPU。该模块不包括应用层命令帧,仅包括I2C总线基本操作函数。
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2015-05-21 armfly 正式发布
*
* Copyright (C), 2015-2016, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
/*
应用说明:
在访问I2C设备前,请先调用 i2c_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO
*/
#include "bsp.h"
/*
安富莱STM32-V4开发板 i2c总线GPIO:
PB6/I2C1_SCL
PB7/I2C1_SDA
*/
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define RCC_I2C_PORT RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define PORT_I2C_SCL GPIOB /* GPIO端口 */
#define PIN_I2C_SCL GPIO_Pin_6 /* GPIO引脚 */
#define PORT_I2C_SDA GPIOB /* GPIO端口 */
#define PIN_I2C_SDA GPIO_Pin_7 /* GPIO引脚 */
#define I2C_SCL_PIN GPIO_Pin_6 /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN GPIO_Pin_7 /* 连接到SDA数据线的GPIO */
/* 定义读写SCL和SDA的宏 */
#define I2C_SCL_1() PORT_I2C_SCL->BSRR = I2C_SCL_PIN /* SCL = 1 */
#define I2C_SCL_0() PORT_I2C_SCL->BRR = I2C_SCL_PIN /* SCL = 0 */
#define I2C_SDA_1() PORT_I2C_SDA->BSRR = I2C_SDA_PIN /* SDA = 1 */
#define I2C_SDA_0() PORT_I2C_SDA->BRR = I2C_SDA_PIN /* SDA = 0 */
#define I2C_SDA_READ() ((PORT_I2C_SDA->IDR & I2C_SDA_PIN) != 0) /* 读SDA口线状态 */
#define I2C_SCL_READ() ((PORT_I2C_SCL->IDR & I2C_SCL_PIN) != 0) /* 读SCL口线状态 */
/*
*********************************************************************************************************
* 函 数 名: bsp_InitI2C
* 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitI2C(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出模式 */
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SCL;
GPIO_Init(PORT_I2C_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);
/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Delay
* 功能说明: I2C总线位延迟,最快400KHz
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
uint8_t i;
/*
CPU主频168MHz时,在内部Flash运行, MDK工程不优化。用台式示波器观测波形。
循环次数为5时,SCL频率 = 1.78MHz (读耗时: 92ms, 读写正常,但是用示波器探头碰上就读写失败。时序接近临界)
循环次数为10时,SCL频率 = 1.1MHz (读耗时: 138ms, 读速度: 118724B/s)
循环次数为30时,SCL频率 = 440KHz, SCL高电平时间1.0us,SCL低电平时间1.2us
上拉电阻选择2.2K欧时,SCL上升沿时间约0.5us,如果选4.7K欧,则上升沿约1us
实际应用选择400KHz左右的速率即可
*/
for (i = 0; i < 30; i++);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线启动信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线停止信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
I2C_SDA_0();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参: _ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
if (i == 7)
{
I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
i2c_Delay();
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参: 无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
i2c_Delay();
}
return value;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参: 无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
uint8_t re;
I2C_SDA_1(); /* CPU释放SDA总线 */
i2c_Delay();
I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay();
if (I2C_SDA_READ()) /* CPU读取SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
I2C_SCL_0();
i2c_Delay();
return re;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CheckDevice
* 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
* 形 参: _Address:设备的I2C总线地址
* 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
if (I2C_SDA_READ() && I2C_SCL_READ())
{
i2c_Start(); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendByte(_Address | I2C_WR);
ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */
i2c_Stop(); /* 发送停止信号 */
return ucAck;
}
return 1; /* I2C总线异常 */
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
/*
*********************************************************************************************************
*
* 模块名称 : STM32内部RTC模块
* 文件名称 : bsp_cpu_rtc.h
* 版 本 : V1.0
* 说 明 : 头文件
*
* 修改记录 :
* 版本号 日期 作者 说明
* v1.0 2015-08-08 armfly 首版.安富莱电子原创
*
* Copyright (C), 2015-2016, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h"
/* 选择RTC的时钟源 */
#define RTC_CLOCK_SOURCE_LSE /* LSE */
//#define RTC_CLOCK_SOURCE_LSI /* LSI */
RTC_T g_tRTC;
/* 平年的每月天数表 */
const uint8_t mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/*
*********************************************************************************************************
* 函 数 名: bsp_InitRTC
* 功能说明: 初始化CPU内部RTC
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitRTC(void)
{
uint16_t u16_WaitForOscSource;
/*
我们在BKP的后备寄存器1中,存了一个特殊字符0xA5A5, 第一次上电或后备电源掉电后,该寄存器数据丢失,
表明RTC数据丢失,需要重新配置
*/
if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
{
//重新配置RTC
/* Enable PWR and BKP clocks */ /* PWR时钟(电源控制)与BKP时钟(RTC后备寄存器)使能 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* Allow access to BKP Domain */ /*使能RTC和后备寄存器访问 */
PWR_BackupAccessCmd(ENABLE);
/* Reset Backup Domain */ /* 将外设BKP的全部寄存器重设为缺省值 */
BKP_DeInit();
/* Enable LSE */
RCC_LSEConfig(RCC_LSE_ON);
#if 0
for(u16_WaitForOscSource=0; u16_WaitForOscSource < 5000; u16_WaitForOscSource++)
{
;
}
#endif
/* Wait till LSE is ready */ /* 等待外部晶振震荡稳定输出 */
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
/* Select LSE as RTC Clock Source */ /*使用外部32.768KHz晶振作为RTC时钟 */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* Enable RTC Clock */
RCC_RTCCLKCmd(ENABLE);
/* Wait for RTC registers synchronization */
RTC_WaitForSynchro();
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* Enable the RTC Second */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
// RTC_EnterConfigMode();
/* Set RTC prescaler: set RTC period to 1sec */
/* 32.768KHz晶振预分频值是32767,如果对精度要求很高可以修改此分频值来校准晶振 */
RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
RTC_WriteClock(2015, 8, 8, 0, 0, 0);//默认时间
/* 配置完成后,向后备寄存器中写特殊字符0xA5A5 */
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
}
else
{
/* 若后备寄存器没有掉电,则无需重新配置RTC */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
for (u16_WaitForOscSource = 0; u16_WaitForOscSource < 5000; u16_WaitForOscSource++)
{
;
}
if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
{
/* 上电复位 */
}
else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
{
/* 外部RST管脚复位 */
}
/* 清除RCC中复位标志 */
RCC_ClearFlag();
//虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行
//但是每次上电后,还是要使能RTCCLK???????
//RCC_RTCCLKCmd(ENABLE);
//等待RTC时钟与APB1时钟同步
RTC_WaitForSynchro();
//使能秒中断
RTC_ITConfig(RTC_IT_SEC, ENABLE);
//等待操作完成
RTC_WaitForLastTask();
}
if(1) //增加秒定时器中断
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Enable the RTC Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
return;
}
/*
*********************************************************************************************************
* 函 数 名: Is_Leap_Year
* 功能说明: 判断是否为闰年
* 形 参:无
* 返 回 值: 1,是.0,不是
*********************************************************************************************************
*/
static uint8_t Is_Leap_Year(uint16_t _year)
{
if (_year % 4 == 0) /* 必须能被4整除 */
{
if (_year % 100 == 0)
{
if (_year % 400 == 0)
{
return 1; /* 如果以00结尾,还要能被400整除 */
}
else
{
return 0;
}
}
else
{
return 1;
}
}
else
{
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: RTC_WriteClock
* 功能说明: 设置RTC时钟
* 形 参:无
* 返 回 值: 1表示成功 0表示错误
*********************************************************************************************************
*/
uint8_t RTC_WriteClock(uint16_t _year, uint8_t _mon, uint8_t _day, uint8_t _hour, uint8_t _min, uint8_t _sec)
{
uint16_t t;
uint32_t seccount=0;
if (_year < 2000 || _year > 2099)
{
return 0; /* _year范围1970-2099,此处设置范围为2000-2099 */
}
for (t = 1970; t < _year; t++) /* 把所有年份的秒钟相加 */
{
if (Is_Leap_Year(t)) /* 判断是否为闰年 */
{
seccount += 31622400; /* 闰年的秒钟数 */
}
else
{
seccount += 31536000; /* 平年的秒钟数 */
}
}
_mon -= 1;
for (t = 0; t < _mon; t++) /* 把前面月份的秒钟数相加 */
{
seccount += (uint32_t)mon_table[t] * 86400; /* 月份秒钟数相加 */
if (Is_Leap_Year(_year) && t == 1)
{
seccount += 86400; /* 闰年2月份增加一天的秒钟数 */
}
}
seccount += (uint32_t)(_day - 1) * 86400; /* 把前面日期的秒钟数相加 */
seccount += (uint32_t)_hour * 3600; /* 小时秒钟数 */
seccount += (uint32_t)_min * 60; /* 分钟秒钟数 */
seccount += _sec; /* 最后的秒钟加上去 */
PWR_BackupAccessCmd(ENABLE); /* 必须要 */
RTC_WaitForLastTask();
RTC_SetCounter(seccount);
RTC_WaitForLastTask(); /* 必须加 PWR_BackupAccessCmd(ENABLE); 不然进入死循环 */
return 1;
}
/*
*********************************************************************************************************
* 函 数 名: RTC_ReadClock
* 功能说明: 得到当前时钟。结果存放在 g_tRTC。
* 形 参:无
* 返 回 值: 1表示成功 0表示错误
*********************************************************************************************************
*/
void RTC_ReadClock(void)
{
static uint16_t daycnt = 0;
uint32_t timecount = 0;
uint32_t temp = 0;
uint16_t temp1 = 0;
timecount = RTC_GetCounter();
temp = timecount / 86400; /* 得到天数 */
if (daycnt != temp) /* 超过一天了 */
{
daycnt = temp;
temp1 = 1970; /* 从1970年开始 */
while (temp >= 365)
{
if (Is_Leap_Year(temp1)) /* 是闰年 */
{
if (temp >= 366)
{
temp -= 366; /* 闰年的秒钟数 */
}
else
{
//temp1++; /* armfly: 这里闰年处理错误,不能加1 */
break;
}
}
else
{
temp -= 365; /* 平年 */
}
temp1++;
}
g_tRTC.Year = temp1; /* 得到年份 */
temp1 = 0;
while (temp >= 28) /* 超过了一个月 */
{
if(Is_Leap_Year(g_tRTC.Year) && temp1 == 1) /* 当年是不是闰年/2月份 */
{
if (temp >= 29)
{
temp -= 29; /* 闰年的秒钟数 */
}
else
{
break;
}
}
else
{
if (temp >= mon_table[temp1])
{
temp -= mon_table[temp1]; /* 平年 */
}
else
{
break;
}
}
temp1++;
}
g_tRTC.Mon = temp1 + 1; /* 得到月份 */
g_tRTC.Day = temp + 1; /* 得到日期 */
}
temp = timecount % 86400; /* 得到秒钟数 */
g_tRTC.Hour = temp / 3600; /* 小时 */
g_tRTC.Min = (temp % 3600) / 60; /* 分钟 */
g_tRTC.Sec = (temp % 3600) % 60; /* 秒钟 */
g_tRTC.Week = RTC_CalcWeek(g_tRTC.Year, g_tRTC.Mon, g_tRTC.Day); /* 计算星期 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_CalcWeek
* 功能说明: 根据日期计算星期几
* 形 参: _year _mon _day 年月日 (年是2字节整数,月和日是字节整数)
* 返 回 值: 周几 (1-7) 7表示周日
*********************************************************************************************************
*/
uint8_t RTC_CalcWeek(uint16_t _year, uint8_t _mon, uint8_t _day)
{
/*
蔡勒(Zeller)公式
历史上的某一天是星期几?未来的某一天是星期几?关于这个问题,有很多计算公式(两个通用计算公式和
一些分段计算公式),其中最著名的是蔡勒(Zeller)公式。
即w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
公式中的符号含义如下,
w:星期;
c:年的高2位,即世纪-1
y:年(两位数);
m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,
比如2003年1月1日要看作2002年的13月1日来计算);
d:日;
[ ]代表取整,即只要整数部分。
算出来的W除以7,余数是几就是星期几。如果余数是0,则为星期日。
如果结果是负数,负数求余数则需要特殊处理:
负数不能按习惯的余数的概念求余数,只能按数论中的余数的定义求余。为了方便
计算,我们可以给它加上一个7的整数倍,使它变为一个正数,然后再求余数
以2049年10月1日(100周年国庆)为例,用蔡勒(Zeller)公式进行计算,过程如下:
蔡勒(Zeller)公式:w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
=49+[49/4]+[20/4]-2×20+[26× (10+1)/10]+1-1
=49+[12.25]+5-40+[28.6]
=49+12+5-40+28
=54 (除以7余5)
即2049年10月1日(100周年国庆)是星期5。
*/
uint8_t y, c, m, d;
int16_t w;
if (_mon >= 3)
{
m = _mon;
y = _year % 100;
c = _year / 100;
d = _day;
}
else /* 某年的1、2月要看作上一年的13、14月来计算 */
{
m = _mon + 12;
y = (_year - 1) % 100;
c = (_year - 1) / 100;
d = _day;
}
w = y + y / 4 + c / 4 - 2 * c + ((uint16_t)26*(m+1))/10 + d - 1;
if (w == 0)
{
w = 7; /* 表示周日 */
}
else if (w < 0) /* 如果w是负数,则计算余数方式不同 */
{
w = 7 - (-w) % 7;
}
else
{
w = w % 7;
}
return w;
}
/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/