PART6——DMA、按键、数码管、编码器、LM75A(硬件IIC)、继电器、步进电机、摇杆、ADC+DMA

DMA

步骤一:配置系统时钟
在配置DMA之前,首先需要配置系统时钟,确保所有外设都能正常工作。以下是一个简单的时钟配置示例,假设使用的是STM32F103芯片。
#include "stm32f10x.h"

void RCC_Config(void) {
    // 配置系统时钟为72MHz,使用外部晶振HSE为8MHz
    RCC_DeInit(); // 复位RCC寄存器
    RCC_HSEConfig(RCC_HSE_ON); // 启动外部晶振
    RCC_WaitForHSEStartUp(); // 等待外部晶振启动

    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // 配置PLL时钟为8MHz * 9 = 72MHz
    RCC_PLLCmd(ENABLE); // 启动PLL
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // 等待PLL稳定

    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // 将PLL时钟设置为系统时钟
    while (RCC_GetSYSCLKSource() != 0x08); // 等待PLL成为系统时钟

    RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB时钟 = 系统时钟
    RCC_PCLK2Config(RCC_HCLK_Div1); // APB2时钟 = 系统时钟
    RCC_PCLK1Config(RCC_HCLK_Div2); // APB1时钟 = 系统时钟 / 2

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
   
}

步骤二:配置DMA控制器6void DMA_Config(void) {
    DMA_InitTypeDef DMA_InitStructure;

    // 使能DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // DMA初始化
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); // 外设地址,比如这里使用USART1的数据寄存器地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer; // 内存地址,比如buffer数组的地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 数据传输方向:外设到内存
    DMA_InitStructure.DMA_BufferSize = sizeof(buffer); // 数据大小,比如buffer数组的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 禁止外设地址自增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 允许内存地址自增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 内存数据大小为字节  半字 字 1 2 4 
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据大小为字节  半字 字 1 2 4 
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA传输模式:单次模式:传输计数器计到0(字节)时结束,循环模式计数到0自动重装
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA优先级:高
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁止内存到内存模式DMA_M2M_Disable:禁用内存到内存模式。这意味着DMA传输只能在外设和内存之间进行,而不能直接在两个内存区域之间传输数据。
//DMA_M2M_Enable:启用内存到内存模式。这时DMA可以直接在两个内存区域之间传输数据,而不需要通过外设。这种模式适用于需要在内存之间进行数据复制或传输的场景。
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    // 使能DMA通道
    DMA_Cmd(DMA1_Channel1, ENABLE);
//DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);
}

步骤三:初始化外设
void USART_Config(void) {
    USART_InitTypeDef USART_InitStructure;

    // 使能USART时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // USART初始化
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART
    USART_Cmd(USART1, ENABLE);
    //使能外设DMA
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
}

步骤四:启动DMA传输
void Start_DMA_Transfer(void) {
    // 清除标志位
    DMA_ClearFlag(DMA1_FLAG_TC1);

    // 启动DMA传输
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

步骤五:处理DMA传输完成中断(如果需要)
如果配置了DMA传输完成中断,在中断处理函数中进行相应的处理。
void DMA1_Channel1_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC1)) {
        // DMA传输完成中断处理
        // 清除中断标志
        DMA_ClearITPendingBit(DMA1_IT_TC1);
    }
}
//若不用中断
void DMAx Enable(DMA Channel TypeDef* DMAy channelx,u16 ndtr)
{
DMA Cmd(DMAy Channelx,DISABLE);
DMA SetCurrDataCounter(DMAy Channelx,ndtr);
DMA Cmd(DMAy Channelx,DISABLE):
while(DMA_GetFlagStatus(DMA1_FlAG_TC1)==RESET)DMA_ClearFlag(DMA1_FlAG_TC1);
}

在这里插入图片描述
是否完成传输
传输数据剩余量
设置传输数据剩余量

在这里插入图片描述

按键双击和长按

#define KEYA_SPEED1	100	  //长按的时间长度(单位10mS)
#define KEYA_SPEED2	10	  //双击的时间长度(单位20mS)


int main (void){//主程序
	u8 a=0,b,c=0;
	RCC_Configuration(); //系统时钟初始化 
	LED_Init();//LED初始化
	TOUCH_KEY_Init();//按键初始化
	while(1){

		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
				}else{ //单击处理
					for(b=0;b<KEYA_SPEED2;b++){//检测双击
						delay_ms(20);
						if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
							a=1;
							//双击后执行的程序放到此处
							GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制

							while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
						}
					}
					if(a==0){ //判断单击
						//单击后执行的程序放到此处
						GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
						
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束

	}
}

触摸按键滑动程序

#define KEYA_SPEED1	100	  //长按的时间长度(单位10mS)
#define KEYA_SPEED2	10	  //双击的时间长度(单位20mS)



int main (void){//主程序
	u16 k=1000;	//用于滑动加减计数
	u8 a=0,b,c=0;
	u8 s=0; //刚刚结束滑动标志
	RCC_Configuration(); //系统时钟初始化 
	USART1_Init(115200); //串口初始化,参数中写波特率
	LED_Init();//LED初始化
	TOUCH_KEY_Init();//按键初始化
	while(1){
//A
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("A键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
				}else{ //单击处理
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){
						k++; //用于显示的计数值
						printf("A键右滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("A键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("A键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
//B
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("B键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B));
				}else{ //单击处理
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){
						k++;
						printf("B键右滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
						k--;
						printf("B键左滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("B键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("B键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
//C
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("C键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C));
				}else{ //单击处理
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){
						k++;
						printf("C键右滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){
						k--;
						printf("C键左滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("C键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("C键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
//D
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("D键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D));
				}else{ //单击处理
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){
						k--;
						printf("D键左滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("D键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("D键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束

	}
}


*/

数码管

单位

在这里插入图片描述
在这里插入图片描述

八位

在这里插入图片描述
mcu
在这里插入图片描述
在这里插入图片描述

旋转编码器

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 卡死时都位低电平
    在这里插入图片描述

非测速(按键)

#include "encoder.h"


u8 KUP;//旋钮锁死标志(1为锁死)
u16 cou;

void ENCODER_Init(void){ //接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
    GPIO_InitStructure.GPIO_Pin = ENCODER_L | ENCODER_D; //选择端口号                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(ENCODER_PORT_A,&GPIO_InitStructure);	

    GPIO_InitStructure.GPIO_Pin = ENCODER_R; //选择端口号                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(ENCODER_PORT_B,&GPIO_InitStructure);				
}

u8 ENCODER_READ(void){ //接口初始化
	u8 a;//存放按键的值
	u8 kt;
	a=0;
	if(GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L))KUP=0;	//判断旋钮是否解除锁死
	if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)&&KUP==0){ //判断是否旋转旋钮,同时判断是否有旋钮锁死
		delay_us(100);
		kt=GPIO_ReadInputDataBit(ENCODER_PORT_B,ENCODER_R);	//把旋钮另一端电平状态记录
		delay_ms(3); //延时
		if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)){ //去抖
			if(kt==0){ //用另一端判断左或右旋转
				a=1;//右转
			}else{
				a=2;//左转
			}
			cou=0; //初始锁死判断计数器
			while(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)&&cou<60000){ //等待放开旋钮,同时累加判断锁死
				cou++;KUP=1;delay_us(20); //
			}
		}
	}
	if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_D)&&KUP==0){ //判断旋钮是否按下  
		delay_ms(20);
		if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_D)){ //去抖动
			a=3;//在按键按下时加上按键的状态值
			//while(ENCODER_D==0);	等等旋钮放开
		}
	}
	return a;
} 

测速

LM75A

在这里插入图片描述

#include "lm75a.h"



//读出LM75A的温度值(-55~125摄氏度)
//温度正负号(0正1负),温度整数,温度小数(点后2位)依次放入*Tempbuffer(十进制)
void LM75A_GetTemp(u8 *Tempbuffer){   
    u8 buf[2]; //温度值储存   
    u8 t=0,a=0;   
    I2C_READ_BUFFER(LM75A_ADD,0x00,buf,2); //读出温度值(器件地址,子地址,数据储存器,字节数)
	t = buf[0]; //处理温度整数部分,0~125度
	*Tempbuffer = 0; //温度值为正值
	if(t & 0x80){ //判断温度是否是负(MSB表示温度符号)
		*Tempbuffer = 1; //温度值为负值
		t = ~t; t++; //计算补码(原码取反后加1)
	}
	if(t & 0x01){ a=a+1; } //从高到低按位加入温度积加值(0~125)
	if(t & 0x02){ a=a+2; }
	if(t & 0x04){ a=a+4; }
	if(t & 0x08){ a=a+8; }
	if(t & 0x10){ a=a+16; }
	if(t & 0x20){ a=a+32; }
	if(t & 0x40){ a=a+64; }
	Tempbuffer++;
	*Tempbuffer = a;
	a = 0;
	t = buf[1]; //处理小数部分,取0.125精度的前2位(12、25、37、50、62、75、87)
	if(t & 0x20){ a=a+12; }
	if(t & 0x40){ a=a+25; }
	if(t & 0x80){ a=a+50; }
	Tempbuffer++;
	*Tempbuffer = a;   
}

//LM75进入掉电模式,再次调用LM75A_GetTemp();即可正常工作
//建议只在需要低功耗情况下使用
void LM75A_POWERDOWN(void){// 
    I2C_SAND_BYTE(LM75A_ADD,0x01,1); //
}

IIc

#include "i2c.h"


void I2C_GPIO_Init(void){ //I2C接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //启动I2C功能 
    GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA; //选择端口号                      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(I2CPORT, &GPIO_InitStructure);
}

void I2C_Configuration(void){ //I2C初始化
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_GPIO_Init(); //先设置GPIO接口的状态
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置为I2C模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_OwnAddress1 = HostAddress; //主机地址(从机不得用此地址)
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//允许应答
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址模式
	I2C_InitStructure.I2C_ClockSpeed = BusSpeed; //总线速度设置 	
	I2C_Init(I2C1,&I2C_InitStructure);
	I2C_Cmd(I2C1,ENABLE);//开启I2C					
}

void I2C_SAND_BUFFER(u8 SlaveAddr,u8 WriteAddr,u8* pBuffer,u16 NumByteToWrite){ //I2C发送数据串(器件地址,寄存器,内部地址,数量)
	I2C_GenerateSTART(I2C1,ENABLE);//产生起始位
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Transmitter);//发送器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除EV6
	I2C_SendData(I2C1,WriteAddr); //内部功能地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄存器非空,数据寄存器已空,产生EV8,发送数据到DR既清除该事件
	while(NumByteToWrite--){ //循环发送数据	
		I2C_SendData(I2C1,*pBuffer); //发送数据
		pBuffer++; //数据指针移位
		while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除EV8
	}
	I2C_GenerateSTOP(I2C1,ENABLE);//产生停止信号
}
void I2C_SAND_BYTE(u8 SlaveAddr,u8 writeAddr,u8 pBuffer){ //I2C发送一个字节(从地址,内部地址,内容)
	I2C_GenerateSTART(I2C1,ENABLE); //发送开始信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //等待完成	
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //发送从器件地址及状态(写入)
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待完成	
	I2C_SendData(I2C1,writeAddr); //发送从器件内部寄存器地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	
	I2C_SendData(I2C1,pBuffer); //发送要写入的内容
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	
	I2C_GenerateSTOP(I2C1,ENABLE); //发送结束信号
}
void I2C_READ_BUFFER(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead){ //I2C读取数据串(器件地址,寄存器,内部地址,数量)
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	I2C_GenerateSTART(I2C1,ENABLE);//开启信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));	//清除 EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //写入器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6
	I2C_Cmd(I2C1,ENABLE);
	I2C_SendData(I2C1,readAddr); //发送读的地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //清除 EV8
	I2C_GenerateSTART(I2C1,ENABLE); //开启信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); //将器件地址传出,主机为读
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //清除EV6
	while(NumByteToRead){
		if(NumByteToRead == 1){ //只剩下最后一个数据时进入 if 语句
			I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
			I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位
		}
		if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)){ //读取数据
			*pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBuffer
			pBuffer++; //指针移位
			NumByteToRead--; //字节数减 1 
		}
	}
	I2C_AcknowledgeConfig(I2C1,ENABLE);
}
u8 I2C_READ_BYTE(u8 SlaveAddr,u8 readAddr){ //I2C读取一个字节
	u8 a;
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); 
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
	I2C_Cmd(I2C1,ENABLE);
	I2C_SendData(I2C1,readAddr);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Receiver);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
	I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
	I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位
	a = I2C_ReceiveData(I2C1);
	return a;
}

继电器

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#include "relay.h"

void RELAY_Init(void){ //继电器的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设时钟使能      
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//启动AFIO重映射功能时钟    
    GPIO_InitStructure.GPIO_Pin = RELAY1 | RELAY2; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(RELAYPORT, &GPIO_InitStructure);
	//必须将禁用JTAG功能才能做GPIO使用
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);// 改变指定管脚的映射,完全禁用JTAG+SW-DP
	GPIO_ResetBits(RELAYPORT,RELAY1 | RELAY2); //都为低电平(0) 初始为关继电器							
}

void RELAY_1(u8 c){ //继电器的控制程序(c=0继电器放开,c=1继电器吸合)
	GPIO_WriteBit(RELAYPORT,RELAY1,(BitAction)(c));//通过参数值写入接口
}
void RELAY_2(u8 c){ //继电器的控制程序(c=0继电器放开,c=1继电器吸合)
	GPIO_WriteBit(RELAYPORT,RELAY2,(BitAction)(c));//通过参数值写入接口
}

步进电机

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 四步
    在这里插入图片描述

  • 八步
    在这里插入图片描述
    在这里插入图片描述

#include "step_motor.h"

u8 STEP; 


void STEP_MOTOR_Init(void){ //接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设GPIO时钟使能      
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//启动AFIO重映射功能时钟    
    GPIO_InitStructure.GPIO_Pin = STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D; //选择端口                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(STEP_MOTOR_PORT, &GPIO_InitStructure);
	//必须将禁用JTAG功能才能做GPIO使用
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);// 改变指定管脚的映射,完全禁用JTAG+SW-DP	
	STEP_MOTOR_OFF(); //初始状态是断电状态 			
}

void STEP_MOTOR_OFF (void){//电机断电
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//各接口置0
}

void STEP_MOTOR_8A (u8 a,u16 speed){//电机单步8拍
	switch (a){
		case 0:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1
			break;
		case 1:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1
			break;
		case 2:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1
			break;
		case 3:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1
			break;
		case 4:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1
			break;
		case 5:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1
			break;
		case 6:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1
			break;
		case 7:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1
			break;
		default:
			break;
	}
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}

void STEP_MOTOR_NUM (u8 RL,u16 num,u8 speed){//电机按步数运行
	u16 i;
	for(i=0;i<num;i++){	
		if(RL==1){ //当RL=1右转,RL=0左转
			STEP++;
			if(STEP>7)STEP=0;
		}else{
			if(STEP==0)STEP=8;
			STEP--;
		}
		STEP_MOTOR_8A(STEP,speed);
	}
}

void STEP_MOTOR_LOOP (u8 RL,u8 LOOP,u8 speed){//电机按圈数运行
	STEP_MOTOR_NUM(RL,LOOP*4076,speed); 
}

模拟摇杆

在这里插入图片描述
#include “JoyStick.h”

void JoyStick_Init(void){ //微动开关的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
    GPIO_InitStructure.GPIO_Pin = JoyStick_KEY; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(JoyStickPORT,&GPIO_InitStructure);			
}

ADC+DMA

在这里插入图片描述
在这里插入图片描述
所以adc1-DR寄存器地址ox4001244C
在这里插入图片描述

#include "adc.h"

vu16 ADC_DMA_IN[2]; //ADC数值存放的变量

void ADC_DMA_Init(void){ //DMA初始化设置
	DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体
	DMA_DeInit(DMA1_Channel1);//复位DMA通道1
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_Address
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址
	DMA_InitStructure.DMA_BufferSize = 2;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 当前存储器地址:Disable不变,Enable递增(用于多通道采集)
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1
	DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}
void ADC_GPIO_Init(void){ //GPIO初始化设置
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟(用于ADC的数据传送)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC1时钟
    GPIO_InitStructure.GPIO_Pin = ADC_CH4 | ADC_CH5; //!!!选择端口                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //选择IO接口工作方式       
	GPIO_Init(ADCPORT, &GPIO_InitStructure);			
}
void ADC_Configuration(void){ //初始化设置
	ADC_InitTypeDef ADC_InitStructure;//定义ADC初始化结构体变量
	ADC_GPIO_Init();//GPIO初始化设置
	ADC_DMA_Init();//DMA初始化设置
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有软件控制转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 2;//!!!顺序进行规则转换的ADC通道的数目(根据ADC采集通道数量修改)
	ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC1,ADC通道x,规则采样顺序值为y,采样时间为28周期		 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期

	ADC_DMACmd(ADC1, ENABLE);// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
	ADC_Cmd(ADC1, ENABLE);//使能ADC1
	ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成
	ADC_StartCalibration(ADC1);//开始ADC1校准
	while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件开始转换
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值