目前共有8篇帖子。 內容轉換:不轉換▼
 
點擊 回復
328 7
【程式】1602液晶中一些非常用功能的使用——讀取屏幕內容以及自定義字元、當前光標位置
一派護法 十九級
1樓 發表于:2016-7-3 10:09
讀取屏幕上寫入的內容以及光標的位置。
一派護法 十九級
2樓 發表于:2016-7-3 10:10
讀取自定義字元的內容:
一派護法 十九級
3樓 發表于:2016-7-3 10:10
【程式】
#include <stm32f10x.h>

#define RS_0 GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_RESET) // 寄存器选择位
#define RS_1 GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET)
#define RW_0 GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_RESET) // 读写选择位
#define RW_1 GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_SET)
#define E_0 GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_RESET) // 使能信号位
#define E_1 GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET)
#define BF GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) // 忙碌标志位

unsigned char userchar[] = {0x10, 0x06, 0x09, 0x08, 0x08, 0x09, 0x06, 0x00};
unsigned char userchar2[] = {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00};

extern uint32_t systick_counter; // 定时器中断中的计时变量

// 延时n毫秒
void delay(uint32_t nms)
{
    systick_counter = nms;
    while (systick_counter > 0);
}

// 把数据端口改为读模式
void LCDBeginRead(void)
{
    GPIO_InitTypeDef init;
    init.GPIO_Mode = GPIO_Mode_IPU;
    init.GPIO_Pin = 0xff00;
    GPIO_Init(GPIOB, &init);
}

// 把数据端口改为写模式
void LCDBeginWrite(void)
{
    GPIO_InitTypeDef init;
    init.GPIO_Mode = GPIO_Mode_Out_PP;
    init.GPIO_Speed = GPIO_Speed_50MHz;
    init.GPIO_Pin = 0xff00;
    GPIO_Init(GPIOB, &init);
}

// 判断液晶模块的忙碌状态
uint8_t LCDBusyTest(void)
{
    uint8_t result;
    LCDBeginRead();
    RS_0;
    RW_1;
    E_1;
    __nop();
    __nop();
    __nop();
    result = BF;
    E_0;
    return result;
}

// 获取当前光标位置
// 虽然时序和检测忙信号完全相同,但是也要检测忙信号
// 避免读到错误的数据
uint8_t LCDGetPos(void)
{
    uint8_t pos;
    while (LCDBusyTest());
    delay(1);
    E_1;
    __nop();
    __nop();
    __nop();
    pos = (GPIO_ReadInputData(GPIOB) >> 8) & 0x7f;
    E_0;
    return pos;
}

// 将模式设置指令或显示地址写入液晶模块
void LCDWriteCmd(uint8_t cmd)
{
    while (LCDBusyTest()); // 如果忙就等待
    LCDBeginWrite();
    RS_0;
    RW_0;
    E_0;
    __nop();
    __nop();
    GPIOB->ODR = (GPIOB->ODR & 0xff) + (cmd << 8);
    __nop();
    __nop();
    __nop();
    __nop();
    E_1;
    __nop();
    __nop();
    __nop();
    __nop();
    E_0;
}

// 设置光标位置(行, 列)
void LCDSetPos(uint8_t row, uint8_t col)
{
    LCDWriteCmd(0x80 + row * 0x40 + col);
}

// 读取一个字符
char LCDReadData(void)
{
    char data;
    while (LCDBusyTest());
    LCDBeginRead();
    RS_1;
    RW_1;
    E_1;
    __nop();
    __nop();
    __nop();
    data = GPIO_ReadInputData(GPIOB) >> 8 & 0xff;
    E_0;
    return data;
}

// 读取指定长度的字符串
void LCDReadString(char *p, int n)
{
    while (n--)
        *p++ = LCDReadData();
    *p = '\0';
}

// 写入字符
void LCDWriteData(char ch)
{
    while (LCDBusyTest());
    LCDBeginWrite();
    RS_1;
    RW_0;
    E_0;
    GPIOB->ODR = (GPIOB->ODR & 0xff) + (ch << 8);
    __nop();
    __nop();
    __nop();
    __nop();
    E_1;
    __nop();
    __nop();
    __nop();
    __nop();
    E_0;
}

// 写入字符串
void LCDWriteString(char *s)
{
    while (*s != '\0')
    {
        LCDWriteData(*s);
        s++;
    }
}

// 清屏, 且光标回到(0, 0), 左上角坐标也回到(0, 0)
void LCDClear(void)
{
    LCDWriteCmd(0x01);
}

// 初始化液晶
void LCDInit(void)
{
    uint8_t i = 3;
    delay(15); // 延时15ms,首次写指令时应给LCD一段较长的反应时间
    while (i--) // 必须写入3次
    {
        LCDWriteCmd(0x38); // 显示模式设置:16×2显示,5×7点阵,8位数据接口
        delay(5);
    }
    LCDWriteCmd(0x0c); // 显示模式设置:显示开,无光标
    delay(5);
    LCDWriteCmd(0x06); // 显示模式设置:光标右移,整屏显示不移
    delay(5);
    LCDClear();
    delay(5);
}

// 添加自定义字符
void LCDAddCharacter(uint8_t id, unsigned char table[])
{
    uint8_t i;
    LCDWriteCmd(0x40 + id * 8);
    for (i = 0; i < 8; i++)
        LCDWriteData(table[i]);
}

// 显示两位十六进制数
void showhex(uint8_t n)
{
    char *list = "0123456789ABCDEF";
    LCDWriteData(list[n / 16]);
    LCDWriteData(list[n % 16]);
}

int main(void)
{
    char buf[10];
    GPIO_InitTypeDef init;
    uint8_t i, j, k;
   
    // 初始化整个PB口
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    init.GPIO_Mode = GPIO_Mode_Out_PP;
    init.GPIO_Pin = GPIO_Pin_All;
    init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &init);
   
    // 初始化定时器, 每1ms触发一次中断, 供delay函数使用
    SysTick_Config(SystemCoreClock / 1000);
   
    LCDInit();
    LCDSetPos(0, 1);
    LCDWriteString("Arslanbar");
    LCDSetPos(1, 2);
    LCDWriteString("Welcome");
   
    // 显示自定义字符
    LCDAddCharacter(0, userchar);
    LCDAddCharacter(1, userchar2);
    LCDSetPos(0, 11);
    LCDWriteData(0);
    LCDWriteData(1);
   
    // 读出自定义字符
    delay(3000);
    LCDWriteCmd(0x40); // 第一个自定义字符的地址
    LCDReadString(buf, 16);
    LCDClear();
    for (i = 0; i < 16; i++)
    {
        if (i == 8)
            LCDSetPos(1, 0);
        showhex(buf[i]);
    }
   
    delay(3000);
    LCDClear();
    LCDSetPos(0, 16);
    LCDWriteString("Please input the "); // 不支持自动换行...
    LCDSetPos(1, 16);
    LCDWriteString("number of rows:");
   
    LCDSetPos(1, 16);
    LCDReadString(buf, 6); // 读取写在屏幕上的字符串
    LCDSetPos(0, 0);
    LCDWriteString(buf); // 显示读取的字符串
    i = LCDGetPos(); // 获取现在的光标位置
    LCDSetPos(1, 0);
    LCDWriteString("Pos: ");
    showhex(i);
    delay(5000);
   
    for (i = 0; i < 16; i++)
    {
        LCDWriteCmd(0x18); // 整屏左移
        delay(500);
    }
    // 左移完毕后,液晶左上角坐标滚动到了0x10处
   
    // 清屏后,液晶左上角坐标又会回到0x00处
    LCDClear();
    LCDWriteString("Here's 0x00.(");
    showhex(LCDGetPos());
    LCDWriteData(')');
    LCDSetPos(1, 0);
    LCDWriteString("Here's 0x40.(");
    showhex(LCDGetPos());
    LCDWriteData(')');
   
    delay(5000);
    LCDClear();
    LCDWriteString("LCDGetPosTest:");
    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < 40; j++) // 每行只有40个空间*而非0x40个, 切记!
        {
            LCDSetPos(i, j);
            k = LCDGetPos();
            LCDSetPos(1, 0);
            showhex(k);
            delay(500);
        }
    }
   
    // 1602液晶的只能保存80个显示字符
    // 每行各40个字符
    // 因此0x28~0x39和0x68~0x79这段地址是不能用的
    LCDSetPos(0, 0);
    LCDWriteString("0000000000111111111122222222223333333333");
    LCDWriteString("4444444444555555555566666666667777777777");
    while (1)
    {
        delay(500);
        LCDWriteCmd(0x1c); // 整屏右移
    }
}
一派護法 十九級
4樓 發表于:2016-7-3 10:14
程式中主要實作了LCDReadData函式:讀資料時序
這是大部分開發板所帶的資料裡面沒有的
一派護法 十九級
5樓 發表于:2016-7-3 10:17
【運行效果】
滾屏:

左上角最初是(0, 0),向左滾屏就到了(0, 39)了
注意是十進位的39,而非0x39
一派護法 十九級
6樓 發表于:2016-7-3 10:18
一派護法 十九級
7樓 發表于:2016-7-3 10:19
一派護法 十九級
8樓 發表于:2016-7-3 10:19

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:328 回複數:7
評論數: ?
作者: 巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2016-7-3 10:19
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。