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软件开始转换
}