一派護法 十九級 |
【程式】 #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); // 整屏右移 } }
|