首页 > 编程语言 >stm32单片机编程模块

stm32单片机编程模块

时间:2022-09-28 05:44:15浏览次数:54  
标签:NVIC USART TIM void 编程 stm32 单片机 InitStruct GPIO

0 keil5库函数配置

1、建立工程文件夹,Keil中新建工程,选择型号
2、工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹。

​ Start文件夹中:

  • 复制 \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm下,8个启动文件之一(stm32f103c8 64kflash,需要后缀是md.s,就复制这一个),程序从启动文件开始执行。
  • 复制\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x 下的3个文件,stm32f10x.h(描述外设寄存器名称和地址,类似regx52.h),system_stm32f10x.c, system_stm32f10x.h(配置时钟用)
  • 复制\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport 下的两个文件 core_cm3.c,core_cm3.h(内核寄存器描述及内核的配置函数)

​ User文件夹:

  • 建立文件main.c
  • 复制 \STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template下的 stm32f10x_conf.h(配置库函数头文件的包含关系及用来函数检查的函数定义),stm32f10x_it.c,stm32f10x_it.h(用来存放中断函数的)

Library文件夹:

  • 复制 \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src 下的所有文件 misc.c(内核的库函数),其他的是外设的库函数

  • 复制和src同级文件夹inc中的所有头文件

3、工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里(用keil5中的工程管理Manage Project Items)
4、工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹Start、Library、Use以及根据项目需要建立的文件夹
5、工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
6、工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run

1 GPIO模块

一、库函数点亮LED

led.h

#ifndef __LED_H__
#define __LED_H__

#define LED_GPIO_Port GPIOB
#define LED_GPIO_Pin GPIO_Pin_8
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB

void Led_Init(void);
void LED_GPIO_High(void);
void LED_GPIO_Low(void);
void LED_GPIO_Cpl(void);

#endif

led.c

#include "stm32f10x.h"  
#include "led.h"

void Led_Init(void)
{
	RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE); 
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=LED_GPIO_Pin;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
}

void LED_GPIO_High(void)
{
	GPIO_SetBits(LED_GPIO_Port, LED_GPIO_Pin);
}

void LED_GPIO_Low(void)
{
	GPIO_ResetBits(LED_GPIO_Port, LED_GPIO_Pin);
}

void LED_GPIO_Cpl(void)
{
	GPIO_WriteBit(LED_GPIO_Port, LED_GPIO_Pin, \
	(BitAction)(GPIO_ReadOutputDataBit(LED_GPIO_Port, LED_GPIO_Pin)? 0:1));
}

main.c

#include "stm32f10x.h" 
#include "led.h" 
#include "Delay.h"

int main(void)
{
	Led_Init();
	while(1)
	{
		LED_GPIO_High();
		Delay_ms(500);
		LED_GPIO_Low();
		Delay_ms(500);
		LED_GPIO_Cpl();
		Delay_ms(500);
		LED_GPIO_Cpl();
		Delay_ms(500);
	}
}

2 SYSTICK滴答定时器

systick 实现延时程序1(启停定时器影响CLKSOURCE位)

void Delay_us(uint32_t xus)  //最大72*xus不能超过2的24次方
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值(72Mhz,计数72个为1us)
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;	//置位CLKSOURCE,ENABLE,时钟源为72Mhz。如果复位,除以8为9Mhz。
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器,
}//SysTick等同于((SysTick_Type*)(0xE000E010)) SysTick_Type是结构体

systick 实现延时程序2

void delay_init()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟,HCLK/8为9Mhz。
	fac_us = SystemCoreClock / 8000000; //数值为9
	fac_ms = (u16)fac_us*1000;  //数值为9000
}

void Delay_us(u32 xus)  //void Delay_ms(u16 nms) 
{
	u32 temp;
	SysTick->LOAD = fac_us * xus;			//设置定时器重装值us,LOAD值不能超过2的24次方
	//SysTick->LOAD = (u32)fac_ms * nms;	//设置定时器重装值ms,最大1864ms
	SysTick->VAL = 0x00;					    //清空当前计数值
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;	//启动定时器
	do
	{
		temp = SysTick->CTRL;
	}while((temp & 0x01) && !(temp&(1<<16)));  //如果定时器已经启动并且COUNTFLAG为0,等待
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 停止计数器
	SysTick->VAL = 0x00;                       //清空当前计数值
}

systick 设置计数值,中断处理

SysTick_Config(uint32_t ticks) //时钟源为内部时钟72Mhz,参数最大不能超过0xFFFFFF,大概233ms,设置中断优先级,
void SysTick_Handler(void)  
{
	LED_GPIO_Cpl();
}    

3 外部中断编程

外部中断EXTI

void exti_init(void) //PB9 外部中断
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); //EXTI、NVIC时钟不用手动开启
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9); //afio选择外部中断引脚
	
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式,别的选项是事件模式
	EXTI_InitStruct.EXTI_Line=EXTI_Line9;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发,别的选项上升沿、上升下降沿
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=EXTI9_5_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStruct);
}

void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line9)==SET)
	{
		//中断处理代码
		EXTI_ClearITPendingBit(EXTI_Line9);
	}
}

4 定时器编程

void time3_init(u16 arr, u16 psc) //arr为9999,psc为7199,定时周期为10000*7200/(72*1000000)=1s
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能time3时钟
	
	TIM_InternalClockConfig(TIM3); //配置time3 选择时钟为内部时钟
	//选择ETR引脚通过外部时钟模式2输入的时钟(对time2为PA0,time3为PD2)
	//TIM_ETRClockMode2Config(TIM3, TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);	
	//参数:预分频,极性,滤波器(采样频率和采样次数),当然需要对GPIO引脚配置,浮空或者上拉输入
	/*
	TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx其它定时器的时钟
	TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter); //选择TIx捕获通道的时钟
	TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                             uint16_t ExtTRGFilter); //选择ETR通过外部时钟模式1输入的时钟
	TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                   uint16_t ExtTRGFilter);//用来单独配置ETR引脚的预分频器、极性、滤波器这些参数的
	*/	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;  //对CNT计数器,SetCounter和GetCounter读写
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //死区及数字滤波采样时钟分频比
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInitStruct.TIM_Period=arr;    //重装载值
	TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //预分频系数
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); //配置时基单元
	
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);  //解决刚运行就进入中断的问题
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //定时器中断中使能更新中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组,可以在main函数中出现一次
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM3_IRQn; //中断源为time3
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //等待优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;        //抢占优先级
	NVIC_Init(&NVIC_InitStruct);
	
	TIM_Cmd(TIM3, ENABLE); //使能定时器
}
void TIM3_IRQHandler(void)
{
	
	if(TIM_GetITStatus(TIM3, TIM_IT_Update)==SET)
	{
		LED_GPIO_Cpl(); //反转led口
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}
}

5 串口编程模块

状态寄存器重要位:

TXE:发送数据寄存器空(Transmit data register empty)复位为空1

TC:发送完成(Transmission complete)复位为完成1

RXNE:读数据寄存器非空(Read data register not empty)复位为空0

void my_usart1_init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //串口1:PA9 TX 发送引脚
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;  //PA10 RX 接受引脚
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate=9600; //波特率
	USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; //发送接收全双工
	USART_InitStruct.USART_Parity=USART_Parity_No;   //奇偶校验位无
	USART_InitStruct.USART_StopBits=USART_StopBits_1;   //停止位1
	USART_InitStruct.USART_WordLength=USART_WordLength_8b; //数据位数
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件数据流控制
	USART_Init(USART1, &USART_InitStruct);
	
	//开启中断并且配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
		
	USART_Cmd(USART1, ENABLE);
}
void usart_send_string(USART_TypeDef* USARTx, char *str)
{
	while(*str!='\0')
	{
		USART_SendData(USARTx, (u16)(*str++));
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==0);
	}
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)==0);
}
void USART_SendByte(USART_TypeDef* USARTx, uint8_t Data)
{
	assert_param(IS_USART_ALL_PERIPH(USARTx));
	assert_param(IS_USART_DATA(Data)); 

	USARTx->DR = (Data & (uint16_t)0x01FF);
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
}

void USART1_IRQHandler(void)
{
	u16 temp;
	if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET)
	{
		temp = USART_ReceiveData(USART1);
		USART_SendByte(USART1, temp);
//		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

使用printf语句,方便把信息通过串口输出到电脑显示,下面是响应的设置

#pragma import(__use_no_semihosting)  //在串口模块输入下面代码

struct __FILE
{
	int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
	x=x;
}

//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (u8)ch);
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
	return ch;
}
//重定向c库函数scanf到串口,重定向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
	while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)==RESET);
	return (int)USART_ReceiveData(USART1);
}
/* // main.c程序中使用printf,当然 需要include "stdio.h"
	u32 temp=12345;
	float f=12.3478;
	my_usart1_init(); //使用printf可以不开启串口中断
	printf("temp=%d\r\n", temp);
	printf("temp=%f\r\n", f);
*/

标签:NVIC,USART,TIM,void,编程,stm32,单片机,InitStruct,GPIO
From: https://www.cnblogs.com/sunwenping/p/16736634.html

相关文章

  • 51单片机
    1定时器0延时模块Timer0Delay.c代码#include<REGX52.H>/***@brief定时器0初始化,1ms中断一次*@param无*@retval无*/voidTimer0Init(){......
  • UNIX网络编程 卷1 套接字联网API pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1myJIO8uvfssIkwiK-63Agg点击这里获取提取码 ......
  • 面向对象编程中级部分习题
    1.作业1定义一个Person类{name,age,job},初始化Person对象数组,有3个person对象,并按照age从大到小进行排序,提示:使用冒泡排序.packagecom.yt.homwork.homework01;pub......
  • Python基础(五) | 函数及面向过程编程详解
    第五章函数⭐本专栏旨在对Python的基础语法进行详解,精炼地总结语法中的重点,详解难点,面向零基础及入门的学习者,通过专栏的学习可以熟练掌握python编程,同时为后续的数据分析,......
  • Java GUI编程(一)AWT
    GUI是什么?GUI怎么用?组件:窗口弹窗面板文本框列表框按钮图片监听事件鼠标键盘事件 这其实就是一个GUI1,简介:GUI核......
  • TCP IP网络编程 pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1etFFpH6JwsA8acuHMBh9rg点击这里获取提取码 ......
  • 51单片机学习日志一:流水灯和定时器
    单片机最小系统的三要素就是电源、晶振、复位电路单片机复位一般是3种情况:上电复位、手动复位、程序自动复位。没有电压差就不会产生电流一、sbit在单片机中的使用C5......
  • C++多线程编程之【线程管理】
    1.如何启动线程?构建std::thread对象即可。直接传函数名(地址)创建一个类并创建伪函数。构建对象(实例化),将对象作为参数传入thread对象实例化。2.为什么要等待线程?首先......
  • 多线程——Robyn编程学习(Java)
    多线程的作用能够创建多个线程,此外线程可以体现程序的动态性,提高效率,在抢票以及各种游戏之中具有非常重要的作用。(线程的魅力在坦克大战中体现的淋漓尽致)多线程的知识体......
  • 面向对象编程跟面向过程的区别
    转: https://zhuanlan.zhihu.com/p/75265007?utm_id=02分钟让你明白什么是面向对象编程呜呜轩轩行业小司机,说点实在话。别呜呜轩轩  相信很......