新手使用keil和proteus联合仿真一个太阳能智能逐日系统,keil中程序正常无错误,但proteus中大多数引脚为灰色且无法实现功能:pcf8591采集的数据显示在lcd1上,按钮B1切换电机的自动与手动旋转,按钮B2和B3控制电机正反转
main.C的代码:
#include "stm32f10x.h"
#include <stdio.h>
#include "sys.h"
#include "lcd.h"
#include "i2c.h"
#include "delay.h"
// 定义按键和电机引脚
#define KEY_AUTO GPIO_Pin_6 // B1
#define KEY_FWD GPIO_Pin_7 // B2
#define KEY_REV GPIO_Pin_8 // B3
static uint8_t auto_mode = 0; // 0:手动模式, 1:自动模式
// 按键扫描函数
uint8_t Key_Scan(void)
{
static uint8_t key_up = 1;
if(key_up && (!GPIO_ReadInputDataBit(GPIOB, KEY_AUTO) ||
!GPIO_ReadInputDataBit(GPIOB, KEY_FWD) ||
!GPIO_ReadInputDataBit(GPIOB, KEY_REV)))
{
Delay_ms(20); // 消抖
key_up = 0;
if(!GPIO_ReadInputDataBit(GPIOB, KEY_AUTO)) return 1;
if(!GPIO_ReadInputDataBit(GPIOB, KEY_FWD)) return 2;
if(!GPIO_ReadInputDataBit(GPIOB, KEY_REV)) return 3;
}
else if(GPIO_ReadInputDataBit(GPIOB, KEY_AUTO) &&
GPIO_ReadInputDataBit(GPIOB, KEY_FWD) &&
GPIO_ReadInputDataBit(GPIOB, KEY_REV))
{
key_up = 1;
}
return 0;
}
// 显示函数
void Display_Data(uint8_t ldr1, uint8_t ldr2)
{
char buffer[16];
LCD_WriteCmd(0x80); // 第一行
sprintf(buffer, "LDR1:%3d %s", ldr1, auto_mode ? "AUTO" : "MANU");
LCD_DisplayString(buffer);
LCD_WriteCmd(0xC0); // 第二行
sprintf(buffer, "LDR2:%3d", ldr2);
LCD_DisplayString(buffer);
}
int main(void)
{
uint8_t ldr1_value, ldr2_value;
uint8_t key;
// 系统初始化
SystemInit(); // STM32系统初始化
System_Init(); // 使能GPIO和I2C时钟
Delay_init(); // 初始化延时功能
// 等待系统稳定
Delay_ms(100);
// 外设初始化
LCD_Init(); // 初始化LCD
I2C_Configuration(); // 初始化I2C和电机控制引脚
// 清屏并显示初始界面
LCD_Clear(); // 清屏
Display_Data(0, 0); // 显示初始值
while(1)
{
// 读取按键
key = Key_Scan();
if(key == 1)
{
auto_mode = !auto_mode; // 切换模式
Delay_ms(10);
}
// 读取光敏电阻值
ldr1_value = PCF8591_ReadAD(0); // 读取LDR1
Delay_ms(10);
ldr2_value = PCF8591_ReadAD(1); // 读取LDR2
Delay_ms(10);
// 显示数据
Display_Data(ldr1_value, ldr2_value);
// 根据模式控制电机
if(auto_mode)
{
// 自动模式:根据光敏电阻值控制
if(ldr1_value > ldr2_value + 20)
{
Motor_Forward();
}
else if(ldr2_value > ldr1_value + 20)
{
Motor_Reverse();
}
}
else
{
// 手动模式:按键控制
if(key == 2) Motor_Forward();
if(key == 3) Motor_Reverse();
}
Delay_ms(10);
}
}
lcd.C:
#include "lcd.h"
#include "delay.h"
// LCD初始化
void LCD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
// 配置LCD数据引脚 PA0-PA7
GPIO_InitStructure.GPIO_Pin = 0xFF; // PA0-PA7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置LCD控制引脚 PB0-PB2
GPIO_InitStructure.GPIO_Pin = LCD_RS | LCD_RW | LCD_EN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// LCD初始化过程
Delay_ms(15);
LCD_WriteCmd(0x38); // 功能设置:8位数据接口,2行显示,5x7点阵
Delay_ms(5);
LCD_WriteCmd(0x38);
Delay_ms(5);
LCD_WriteCmd(0x38);
LCD_WriteCmd(LCD_DISPLAY_ON); // 显示开,光标关,不闪烁
LCD_WriteCmd(LCD_CLEAR); // 清屏
Delay_ms(2);
LCD_WriteCmd(LCD_MODE); // 设置输入模式
}
// 写命令
void LCD_WriteCmd(uint8_t cmd)
{
GPIO_ResetBits(GPIOB, LCD_RS); // RS=0,命令
GPIO_ResetBits(GPIOB, LCD_RW); // RW=0,写
GPIOA->ODR = cmd; // 写入命令
GPIO_SetBits(GPIOB, LCD_EN); // EN=1
Delay_us(1); // 延时
GPIO_ResetBits(GPIOB, LCD_EN); // EN=0
Delay_ms(2); // 等待命令执行完成
}
// 写数据
void LCD_WriteData(uint8_t data)
{
GPIO_SetBits(GPIOB, LCD_RS); // RS=1,数据
GPIO_ResetBits(GPIOB, LCD_RW); // RW=0,写
GPIOA->ODR = data; // 写入数据
GPIO_SetBits(GPIOB, LCD_EN); // EN=1
Delay_us(1); // 延时
GPIO_ResetBits(GPIOB, LCD_EN); // EN=0
Delay_ms(2); // 等待数据写入完成
}
// 显示字符串
void LCD_DisplayString(char *str)
{
while(*str)
{
LCD_WriteData(*str++);
}
}
// 清屏
void LCD_Clear(void)
{
LCD_WriteCmd(LCD_CLEAR);
Delay_ms(2);
}
// 设置光标位置
void LCD_SetCursor(uint8_t x, uint8_t y)
{
uint8_t addr;
if(y == 0)
addr = 0x80 + x;
else
addr = 0xC0 + x;
LCD_WriteCmd(addr);
}
lcd.H:
#ifndef __LCD_H
#define __LCD_H
#include "stm32f10x.h"
// LCD引脚定义
#define LCD_RS GPIO_Pin_0 // PB0
#define LCD_RW GPIO_Pin_1 // PB1
#define LCD_EN GPIO_Pin_2 // PB2
// LCD命令
#define LCD_CLEAR 0x01 // 清屏
#define LCD_HOME 0x02 // 光标归位
#define LCD_MODE 0x06 // 光标右移,显示不移动
#define LCD_DISPLAY_ON 0x0C // 显示开,光标关,不闪烁
#define LCD_FUNC_SET 0x38 // 8位数据接口,2行显示,5x7点阵
// 函数声明
void LCD_Init(void);
void LCD_WriteCmd(uint8_t cmd);
void LCD_WriteData(uint8_t data);
void LCD_DisplayString(char *str);
void LCD_Clear(void);
void LCD_SetCursor(uint8_t x, uint8_t y);
#endif
delay.C:
#include "delay.h"
#include "stm32f10x.h"
static uint8_t fac_us = 0;
static uint16_t fac_ms = 0;
// 初始化延时函数
void Delay_init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us = SystemCoreClock / 8000000;
fac_ms = (uint16_t)fac_us * 1000;
}
// 微秒延时
void Delay_us(uint32_t us)
{
uint32_t temp;
SysTick->LOAD = us * fac_us;
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
do
{
temp = SysTick->CTRL;
}while((temp & 0x01) && !(temp & (1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0X00;
}
// 毫秒延时
void Delay_ms(uint32_t ms)
{
uint32_t temp;
SysTick->LOAD = (uint32_t)ms * fac_ms;
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
do
{
temp = SysTick->CTRL;
}while((temp & 0x01) && !(temp & (1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0X00;
}
delay.H:
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void Delay_init(void);
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
#endif
i2c.C:
#include "i2c.h"
#include "delay.h"
// I2C引脚定义
#define SCL_PIN GPIO_Pin_3 // PB3
#define SDA_PIN GPIO_Pin_4 // PB4
#define MOTOR_A GPIO_Pin_10 // PB10
#define MOTOR_B GPIO_Pin_11 // PB11
#define MOTOR_C GPIO_Pin_12 // PB12
#define MOTOR_D GPIO_Pin_13 // PB13
// I2C操作函数
#define SDA_H GPIO_SetBits(GPIOB, SDA_PIN)
#define SDA_L GPIO_ResetBits(GPIOB, SDA_PIN)
#define SCL_H GPIO_SetBits(GPIOB, SCL_PIN)
#define SCL_L GPIO_ResetBits(GPIOB, SCL_PIN)
#define SDA_READ GPIO_ReadInputDataBit(GPIOB, SDA_PIN)
// I2C基本操作函数
void I2C_Start(void)
{
SDA_H;
SCL_H;
Delay_us(4);
SDA_L;
Delay_us(4);
SCL_L;
}
void I2C_Stop(void)
{
SDA_L;
SCL_H;
Delay_us(4);
SDA_H;
Delay_us(4);
}
uint8_t I2C_WaitAck(void)
{
uint8_t ucErrTime=0;
SDA_H;
Delay_us(1);
SCL_H;
Delay_us(1);
while(SDA_READ)
{
ucErrTime++;
if(ucErrTime>250)
{
I2C_Stop();
return 1;
}
}
SCL_L;
return 0;
}
void I2C_Ack(void)
{
SCL_L;
SDA_L;
Delay_us(2);
SCL_H;
Delay_us(2);
SCL_L;
}
void I2C_NAck(void)
{
SCL_L;
SDA_H;
Delay_us(2);
SCL_H;
Delay_us(2);
SCL_L;
}
void I2C_SendByte(uint8_t txd)
{
uint8_t t;
SCL_L;
for(t=0;t<8;t++)
{
if((txd&0x80)>>7)
SDA_H;
else
SDA_L;
txd<<=1;
Delay_us(2);
SCL_H;
Delay_us(2);
SCL_L;
Delay_us(2);
}
}
uint8_t I2C_ReadByte(unsigned char ack)
{
unsigned char i,receive=0;
for(i=0;i<8;i++ )
{
SCL_H;
Delay_us(2);
receive<<=1;
if(SDA_READ)receive++;
SCL_L;
Delay_us(1);
}
if (!ack)
I2C_NAck();
else
I2C_Ack();
return receive;
}
void I2C_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 配置I2C引脚
GPIO_InitStructure.GPIO_Pin = SCL_PIN | SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置电机控制引脚
GPIO_InitStructure.GPIO_Pin = MOTOR_A | MOTOR_B | MOTOR_C | MOTOR_D;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 设置初始电平
GPIO_SetBits(GPIOB, SCL_PIN | SDA_PIN);
}
// 电机控制函数
void Motor_Forward(void)
{
static const uint8_t step_sequence[] = {0x01, 0x02, 0x04, 0x08};
static uint8_t step_index = 0;
uint8_t output = step_sequence[step_index];
GPIO_Write(GPIOB, (uint16_t)((GPIO_ReadOutputData(GPIOB) & 0xF3FF) |
((output & 0x0F) << 10)));
step_index = (step_index + 1) % 4;
Delay_ms(5);
}
void Motor_Reverse(void)
{
static const uint8_t step_sequence[] = {0x08, 0x04, 0x02, 0x01};
static uint8_t step_index = 0;
uint8_t output = step_sequence[step_index];
GPIO_Write(GPIOB, (uint16_t)((GPIO_ReadOutputData(GPIOB) & 0xF3FF) |
((output & 0x0F) << 10)));
step_index = (step_index + 1) % 4;
Delay_ms(5);
}
// PCF8591相关函数
uint8_t PCF8591_ReadAD(uint8_t channel)
{
uint8_t value;
// 发送起始信号
I2C_Start();
// 发送设备地址和写命令
I2C_SendByte(PCF8591_ADDR);
I2C_WaitAck();
// 发送控制字节
I2C_SendByte(0x40 | channel);
I2C_WaitAck();
// 重新开始
I2C_Start();
// 发送设备地址和读命令
I2C_SendByte(PCF8591_ADDR | 0x01);
I2C_WaitAck();
// 读取数据
value = I2C_ReadByte(0);
// 发送停止信号
I2C_Stop();
return value;
}
i2c.H:
#ifndef __I2C_H
#define __I2C_H
#include "stm32f10x.h"
#define PCF8591_ADDR 0x90 // PCF8591地址
// I2C基本操作函数
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(uint8_t txd);
uint8_t I2C_ReadByte(unsigned char ack);
uint8_t I2C_WaitAck(void);
void I2C_Ack(void);
void I2C_NAck(void);
// 外设控制函数
void I2C_Configuration(void);
uint8_t PCF8591_ReadAD(uint8_t channel);
void Motor_Forward(void);
void Motor_Reverse(void);
#endif
sys.C:
#include "sys.h"
void System_Init(void)
{
// 使能APB2外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
RCC_APB2Periph_GPIOB |
RCC_APB2Periph_AFIO, ENABLE);
// 配置系统时钟
RCC_HSEConfig(RCC_HSE_ON); // 使能外部高速晶振
while(RCC_WaitForHSEStartUp() != SUCCESS); // 等待HSE就绪
// 配置PLL
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // PLLCLK = HSE * 9 = 72MHz
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // 等待PLL就绪
// 配置系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // 选择PLL作为系统时钟
while(RCC_GetSYSCLKSource() != 0x08); // 等待PLL成为系统时钟源
// 配置AHB、APB1、APB2分频
RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB = SYSCLK
RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 = HCLK/2
RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 = HCLK
}
sys.H:
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
// 系统初始化函数
void System_Init(void);
// 位带操作相关宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
// GPIO地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#endif
反复查看引脚定义,系统初始化,外接上拉电阻均无用,希望有大佬能帮帮忙看看哪里出了错误该怎么修改