一:原理图
二、STM32CUBEMAX配置
1.时钟配置 :输入外部高速时钟为24MHZ
2.ADC配置 :PB15和PB12位ADC输入
3.ADC直接采样
将IN11通道设置为single-ended,ContinuousConvMode = DISABLE;
DMAContinuousRequests = DISABLE; 其他配置不变
4-代码部分,直接采样
在主函数中直接调用getADC1()和getADC2()函数就可以可以,就是有点浪费资源
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint16_t getADC1(void) //得到ADC1的值
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc1);
adc = HAL_ADC_GetValue(&hadc1);
return adc;
}
uint16_t getADC2(void) //得到ADC2的值
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc2); //调要HAL_ADC_Start()函数
adc = HAL_ADC_GetValue(&hadc2); //调要HAL_ADC_GetValue()函数获得adc的值
return adc;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_ADC1_Init();
MX_ADC2_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_Clear(White);
LCD_SetBackColor(White);
LCD_SetTextColor(Black);
LCD_Clear(White);
LCD_DisplayStringLine(Line1 ,(unsigned char *)" hello dma. ");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//3.3为总的电压值 2的12次方等于4096 用于对电压值分成一份
sprintf((char*)Lcd_Disp_String, " RES38_vol: %6.3f ",getADC1()*(3.3/4096));
LCD_DisplayStringLine(Line6, Lcd_Disp_String);
sprintf((char*)Lcd_Disp_String, " RES37_vol: %6.3f ",getADC2()*(3.3/4096));
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
51的学习点点:
一:led和按键
(2-1-led点亮 2-2-led闪烁 2-3-改写延迟函数。)
1.串口定义的解析:移位的知识点,数电的各个进制的转化。
1)P3=0xFE(16进制) ,看起内置一般是8个灯也就是8位。如果写了数字自动转成2进制就会灯乱亮; 即P3_0= 3(不会亮第三个灯);
2)P2=~(0x01<<LEDNum); 这个才会亮相应的灯。
#include <REGX52.H>
#include <INTRINS.H>
/************************************
//点亮led
void main()
{//二进制转化成16进制。16进制是从右往左读
P2=0xFE;//1111 1110点亮第一个led;P2口的8个led灯
while(1)//当括号逻辑符合的时候执行,0停止
{}
}
************************************/
//软件计算生成代码,我们可以根据这个运算范例改成自己用的代码
void Delayms(int shijian) //@12.000MHz
{
while(shijian)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
shijian--;
}
}
void main()
{
while(1)
{
P2=0xFE;
Delayms(2000);
P2=0xFD;
Delayms(500);
}
}
(3-1-按键控制led亮灭 ( 1.就是给高低电平))
#include <REGX52.H>
void main()
{
while(1)
{
if(P3_1==0 || P3_0==0) //如果K1按键或K2按键按下
{
P2_0=0; //LED1输出0,点亮
}
else
{
P2_0=1; //LED1输出1,熄灭
}
}
}
3-2独立按键控制led状态
P2_0=~P2_0;电平取反。
3-3独立按键控制led显示二进制亮灯模式
Delay(20);//消抖延迟
while(P3_1==0);
Delay(20);
2.预编译
1)#ifdef aaa .......#endif 如果没有在前面define aaa 那么其中的内容不会被编译,如果定义了,就会报错。这样可能可以实现程序选择功能。防止重复定义。
(5-2LCD1602液晶屏调试 前三个led和数码管都不能用了。)
2)调试方法,类似于串口
(6-2矩阵键盘密码锁:开始加入基础逻辑实现相应内容。)
3)在main里面写逻辑函数,能不能在相应的.c里面写,然后让main更整洁,但32好像不行,还没找到问题在哪。
二:串口通信:
1.1数据通信就是指单片机与单片机之间或者单片机和其他设备之间的信息交换
而数据通信又分为串行通信和并行通信
并行通信:数据的各位同时进行发送或接收的通信方式。优点是速率高。缺点是需要的传输线多,成本高,只适合近距离的数据通信。
串行通信:一位一位的按顺序的进行发送或接收的通信方式。优点是需要的传输线少,成本低。缺点是传输的速率慢,适合远距离的数据通信。
从上述来看,串行口的功能,就是通过串行口连接的数据传输线大大减少,可以进行远距离的数据通信。
1.2 51单片机串口的结构
串口的结构如下图所示,
通过定时器T1计时,由T1产生溢出率,作为波特率发生器。
两个数据缓冲器,SBUF。分别是发送数据的发送寄存器,读取数据的接收寄存器。
串口写入时,写入的是发送寄存器,即数据向发送寄存器SBUF写入。
向串口读时,读出的是接收寄存器,即数据由接收寄存器SBUF读出。
定时器1产生波特率, 串口一般使用定时器1,模式2,八位自动重装模式,来产生溢出率,从而产生波特率。而且在配置定时器相关的寄存器时不用配置定时器中断,只是使用定时器1来产生波特率的功能。
移位寄存器,在接受控制器的控制下,将输入的数据逐位移入接收SBUF。
串行控制寄存器SCON,SCON的功能是控制串行通信口的工作方式以及工作的状态。
实践代码
#include <REGX52.H>
/**
* @brief 串口初始化
* @param 无
* @retval 无
*/
void Uart_Init() //4800bps@11.0592MHz
{
SCON=0x40; //方式1 REN置1 允许/禁止数据接收的控制位
PCON |= 0x80; //波特率加倍
//配置定时器1 模式二 8位自动重装
TMOD&=0x0F;
TMOD|=0x20; //模式二
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1=0; //不用开启中断 定时器在这里的作用是产生波特率
TR1=1; //启动定时器1
}
/**
* @brief 串口发送一位字节数据
* @param Byte 要发送的字节数据
* @retval 无
*/
void Uart_SendByte(unsigned char Byte)
{
SBUF=Byte; //向缓存器中写入内容
while(TI==0); //发送中断标志位 如果检测到了寄存器的TI位 如果为0 就表示数据未发送完成 反复执行本条语句检测TI位
//如果TI为1 就表示SUBF的数据已经发送完成了 马上执行下条语句将TI位清零
TI=0;
}
#include <REGX52.H>
#include "Delay.h"
#include "Uart.h"
unsigned char Sec;
void main()
{
Uart_Init();
while(1)
{
Sec++;
Uart_SendByte(Sec);
Delay(50);
}
}