GPIO八种模式:
1、输入浮空:信号输入后没有上下拉电阻,经过施密特触发器转换为0,1信号->进入输入寄存器
2、输入上拉:信号输入后有上拉电阻 经过施密特触发器转换为0,1信号->进入输入寄存器
3、输入下拉:信号输入后有下拉电阻 经过施密特触发器转换为0,1信号->进入输入寄存器
4、模拟输入:信号输入 不经过施密特
5、开漏输出:CPU写入位设置寄存器->输出寄存器(ODR)->输出控制电路->如果是1,低电平Nmos管截止,外部上拉下拉决定。如果是0,低电平Nmos管开启,电平被拉低到0
6、推挽输出:CPU写入位设置寄存器->输出寄存器(ODR)->输出控制电路->如果是1,低电平Nmos管截止,高电平P-Mos导通;如果是0,低电平Nmos管导通,高电平P-Mos截止
7、推挽式复用功能:由外设代替CPU执行的推外输出
8、开漏复用功能:由外设功能块替代CPU决定控制电路,执行的开漏输出
在这里插入图片描述
SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可
编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
SPI 总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串
行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果
CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电
平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果
CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果 CPHA=1,在串
行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI 主模块和与之通信的外设备时钟
相位和极性应该一致。
点灯
#include "Led.h"
#include "stm32f10x.h"
void LedInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //GPIOB 使能IO口时钟
//GPIOE 查看入口参数 assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph)); 之IS_RCC_APB2_PERIPH
//GPIOE初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化GPIO_Init()
GPIO_SetBits(GPIOE, GPIO_Pin_5); //输出端口GPIOE, 高电平GPIO_Pin_5
//GPIOB初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIO_Init()
GPIO_SetBits(GPIOB, GPIO_Pin_5); //输出端口GPIOB, 高电平GPIO_Pin_5
}
#include "stm32f10x.h"
#include "Led.h"
#include "delay.h"
int main(void)
{
delay_init();
LedInit();
while(1)
{
GPIO_SetBits(GPIOE, GPIO_Pin_5);
GPIO_SetBits(GPIOB, GPIO_Pin_5);
delay_ms(500);
GPIO_ResetBits(GPIOE, GPIO_Pin_5);
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
delay_ms(500);
}
}
指针结构体复习
// CPlusDemon.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
void f1(int i)
{
i = i + 1;
}
void f2(int* i)
{
*i = *i + 1;
}
struct Employee
{
int age;
int height;
int id;
int salary;
char name[20];
};
//typedef struct
//{
// int age = 11;
// int height = 10;
// int id = 1;
// int salary = 2;
// char name[20] = "zg";
//} Employee;
int main()
{
int num = 999;
int i = 888;
int* p1, * p2;
p1 = #
p2 = p1; //没有经过p1,直接把num的地址给了p2
printf("%p,%p,%p\n", &num, p1, p2);
p1 = &i;
printf("%p,%p,%p,%p\n", &num,&i, p1, p2); //&i, p1指向同一个, p2依然指向num
//std::cout << "%p,%p,%p,%p\n";
int* p = &i; //*出现在定义变量前面,表示变量是指针类型。 当*单独和变量放一起,表示读/写指向内存中的数据
printf("%d\n" ,*p); //888
f1(i);
printf("%d\n", i);
f2(&i); //指针当参数,会改变外部变量的值,ref out
printf("%d\n", i);
//struct Employee e1;
//e1.age = 15;
//strcpy_s(e1.name, "zg");
//e1.height = 180;
//e1.id = 2;
//e1.salary = 99999;
struct Employee e1 = { 18,180,1,99999,"zg" };
struct Employee *pe1 = &e1;
//(Employee*) # //强制转换为指针 指向
//printf("姓名%s,年龄%d,工资%d", e1.name, e1.age, e1.salary);
//printf("姓名%s,年龄%d,工资%d", (*pe1).name, (*pe1).age, (*pe1).salary);
printf("姓名%s,年龄%d,工资%d", pe1->name,pe1->age, pe1->salary);
}
//typedef struct
//{
// __IO uint32_t CRL;
// __IO uint32_t CRH;
// __IO uint32_t IDR;
// __IO uint32_t ODR;
// __IO uint32_t BSRR;
// __IO uint32_t BRR;
// __IO uint32_t LCKR;
//} GPIO_TypeDef;
//#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) //强制转化GPIOA为结构体指针
//#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
//
// #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
//#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
// #define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */ 指向外围设备的基地址
定义串口自测试
#include "stm32f10x.h"
void Delay(u32 count)
{
u32 i=0;
for(;i<count;i++);
}
void My_USART1_Init(void)
{
//GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//串口和IO口的时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //变量,进入assert_param ① 使能相应的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//GPIOA 9初始化 IO口初始化
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP; //推挽复用输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; //输出引脚
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //② 初始化IO口
//GPIOA 10初始化 IO口初始化
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IN_FLOATING; //浮空输出输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; //输出引脚
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //结构体,进入结构体类型 //② 初始化IO口
//USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct
//9600 N 8 1设置
USART_InitStruct.USART_BaudRate=9600;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不使用硬件流控制
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //发送接收都使能
USART_InitStruct.USART_Parity= USART_Parity_No; //校验位,0 //在参数搜索进入头文件,在函数形参搜索进入源文件->头文件
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //数据位,8
USART_InitStruct.USART_StopBits = USART_StopBits_1;
//串口初始化
USART_Init(USART1, &USART_InitStruct); //③ 初始化串口参数
//串口使能
USART_Cmd(USART1,ENABLE); //④ 使能串口
//中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //接收缓冲区非空,⑤开启接收中断
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //开启中断通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; //响应优先级
NVIC_Init(&NVIC_InitStruct);//设置中断优先级
}
void USART1_IRQHandler(void) //中断服务函数
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE))
{
res = USART_ReceiveData(USART1);
USART_SendData(USART1,res);
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级 系统优先级设置为2 ,2位抢占,2位响应
My_USART1_Init();
while(1);
}
串口实验:
#include "sys.h"
#include "usart.h"
//
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos 使用
#endif
//
//V1.5修改说明
//1,增加了对UCOSII的支持
//
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
/*使用microLib的方法*/
/*
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}
return ch;
}
int GetKey (void) {
while (!(USART1->SR & USART_FLAG_RXNE));
return ((int)(USART1->DR & 0x1FF));
}
*/
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. 200
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 TX
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 RX
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res; //
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据 一个字节一个字节接收
//bit15 接收完成标志
//bit14 接收到0x0D标志 回车:0x0D 换行:0x0A 0x0D=>0100 0x0D+0x0A=>1100
//bit13-0 有效数据
if((USART_RX_STA&0x8000)==0)//接收未完成 1000 0000 0000 0000
{
if(USART_RX_STA&0x4000)//上次接收到了0x0d回车 0100 0000 0000 0000
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始 本次接收数据不是0x0a换行: Res是8位
else USART_RX_STA|=0x8000; //接收完成了 (回车+换行) 1000 0000 0000 0000 最高位设置为1
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000; //0100 0000 0000 0000 bit14置位为1
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; //0011 1111 1111 1111 确定长度 前13位有效 //u8 BUF缓冲 u16 STA标志
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
#endif
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
/************************************************
************************************************/
int main(void)
{
u16 t;
u16 len;
u16 times=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(9600); //串口初始化为9600
LED_Init(); //LED端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000) //判断最高位
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\n串口实验\r\n");
printf("ZGZG@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
外部中断实验:
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "beep.h"
//
//
//外部中断0服务程序
void EXTIX_Init(void)
{
/*
1,初始化IO口为输入
GPIO_Init();
2,开启IO口复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
3,设置IO口与中断线的映射关系
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
4,初始化线上中断,设置触发条件等
EXTI_Init(&EXTI_InitStructure);
5,配置中断分组(NVIC),并使能中断
NVIC_Init(&NVIC_InitStructure);
6,编写中断服务函数
EXTI0_IRQHandler(void)
7,清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0)
*/
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init(); // 按键端口初始化 默认为GPIO_Mode_IPU = 0x48, 上拉
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟 APB2的外设时钟
//GPIOE.2 中断线以及中断初始化配置 下降沿触发
/*
GPIO_Pin_4和GPIO_PinSource4,GPIO_Pin_4是每个引脚对应的相对于GPIOA~GPIOG的基地址的对应地址变化,
而GPIO_PinSource4是在引脚复用时的对应的复用序列号,
因为每组引脚都对应着16种复用序号,所以GPIO_PinSource4对应的地址号0x04就是第五个复用序号,
因为是从零开始的。代表的是顺序号。和GPIO_Pin_4不一样。
PA0
PB0 7个 => ExTi0
...
PG0
PA1
PB1 7个 => ExTi1
...
PG1
...
PA15
PB15 7个 => ExTi15
...
PG15
总共有16*7=112个映射 16个中断线
但同一时间只能一个IO口映射到一个中断线
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)//读取按键2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP)
*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2); //定义在stm32f10x_gpio.h ,STM32的IO口使用了PE,对应中断线为ExTi4
//初始化外部中断线
EXTI_InitStructure.EXTI_Line=EXTI_Line2; //KEY2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOE.3 中断线以及中断初始化配置 下降沿触发 //KEY1
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3); //端口GPIO_PortSourceGPIOE 外部线GPIO_PinSource3
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOE.4 中断线以及中断初始化配置 下降沿触发 //KEY0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.0 中断线以及中断初始化配置 上升沿触发 PA0 WK_UP
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键WK_UP所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能按键KEY2所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //使能按键KEY1所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //使能按键KEY0所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(WK_UP==1) //WK_UP按键 GPIOA
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY2==0) //按键KEY2
{
LED0=!LED0;
}
EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE2上的中断标志位
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY1==0) //按键KEY1
{
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
void EXTI4_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY0==0) //按键KEY0
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位
}
#include "stm32f10x.h"
#include "key.h"
#include "sys.h"
#include "delay.h"
//
//
//按键初始化函数
void KEY_Init(void) //IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//KEY0-KEY2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4
//初始化 WK_UP-->GPIOA.0 下拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下
//4,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(WK_UP==1)return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "exti.h"
#include "beep.h"
/************************************************
************************************************/
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
BEEP_Init(); //初始化蜂鸣器端口
KEY_Init(); //初始化与按键连接的硬件接口
EXTIX_Init(); //外部中断初始化
LED0=0; //点亮LED0
while(1)
{
printf("OK\r\n");
delay_ms(1000);
}
}
定时器
#include "timer.h"
#include "led.h"
//
//
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
/*
1,使能定时器时钟
RCC_APB1PeriphClockCmd()
2,初始化定时器,配置ARR自动重装载寄存器周期的值, PSC: 设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseInit()
3,开启定时器中断配置NVIC
Void TIM_ITConfig()
NVIC_Init()
4,使能定时器
TIM_Cmd()
先说结论, TIM3_Int_Init(5000-1,8400-1)这一句是0.5s进入一次的中断。
中断代码,8400是分频系数,5000是预装载值。我们使用的是TIM3.
根据时钟树,可以知道TIM3挂载在APB1下,APB1的时钟频率是=42MHZ,但是因为分频系数不等于1,所以TIM3的时钟为APB1的频率*2=84MHZ。
*/
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //1,时钟使能
/*
voidTIM_TimeBaseInit(TIM_TypeDef*TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
第一个参数是确定是哪个定时器
第二个参数是定时器初始化参数结构体指针,结构体类型为 TIM_TimeBaseInitTypeDef
typedef struct
{
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMode;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
对于通用定时器只有前面四个参数有用,最后一个参数 TIM_RepetitionCounter 是高级定时器才有用的
第一个参数 TIM_Prescaler 是用来设置分频系数
第二个参数 TIM_CounterMode 是用来设置计数方式
第三个参数是设置自动重载计数周期值
第四个参数是用来设置时钟分频因子
*/
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值, 传入值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值, 传入值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //2,根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //3,使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //3, 初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //4, 使能TIMx
}
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
LED1=!LED1;
}
}
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
/************************************************
************************************************/
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
/************************************************
************************************************/
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
//10Khz的计数频率,
//u16 arr 设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms u16 psc 设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率 u16 arr,u16 psc
TIM3_Int_Init(4999,7199);
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}
}
PWM实验
#include "timer.h"
#include "led.h"
//
//
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
/*
1,使能定时器时钟
RCC_APB1PeriphClockCmd()
2,初始化定时器,配置ARR自动重装载寄存器周期的值, PSC: 设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseInit()
3,开启定时器中断配置NVIC
Void TIM_ITConfig()
NVIC_Init()
4,使能定时器
TIM_Cmd()
先说结论, TIM3_Int_Init(5000-1,8400-1)这一句是0.5s进入一次的中断。
中断代码,8400是分频系数,5000是预装载值。我们使用的是TIM3.
根据时钟树,可以知道TIM3挂载在APB1下,APB1的时钟频率是=42MHZ,但是因为分频系数不等于1,所以TIM3的时钟为APB1的频率*2=84MHZ。
*/
//
/*
#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \ //模拟输入 //浮空输入
((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \ //输入下拉 //输入上拉
((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \ //开漏输出 //推挽式输出
((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP)) //开漏复用输出 //推挽式复用输出
16*7(ABCDEFG)=112个IO
#define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || \
((PIN) == GPIO_Pin_1) || \
((PIN) == GPIO_Pin_2) || \
((PIN) == GPIO_Pin_3) || \
((PIN) == GPIO_Pin_4) || \
((PIN) == GPIO_Pin_5) || \
((PIN) == GPIO_Pin_6) || \
((PIN) == GPIO_Pin_7) || \
((PIN) == GPIO_Pin_8) || \
((PIN) == GPIO_Pin_9) || \
((PIN) == GPIO_Pin_10) || \
((PIN) == GPIO_Pin_11) || \
((PIN) == GPIO_Pin_12) || \
((PIN) == GPIO_Pin_13) || \
((PIN) == GPIO_Pin_14) || \
((PIN) == GPIO_Pin_15))
*/
/*
1,使能定时器3和相关的IO口时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器 3 时钟.
库函数使能 TIM3 时钟的方法是:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //复用时钟使能 设置 PB5(GPIO B 在APB2上) 为复用功能输出
APB1 上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 等等,
APB2 上面连接的是高速外设包括 UART1、SPI1、Timer1、ADC1、ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等
APB1负责DA,串口2,3,4,5,普通定时器TIM2-7, USB , IIC , CAN
APB2负责AD(ADC1,2,3),I/O,串口1,高级定时器TIM1,TIM8, GPIOA,GPIOB
TIM3_CH2 默认是接在 PA7上面 ,需要重映射配置到 PB5 为复用输出。
要使用 TIM3,我们必须先开启 TIM3 的时钟 这是因为 TIM3_CH2 通道将重映射到 PB5 上,此时,PB5
属于复用功能输出。
2,初始化IO口为复用功能输出。函数GPIO_Init();
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
3,需要把PB5作为定时器的PWM输出引脚,所以要重映射配置,需要开启AFIO时钟,同时设置重映射。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_Tim3,ENABLE);
4,初始化定时器,ARR,PSC等
TIM_TimeBaseInit();
5,初始化输出比较参数:TIM_OC2Init();
6,使能预装载寄存器: TIM_OC2PreloadConfig(TIM3,TIM_OC2Preload_Enable);
7,使能定时器。 TIM_Cmd();
8,不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();
*/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
//定义GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStructrue;
//定时器结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructrue;
//PWM 通道设置结构体
TIM_OCInitTypeDef TIM_OCInitStructrue;
//1,时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能定时器3时钟 参数:复用功能时钟,使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); //使能GPIO外设和AFIO复用功能模块时钟 或运算:2个都初始化
//2,初始化IO口为复用功能输出。函数GPIO_Init();
//GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructrue.GPIO_Pin=GPIO_Pin_5; //LED->PB.5 端口配置 //指定IO口
GPIO_InitStructrue.GPIO_Mode= GPIO_Mode_AF_PP; // 指定IO口模式(8种IO口模式中的1种) 复用推挽输出
GPIO_InitStructrue.GPIO_Speed=GPIO_Speed_50MHz; //指定IO口的速度
GPIO_Init(GPIOB,&GPIO_InitStructrue);
//3,需要把PB5作为定时器的PWM输出引脚,所以要重映射配置,需要开启AFIO时钟,同时设置重映射。
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//GPIO_PinRemapConfig(GPIO_PartialRemap_Tim3,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //定时器3部分重映射GPIO_PartialRemap_TIM3
//4,初始化定时器,ARR,PSC等
//TIM_TimeBaseInit();
/*
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMode;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
*/
TIM_TimeBaseStructrue.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器的周期的值
TIM_TimeBaseStructrue.TIM_RepetitionCounter = psc; //设置用来作为TIMX时钟
TIM_TimeBaseStructrue.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS=Tck_tim
TIM_TimeBaseStructrue.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructrue); //根据指定的参数初始化TIMx的时间基数单位
//5,初始化输出比较参数:TIM_OC2Init(); PWM 通道设置
//参数 TIM_OCMode 设置模式是 PWM 还是输出比较,这里是 PWM 模式。
//参数 TIM_OutputState 用来设置比较输出使能,也就是使能 PWM 输出到端口。
//参数 TIM_OCPolarity 用来设置极性是高还是低。
TIM_OCInitStructrue.TIM_OCMode=TIM_OCMode_PWM2; //选择 PWM 模式 2
TIM_OCInitStructrue.TIM_OCPolarity=TIM_OCPolarity_High; //输出极性高
TIM_OCInitStructrue.TIM_OutputState= TIM_OutputState_Enable; //比较输出使能
TIM_OC2Init(TIM3,&TIM_OCInitStructrue);
//6,使能预装载寄存器: TIM_OC2PreloadConfig(TIM3,TIM_OC2Preload_Enable);
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能预装载寄存器
//7,使能定时器。 TIM_Cmd();
TIM_Cmd(TIM3, ENABLE);
//8,不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2(); 主函数中
}
}
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
/************************************************
从死循环函数可以看出,将 led0pwmval 这个值设置为 PWM 比较值,也就
是通过 led0pwmval 来控制 PWM 的占空比,然后控制 led0pwmval 的值从 0 变到 300,然后又
从 300 变到 0,如此循环,因此 DS0 的亮度也会跟着从暗变到亮,然后又从亮变到暗。至于这
里的值,我们为什么取 300,是因为 PWM 的输出占空比达到这个值的时候,我们的 LED 亮度
变化就不大了(虽然最大值可以设置到 899),因此设计过大的值在这里是没必要的。
************************************************/
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
TIM3_PWM_Init(899,0); //不分频。PWM频率=72000000/900=80Khz
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare2(TIM3,led0pwmval); //LED灯低电平有效,不断改变占空比,改变灯的亮度
}
}
I2C实验:
#include "myiic.h"
#include "delay.h"
//
//
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low SCL,SDA全拉高。 SCL为高时, SDA随后拉低,开始
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high SCL SDA全拉低。 SCL为高之后,SDA也拉高,结束
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA) //如果输入线READ_SDA一直为高,则为无应答。
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0 READ_SDA低电平有效
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2); //SCL为高时, SDA一直为低 产生应答 第九个时钟期间
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2); //SCL为高时 SDA一直为高, 不产生应答
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
//IIC_SDA=(txd&0x80)>>7;
if((txd&0x80)>>7) //发送的数据最高位 右移7位变成第0位。如果 为1,则SDA置为1
IIC_SDA=1; //发送1
else
IIC_SDA=0; //否则发送0
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1; // SCL 为高的时候 发送数据不能变
delay_us(2);
IIC_SCL=0; //SCL为低才能变 发信号
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1; //与上面相反,SCL为高时候读信号
receive<<=1; //左移一位
if(READ_SDA)receive++; //如果READ_SDA为1,则对receive最低位加1
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//
//
//IO方向设置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函数
#define IIC_SCL PBout(6) //SCL 时钟线
#define IIC_SDA PBout(7) //SDA 数据线 半双工
#define READ_SDA PBin(7) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
Can通信
#include "can.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
//
//
//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
//返回值:0,初始化OK;
// 其他,初始化失败;
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
//CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode; //模式设置: mode:0,普通模式;1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位宽
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
#if CAN_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; // 标准标识符
TxMessage.ExtId=0x12; // 设置扩展标示符
TxMessage.IDE=CAN_Id_Standard; // 标准帧
TxMessage.RTR=CAN_RTR_Data; // 数据帧
TxMessage.DLC=len; // 要发送的数据长度
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束
if(i>=0XFFF)return 1;
return 0;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "can.h"
/************************************************
************************************************/
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 canbuf[8];
u8 res;
u8 mode=CAN_Mode_LoopBack;//CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init(); //初始化LCD
KEY_Init(); //按键初始化
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);//CAN初始化环回模式,波特率500Kbps
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"CAN TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2015/1/15");
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
LCD_ShowString(60,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,170,200,16,16,"Count:"); //显示当前计数值
LCD_ShowString(60,190,200,16,16,"Send Data:"); //提示发送的数据
LCD_ShowString(60,250,200,16,16,"Receive Data:"); //提示接收到的数据
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES)//KEY0按下,发送一次数据
{
for(i=0;i<8;i++)
{
canbuf[i]=cnt+i;//填充发送缓冲区
if(i<4)LCD_ShowxNum(60+i*32,210,canbuf[i],3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,230,canbuf[i],3,16,0X80); //显示数据
}
res=Can_Send_Msg(canbuf,8);//发送8个字节
if(res)LCD_ShowString(60+80,190,200,16,16,"Failed"); //提示发送失败
else LCD_ShowString(60+80,190,200,16,16,"OK "); //提示发送成功
}else if(key==WKUP_PRES)//WK_UP按下,改变CAN的工作模式
{
mode=!mode;
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);//CAN普通模式初始化, 波特率500Kbps
POINT_COLOR=RED;//设置字体为红色
if(mode==0)//普通模式,需要2个开发板
{
LCD_ShowString(60,130,200,16,16,"Nnormal Mode ");
}else //回环模式,一个开发板就可以测试了.
{
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
}
POINT_COLOR=BLUE;//设置字体为蓝色
}
key=Can_Receive_Msg(canbuf);
if(key)//接收到有数据
{
LCD_Fill(60,270,130,310,WHITE);//清除之前的显示
for(i=0;i<key;i++)
{
if(i<4)LCD_ShowxNum(60+i*32,270,canbuf[i],3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,290,canbuf[i],3,16,0X80); //显示数据
}
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0;//提示系统正在运行
t=0;
cnt++;
LCD_ShowxNum(60+48,170,cnt,3,16,0X80); //显示数据
}
}
}
.
CAN通信实验:
#include "can.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
//
//
//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
//返回值:0,初始化OK;
// 其他,初始化失败;
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
//CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode; //模式设置: mode:0,普通模式;1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位宽
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
#if CAN_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; // 标准标识符
TxMessage.ExtId=0x12; // 设置扩展标示符
TxMessage.IDE=CAN_Id_Standard; // 标准帧
TxMessage.RTR=CAN_RTR_Data; // 数据帧
TxMessage.DLC=len; // 要发送的数据长度
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束
if(i>=0XFFF)return 1;
return 0;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "can.h"
/************************************************
************************************************/
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 canbuf[8];
u8 res;
u8 mode=CAN_Mode_LoopBack;//CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init(); //初始化LCD
KEY_Init(); //按键初始化
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);//CAN初始化环回模式,波特率500Kbps
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"CAN TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2015/1/15");
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
LCD_ShowString(60,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,170,200,16,16,"Count:"); //显示当前计数值
LCD_ShowString(60,190,200,16,16,"Send Data:"); //提示发送的数据
LCD_ShowString(60,250,200,16,16,"Receive Data:"); //提示接收到的数据
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES)//KEY0按下,发送一次数据
{
for(i=0;i<8;i++)
{
canbuf[i]=cnt+i;//填充发送缓冲区
if(i<4)LCD_ShowxNum(60+i*32,210,canbuf[i],3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,230,canbuf[i],3,16,0X80); //显示数据
}
res=Can_Send_Msg(canbuf,8);//发送8个字节
if(res)LCD_ShowString(60+80,190,200,16,16,"Failed"); //提示发送失败
else LCD_ShowString(60+80,190,200,16,16,"OK "); //提示发送成功
}else if(key==WKUP_PRES)//WK_UP按下,改变CAN的工作模式
{
mode=!mode;
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);//CAN普通模式初始化, 波特率500Kbps
POINT_COLOR=RED;//设置字体为红色
if(mode==0)//普通模式,需要2个开发板
{
LCD_ShowString(60,130,200,16,16,"Nnormal Mode ");
}else //回环模式,一个开发板就可以测试了.
{
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
}
POINT_COLOR=BLUE;//设置字体为蓝色
}
key=Can_Receive_Msg(canbuf);
if(key)//接收到有数据
{
LCD_Fill(60,270,130,310,WHITE);//清除之前的显示
for(i=0;i<key;i++)
{
if(i<4)LCD_ShowxNum(60+i*32,270,canbuf[i],3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,290,canbuf[i],3,16,0X80); //显示数据
}
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0;//提示系统正在运行
t=0;
cnt++;
LCD_ShowxNum(60+48,170,cnt,3,16,0X80); //显示数据
}
}
}