摩尔芯闻 > 行业新闻 > 半导体 > AVR单片机教程——示波器

AVR单片机教程——示波器

eefocus ·2020-09-08 00:00·电子工程世界
阅读:1694

在用DAC做了一个稍大的项目之后,我们来拿ADC开开刀。在本讲中,我们将了解0.96寸OLED屏,移植著名的U8g2库到我们的开发板上,学习在屏幕上画直线的算法,编写一个示波器程序,使用EEPROM加入人性化功能,最后利用示波器观察555定时器、放大电路、波形变换电路的各种波形。


OLED屏

我们使用的是0.96寸OLED屏,它由128*64个像素点构成,上16行为蓝色,下48行为黄色,两部分之间有大约两像素的空隙。虽然有两种颜色,但每个像素点都只能发出一种颜色的光,因此这块OLED屏算作单色屏。


可以插在开发板上的是显示屏模块,它由裸屏和PCB等组成,裸屏通过30 pin的排线焊接在PCB的反面。

在裸屏的内部有一块控制与驱动芯片,型号为SSD1315,与SSD1306兼容,它是外部与像素点之间的桥梁。SSD1315有200多个引脚,其中128个segment和64个common以动态扫描的方式驱动每一个像素点,这就是它为什么必须做在裸屏的内部。除了这些以外,它还有许多电源和控制引脚:

  • VDD是控制逻辑的供电,范围为1.65V到3.5V;VCC是OLED面板驱动电压,范围为7.5V到16.5V;VBAT是内部电荷泵的供电,范围为3.0V到4.5V,VBAT经电荷泵升压后提供给VCC,此时VCC需要连接电容到地;电荷泵需要两个外部电容,连接在C1P和C1N、C2P和C2N之间;VCOMH是一个内部电压,需要连接电容到地;VSS、VLSS、BGGND、LS都接地;IREF用于控制参考电压。

  • BS[2:0]用于选择接口模式,支持4线SPI、3线SPI、I²C、8位8080和6800;E(RD)和R/W(WR)在并行模式下使用;D[7:0]为数据,在SPI模式下,D0是时钟信号,D1是输入数据信号,D2连接D1或地;在I²C模式下,D0是时钟信号,D1和D2一起是数据信号;RES是复位信号;CS是片选信号;D/C用于指定输入是数据还是指令,在I²C模式下为地址选择,在3线SPI模式下保持低电平;FR、CL、CLS都是时钟信号。

看起来很复杂,但事实上有些信号根本不用管,因为裸屏只有30个引脚,去掉了BS2、E(RD)、R/W(WR)、D[7:3]、FR、CL、CLS,这些都是不常用的(除了FR帧同步信号,我觉得有点用)。剩下的你也许需要学,但不是现在,而是在你的项目需要用裸屏的时候,因为那块蓝色的PCB把这些都处理好了,只留下了7个引脚:GND、VCC、D0、D1、RES、DC、CS。可用的通信模式只有4线SPI、3线SPI和I²C,但已经相当丰富了,可以通过模块背面的电阻来选择,出厂时是4线SPI,也就是我们将要使用的模式。有的模块只支持I²C模式,也就只需要4个引脚了。


在4线SPI模式下,D0连接单片机USART1的XCK1,D1连接TXD1,CS连接PB2,这些是标准SPI的信号;RES连接PB0,D/C连接PB1。芯片在时钟上升沿采样数据信号,SPI模式0或3都可以使用。接下来我们来看总线上的数据。


当D/C为低时,总线上传输的是控制指令;当D/C为高时,总线上传输的是显示数据。64行被分为8页,芯片内部有1024字节的显存,每一字节对应一页中的一列,也就是纵向8个像素:

显存支持页面、水平、垂直三种寻址模式,伴随有一个指针,每写入一字节数据,指针就以某种形式增长,类似于我们在C中写的*ptr++:

芯片支持很多指令,它们的长度由第一个字节决定,有各自的格式,大致可以分为以下几类:

  • 显存:寻址模式、行列地址、页面地址;

  • 显示:起始行、显示行数、对比度、各种remap、全亮、反转、睡眠、偏移;

  • 电源:IREF电流大小、VCOMH电压阈值、电荷泵开关;

  • 时钟:时钟频率、时钟分频、预充电周期;

  • 滚动:水平滚动、水平垂直滚动、滚动区域、启用禁用滚动;

  • 高级:淡化、闪烁、放大。

对照着datasheet,我们来写几个指令,让屏幕亮起来。

#include

#include

#include


void spi_init()

{

UCSR1B =    1 << TXEN1;

UCSR1C = 0b11 << UMSEL10

#define              UDORD1 2

|    0 << UDORD1

#define              UCPHA1 1

|    0 << UCPHA1

|    0 << UCPOL1;

set_bit(DDRD, 3);

set_bit(DDRD, 4);

}


void spi_send(uint8_t _data)

{

UDR1 = _data;

while (!read_bit(UCSR1A, TXC1))

;

set_bit(UCSR1A, TXC1);

}


void oled_init()

{

spi_init();

set_bit(DDRB, 0);  // RES

set_bit(DDRB, 1);  // DC

set_bit(DDRB, 2);  // CS

set_bit(PORTB, 2); // CS  high

set_bit(PORTB, 0); // RES high

}


void oled_control(uint8_t _size, ...)

{

reset_bit(PORTB, 1); // DC low

reset_bit(PORTB, 2); // CS low

va_list args;

va_start(args, _size);

for (uint8_t i = 0; i != _size; ++i)

spi_send(va_arg(args, int));

va_end(args);

set_bit(PORTB, 2);   // CS high

}


void oled_data(uint16_t _size, const uint8_t* _data)

{

set_bit(PORTB, 1);   // DC high

reset_bit(PORTB, 2); // CS low

for (const uint8_t* end = _data + _size; _data != end; ++_data)

spi_send(*_data);

set_bit(PORTB, 2);   // CS high

}


int main(void)

{

oled_init();

oled_control(2, 0x8D, 0x95); // enable charge pump

oled_control(1, 0xA1);       // segment remap

oled_control(1, 0xC8);       // common remap

oled_control(1, 0xAF);       // display on

uint8_t data[128];

for (uint8_t i = 0; i != 128; ++i)

data[i] = i;

for (uint8_t i = 0; i != 8; ++i)

{

oled_control(1, 0xB0 + i);

oled_data(128, data);

}

while (1)

;

}


先来看指令:

  • 0x8D, 0x95启用内置电荷泵,将输出电压设置为9.0V;

  • 0xA1和0xC8分别设置segment和common的remap,因为另一份datasheet中指明,显示屏的第一行连接Common 62,第一列连接Segment 127;

  • 0xAF开启显示,显示是默认关闭的,需要手动开启;

  • 0xB0到0xB7设置页面寻址模式下的页面地址,这是默认的寻址模式,我们在循环中先设置地址,再发送128字节的数据,内容是0到127,循环8次,把每一页都填满。

画出的是一个美丽的分形图:

再来看oled_control这个函数。参数列表的最后是...,表示可变参数。在函数调用时,匹配到...的参数需要用 中的工具取用:

  • va_list是一个类型,创建一个这个类型的变量,表示可变参数列表;

  • va_start是一个宏,第一个参数为va_list变量,第二个为可变参数的数量;

  • va_arg取出可变参数列表中的下一个变量,类型由第二个参数指定;

  • va_end在使用完可变参数后做一些清理工作。

需要提醒的是,编译器无法检查标称的参数数量和类型与实际的是否符合。


移植U8g2库

U8g2是一个著名的单色显示屏驱动与图形库。“U”是universal,支持众多显示驱动芯片;“8”是8-bit,单片机与芯片以字节为单位通信;“g”是graphics,有绘制各种图形的函数;“2”是第二代。


文首的资料中包含了U8g2仓库的全部资料,下载于2020年2月9日,你也可以从GitHub上下载。C源代码在文件夹csrc中,包含头文件与实现。为了在我们的项目中包含这些文件,我们在Atmel Studio的Solution Explorer中对项目右键,点击Add→New Folder,命名为“u8g2”,然后右键它并点击Add→Existing Item,选择csrc中的文件,它们就会被拷贝到项目目录下,在代码中可以通过`#include 引用头文件。


U8g2的使用很简单,Wiki告诉我们,要首先创建u8g2_t类型的对象,随后每个函数的第一个参数都是它的指针。先根据显示屏的芯片型号选择合适的设置函数,初始化后就有那么多函数可以使用了。


U8g2没有提供SSD1315的驱动,但由于SSD1315与SSD1306兼容,我们可以选择u8g2_Setup_ssd1306_128x64_noname_f函数。后缀为_f的函数在RAM中设置了整个缓存,共128 * 64 / 8 = 1KB,这样用起来比较方便。


移植的核心就在于初始化时注册的两个回调函数。根据Wiki,我们要提供的两个函数的模板为:

uint8_t u8x8_comm_callback(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

{

switch (msg)

{

case U8X8_MSG_BYTE_INIT:

break;

case U8X8_MSG_BYTE_SET_DC:

break;

case U8X8_MSG_BYTE_START_TRANSFER:

break;

case U8X8_MSG_BYTE_SEND:

break;

case U8X8_MSG_BYTE_END_TRANSFER:

break;

default:

return 0;

}

return 1;

}


uint8_t u8x8_gpio_delay_callback(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

{

switch (msg)

{

case U8X8_MSG_GPIO_AND_DELAY_INIT:

break;

case U8X8_MSG_DELAY_NANO:

break;

case U8X8_MSG_DELAY_100NANO:

break;

case U8X8_MSG_DELAY_10MICRO:

关键字: AVR 单片机 示波器 编辑:什么鱼 引用地址: http://news.eeworld.com.cn/mcu/ic509378.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。
分享到:
微信 新浪微博 QQ空间 LinkedIn

上一篇:AVR单片机教程——DAC

下一篇:AVR单片机教程——走向高层

打开摩尔直播,更多新闻内容
半导体大咖直播分享高清观看
立即下载