这篇文章以上一篇文章为基础,着重讲如何将ADC采样得到的值显示在LCD12864上面,关于如何点亮LCD屏幕,以及实物硬件连线图、原理,请参考之前一篇文章。加上上一篇文章,这算是一个十分迷你的项目了~还有一个比较常用且重要的知识点就是sprintf()函数的使用——在stm32的编程中如何将浮点小数转换成字符串打印。废话不多说,放码吧!
1.lcd.h部分
#ifndef _LCD_H
#define _LCD_H
#include "sys.h"
#include "delay.h"
#define RS PCout(0) //发数据/命令控制口
#define RW PCout(1) //读/写控制口,写比较常用
#define EN PCout(2) //使能口
#define LINE1 0x80 //第一行起始地址,下同
#define LINE2 0x90
#define LINE3 0x88
#define LINE4 0x98
//以下是点亮你的屏幕必要的六个函数,不能再少了,想添加其他功能就得再加其他函数
void IO_Init(void); //必要的IO口初始化
void CheckBusy(void); //检查忙/闲状态
void LCD_wdat(u8 dat); //写数据
void LCD_wcmd(u8 com); //写命令
void LCD_Init(void); //LCD初始化
void LCD_Wmessage(u8* message,u8 address); //向屏幕里写入字串
#endif
2.lcd.c部分
#include "lcd.h"
#include "sys.h"
#include "delay.h"
u16 temp;
void IO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);
//想实现控制,激活控制口(PC0,PC1,PC2)必不可少
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
//想发送数据,激活数据口(PA0~PA7)必不可少
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
//LCD初始化
void LCD_Init()
{
LCD_wcmd(0x30);//功能设定:基本指令集
delay_ms(5);
LCD_wcmd(0x0C);//显示开,关光标
delay_ms(5);
LCD_wcmd(0x01);//清除显示
}
//忙判断
void CheckBusy(void)
{
u8 status;
RS=0;
RW=1; //读出数据,RW=1
GPIOA->ODR = 0xFF;
do
{
EN = 1;
delay_ms(5);
status = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7);//接收BF位,判断是否忙
}while(status & 0x80);
EN=0;
}
//LCD写命令
void LCD_wcmd(u8 cmd)
{
CheckBusy();
RS=0;
RW=0;
delay_ms(5);
temp=(temp&0xff00)|cmd;//temp的低八位清零后将cmd放进去
GPIO_Write(GPIOA,temp);//通过GPIO_Write()函数将数据发到A端口(也就是LCD的数据口)
EN=1;//使能位开启
delay_ms(10);//10ms应该能发送完了
EN=0;//使能位关闭
}
//LCD写数据
void LCD_wdat(u8 dat)
{
CheckBusy();
RS=1;
RW=0;
delay_ms(5);
temp=(temp&0xff00)|dat; //temp的低八位清零后将dat放进去
GPIO_Write(GPIOA,temp);//通过GPIO_Write()函数将数据发到A端口(也就是LCD的数据口)
EN=1;//使能位开启
delay_ms(10);//10ms应该能发送完了
EN=0;//使能位关闭
}
//向LCD12864中写入一行数据(因为你不可能每次只发送一字节数据)
void LCD_Wmessage(u8* message,u8 address)
{
LCD_wcmd(address);//要显示的位置,你想让内容显示在LCD的哪一行,就把该行的起始地址通过写命令的方式发送出去,很神奇,有木有
while(*message>0)//这个判断很关键,判断你的内容有没有发完
{
LCD_wdat(*message); //内核还是发字节函数
message++; //指针挺好用的。。
}
}
3.adc.h和lcd.c部分随便参照原子哥的一个ADC例程即可,我也是直接拿过来用的。。采样的是战舰V3板载的光敏电阻。
adc.h文件:
#ifndef __ADC_H
#define __ADC_H
#include "sys.h"
#include "stm32f10x.h"
void Adc_Init(void);
u16 Get_Adc(u8 ch);
u16 Get_Adc_Average(u8 ch,u8 times);
#endif
4.adc.c
#include "adc.h"
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void Adc_Init()
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF|RCC_APB2Periph_ADC3, ENABLE );
//RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHZ(不分频好像也没事,现象和分频现象一致。好像有个默认的分频)
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
GPIO_Init(GPIOF,&GPIO_InitStructure);
ADC_DeInit(ADC3); //复位ADC1
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel=1;
ADC_InitStructure.ADC_ScanConvMode=DISABLE;
ADC_Init(ADC3,&ADC_InitStructure);
ADC_Cmd(ADC3, ENABLE); //使能指定的ADC3
ADC_ResetCalibration(ADC3); //重置指定的ADC3的复位寄存器
while(ADC_GetResetCalibrationStatus(ADC3)); //获取ADC1重置校准寄存器的状态,设置状态则等待
ADC_StartCalibration(ADC3);
while(ADC_GetCalibrationStatus(ADC3)); //获取指定ADC1的校准程序,设置状态则等待
}
u16 Get_Adc(u8 ch)
{
ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC3, ENABLE); //使能指定的ADC3的软件转换启动功能
while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC3); //返回最近一次ADC3规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t
5.最后就是主函数部分了。前面的代码比较简单,主函数最重要的一个作用就是对接收到的数据进行处理。其中一个就是如何把ADC得到的数字以一个字符串的方式在LCD屏幕上打印出来。这花了我一些时间去找解决方法(因为水平比较烂,所以很多都得找着学~),后来找到了解决方法,就是开头说的sprintf()函数。先把代码发出来:
#include "stm32f10x.h"
#include "sys.h"
#include "lcd.h"
#include "delay.h"
#include "usart.h"
#include "adc.h"
/************************
一行16个字节,一个汉字2字节,一个字母1字节,每个汉字只能在偶字节处起
************************/
int main(void)
{
u8 dis1[]={"µ±Ç°µç×èÖµ£º"};
u16 adcx;
float temp;
u8 aa[10];
uart_init(115200);
Adc_Init();
delay_init();
IO_Init();
LCD_Init();
while(1)
{
adcx=Get_Adc_Average(ADC_Channel_6,10);
temp=(float)adcx*(3.3/4096);
//printf("adcx: %5.3f",temp);//向串口打印验证用
sprintf(aa,"%5.3f",temp);
LCD_Wmessage(dis1,LINE1);
LCD_Wmessage(aa,LINE2+2);
}
}
可以看到,sprintf()函数一共有三个参数,第一个参数是我们要的(字符串类型),第二个为格式化输出的形式,第三个是待处理的数据(浮点型数据)。因为LCD最终接受的要打印的数据为字符串,所以我们AD采样得到的浮点数据不可以直接发送给LCD_Wmessage()函数,必须经过这样一次处理才能将其打印(一时没想起其他的方法,欢迎有其他方法的伙伴交流进步)。
最后呈现的结果如下:
(没开手电筒时)
(打开手电筒后)
完。欢迎大家一起交流,共同进步。有需要完整工程文件的加我QQ(在上篇文章结尾处放出),请务必注明自己所在的学校和机构,免积分私发。