千万别手欠执行stop slave

今天我一个朋友,执行了stop slave,给卡死了,结果kill 进程ID也杀不死。

这是由于在主库上执行了一条很耗时的大SQL,通过主从复制在从库接收过来后,SQL_THREAD开始执行,

这时你只要执行了stop slave,立马就卡死,之后你再执行show slave status\G;也会被卡住,必须等

待那条大SQL执行完,才会结束stop slave,除非你pkill -9 mysql进程。


下面就来重现一下,主库上执行全表更新update sbtest set c='mariadb';等执行完以后,会记录到binlog日志里,然后在从库执行的时候,stop slave,就会卡住。


wKioL1QiaFOBJGzTAAQiz6rFjBE552.jpg

wKiom1QiaDGzUh1kAABmRp0ofZE342.jpg


最后,提醒一下,在执行stop slave的时候,一定要看下主库上的慢SQL,避免出现被卡住的情况发生。



man.c:#include "includes.h" #include "tim.h" #include "OLED_I2C.h" #include "stdio.h" #include "dht11.h" // 包含DHT11头文件 #include "mlx90614.h" #include "exti.h" #include "key.h" #include "beep.h" // 添加蜂鸣器头文件 /* 任务句柄 */ TaskHandle_t app_task_init_handle = NULL; TaskHandle_t app_task_display_handle = NULL; TaskHandle_t app_task_clock_handle = NULL; // 新增时钟任务句柄 /* 全局时间变量 */ static uint8_t g_hour = 15; static uint8_t g_minute = 04; static uint8_t g_second = 56; /* 任务函数 */ static void app_task_init(void *pvParameters); static void app_task_display(void *pvParameters); static void app_task_clock(void *pvParameters); // 新增时钟任务 // 显示时间函数 static void display_time(void) { char time_str[20]; /* 显示时间标题 */ OLED_ShowCN(0, 0, 0); // 在(0, 0)位置显示中文"时" OLED_ShowCN(16, 0, 1); // 在(16, 0)位置显示中文"间" OLED_ShowStr(32, 0, ":", 2); /* 格式化并显示当前时间 */ sprintf(time_str, "%02d:%02d:%02d", g_hour, g_minute, g_second); OLED_ShowStr(40, 0, time_str, 2); } // 显示体温函数 static void display_body_temp(void) { char body_temp_str[10]; // 新增用于存储体温的字符串 float body_temp; // 新增用于存储体温的浮点数 /* 显示体温标题 */ OLED_ShowCN(0, 2, 2); // 体温 OLED_ShowCN(16, 2, 3); OLED_ShowStr(32, 2, ":", 2); // 读取体温数据 body_temp = SMBus_ReadTemp(); if (body_temp < -70.01 || body_temp > 382.19) { // 处理读取失败的情况 sprintf(body_temp_str, "Err"); // 关闭蜂鸣器 GPIO_ResetBits(GPIOF, GPIO_Pin_8); printf("SMBus_ReadTemp failed, body_temp = %.2f\n", body_temp); // 添加调试信息 } else { // 格式化体温数据 sprintf(body_temp_str, "%.2f C", body_temp); // 判断体温是否大于37°C if (body_temp > 37) { // 打开蜂鸣器 GPIO_SetBits(GPIOF, GPIO_Pin_8); } else { // 关闭蜂鸣器 GPIO_ResetBits(GPIOF, GPIO_Pin_8); } } // 显示体温数据 OLED_ShowStr(40, 2, body_temp_str, 2); } // 显示温湿度函数 static void display_temp_humi(void) { char temp_str[10]; char humi_str[10]; u8 dht11_data[5]; /* 显示温湿度标题 */ OLED_ShowCN(0, 4, 4); // 温湿度 OLED_ShowCN(16, 4, 5); OLED_ShowCN(32, 4, 6); OLED_ShowStr(47, 4, ":", 2); // 读取DHT11数据 if (Dht11_Data(dht11_data) == 0) { // 提取温度和湿度数据 uint8_t temp = dht11_data[2]; uint8_t humi = dht11_data[0]; // 格式化温度和湿度数据 sprintf(temp_str, "%d C", temp); sprintf(humi_str, "%d %%", humi); // 显示温度和湿度数据 OLED_ShowStr(55, 4, temp_str, 2); OLED_ShowStr(85, 4, humi_str, 2); } else { // 数据读取失败,显示温湿度信息 OLED_ShowStr(55, 4, "err", 2); OLED_ShowStr(85, 4, "err", 2); } } int main(void) { /* 创建初始化任务 */ xTaskCreate((TaskFunction_t)app_task_init, (const char *)"app_task_init", (uint16_t)512, (void *)NULL, (UBaseType_t)4, (TaskHandle_t *)&app_task_init_handle); /* 开启任务调度 */ vTaskStartScheduler(); extern const unsigned char BMP1[]; } void app_task_init(void *pvParameters) { /* 设置系统中断优先级分组4 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); /* 初始化外设 */ Usart1_Init(115200); I2C_Configuration(); OLED_Init(); OLED_CLS(); // 清屏 MLX90614_Init(); Dht11_Init(); // 初始化DHT11传感器 key_init(); // 初始化按键 Beep_Init(); // 初始化蜂鸣器 /* 创建显示任务 */ xTaskCreate((TaskFunction_t)app_task_display, (const char *)"app_task_display", (uint16_t)512, (void *)NULL, (UBaseType_t)7, (TaskHandle_t *)&app_task_display_handle); /* 创建时钟任务(优先级低于显示任务) */ xTaskCreate((TaskFunction_t)app_task_clock, (const char *)"app_task_clock", (uint16_t)256, (void *)NULL, (UBaseType_t)5, (TaskHandle_t *)&app_task_clock_handle); vTaskDelete(NULL); } /* 时钟任务:每秒递增计数器 */ static void app_task_clock(void *pvParameters) { while (1) { /* 延时1秒 */ vTaskDelay(1000); /* 递增秒数 */ g_second++; if (g_second >= 60) { g_second = 0; g_minute++; // 秒满60,进一位分钟 if (g_minute >= 60) { g_minute = 0; g_hour++; // 分钟满60,进一位小时 if (g_hour >= 24) { g_hour = 0; // 小时满24,归零 } } } } } /* 显示任务:更新OLED时间显示 */ static void app_task_display(void *pvParameters) { u8 key_value; OLED_Fill(0xFF); // 全屏点亮 OLED_DrawBMP(0, 0, 128, 8, (unsigned char *)BMP1); // 测试BMP位图 OLED_Fill(0x00); // 全屏灭 vTaskDelay(1000); while (1) { /* 扫描按键 */ key_value = key_scanf(1); /* 根据按键值调节时间 */ switch (key_value) { case KEY0_VALUE: g_hour = (g_hour + 1) % 24; break; case KEY1_VALUE: g_minute = (g_minute + 1) % 60; break; case KEY2_VALUE: g_second = (g_second + 1) % 60; break; default: break; } // 显示时间 display_time(); // 显示体温 display_body_temp(); // 显示温湿度 display_temp_humi(); vTaskDelay(1500); // 增加读取频率,可根据实际情况调整 } } void vApplicationTickHook(void) { // lv_tick_inc(1); // 如果使用LVGL,启用此函数 } mlx90614.c:#include "MLX90614.h" #include "delay.h" #define uint unsigned int #define uchar unsigned char #define Nack_counter 10 /*********************************************** 引脚说明: PB9 -- SDA PB8 -- SCL ************************************************/ void MLX90614_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; //结构体 //使能GPIO B组时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9; //引脚8 9 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式 GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //输出推挽 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //上拉 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //输出速度 GPIO_Init(GPIOB, &GPIO_InitStruct); //总线空闲 SMBUS_SCL = 1; SMBUS_SDA_OUT = 1; } //引脚模式变更--SDA void SMBUS_Sda_Mode(GPIOMode_TypeDef mode) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //第9号引脚 GPIO_InitStructure.GPIO_Mode = mode; //输入/输出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //推挽输出,增强驱动能力,引脚的输出电流更大 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //引脚的速度最大为100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //没有使用内部上拉电阻 GPIO_Init(GPIOB, &GPIO_InitStructure); } void MLX_start(void) //启动信号 { SMBUS_Sda_Mode(GPIO_Mode_OUT); SMBUS_SDA_OUT = 1; SMBUS_Delay(10); SMBUS_SCL = 1; SMBUS_Delay(10); SMBUS_SDA_IN = 0; SMBUS_Delay(10); SMBUS_SCL = 0; SMBUS_Delay(10); } void MLX_stop(void) //止信号 { SMBUS_Sda_Mode(GPIO_Mode_OUT); SMBUS_SCL = 0; SMBUS_Delay(10); SMBUS_SDA_IN = 0; SMBUS_Delay(10); SMBUS_SCL = 1; SMBUS_Delay(10); SMBUS_SDA_OUT = 1; } //--------- 发送一个字节--------- u8 SMBUS_Sendbyte(u8 dat_byte) { u8 Bit_counter; u8 Ack_bit; u8 bit_out; for(Bit_counter=8; Bit_counter; Bit_counter--) { if (dat_byte&0x80) { bit_out=1; // 如果dat_byte的当前位为1,则设置bit_out } else { bit_out=0; // 否则清0 } SMBUS_SendBit(bit_out); // 向SDA发送当前位 dat_byte<<=1; // 获取下一位 } Ack_bit = SMBUS_ReceiveBit(); // 获取确认位 return Ack_bit; } //-----------发送一个位--------- void SMBUS_SendBit(u8 bit_out) { SMBUS_Sda_Mode(GPIO_Mode_OUT); if(bit_out==0) { SMBUS_SDA_IN = 0; } else { SMBUS_SDA_OUT = 1; } SMBUS_Delay(4); SMBUS_SCL = 1; // 设置SCL SMBUS_Delay(12); // 高电平时钟脉冲 SMBUS_SCL = 0; // 清空SCL SMBUS_Delay(6); // 时钟脉冲低电平 // SMBUS_SDA_OUT = 1; return; } //---------- 接收一个字节-------- u8 SMBUS_RecriveByte(u8 ack_nack) { u8 RX_buffer; u8 Bit_Counter; for(Bit_Counter=8; Bit_Counter; Bit_Counter--) { if(SMBUS_ReceiveBit()) //获取SDA的一个字节 { RX_buffer <<= 1; // 如果该位为高,设置RX_buffer为1 RX_buffer |=0x01; } else { RX_buffer <<= 1; // 如果该位为低,设置RX_buffer为0 RX_buffer &=0xfe; } } SMBUS_SendBit(ack_nack); // 发送确认位 return RX_buffer; } //---------- 接收一个位---------- u8 SMBUS_ReceiveBit(void) { u8 Ack_bit; SMBUS_Sda_Mode(GPIO_Mode_OUT); SMBUS_SDA_OUT = 1; //引脚靠外部电阻上拉,当作输入 SMBUS_Delay(4); // 时钟信号高电平 SMBUS_SCL = 1; // 设置SCL SMBUS_Delay(10); // 时钟脉冲高电平 SMBUS_Sda_Mode(GPIO_Mode_IN); if (SMBUS_SDA_PIN()) { Ack_bit=1; } else { Ack_bit=0; } SMBUS_SCL = 0; // 清空SCL SMBUS_Delay(6); // 时钟脉冲低电平 return Ack_bit; } u16 data; // 数据存储 u8 DataL=0; // 低数据存储 u8 DataH=0; // 高数据存储 //从RAM / EEPROM读取数据 u16 SMBUS_ReadMemory(u8 slaveAddress, u8 command) { u8 Pec; // PEC字节存储 u8 arr[6]; // 发送字节缓存区 u8 PecReg; // 计算PEC字节存储 u8 ErrorCounter; // 定义与MLX90614尝试通信的次数 ErrorCounter=0x00; // 初始化ErrorCounter slaveAddress <<= 1; //2-7位表示从机地址 do { repeat: MLX_stop(); //如果从机发送NACK止通信 --ErrorCounter; //错误计数器递减 if(!ErrorCounter) //ErrorCounter=0? { break; //退出循环 } MLX_start(); //启动信号 if(SMBUS_Sendbyte(slaveAddress)) //Send SlaveAddress 最低位Wr=0表示接下来写命令 { goto repeat; //再次尝试通信 } if(SMBUS_Sendbyte(command)) //发送指令 { goto repeat; //再次尝试通信 } MLX_start(); //启动信号 if(SMBUS_Sendbyte(slaveAddress+1)) //Send SlaveAddress 最低位Rd=1表示接下来读数据 { goto repeat; //再次尝试通信 } DataL = SMBUS_RecriveByte(ACK); //读取低位数据,主机发送ACK应答信号 DataH = SMBUS_RecriveByte(ACK); //读取高位数据,主机发送ACK应答信号 Pec = SMBUS_RecriveByte(NACK); //读取PEC数据, 主机发送NACK信号 MLX_stop(); //止信号 arr[5] = slaveAddress; arr[4] = command; arr[3] = slaveAddress+1; //加载数组 arr[2] = DataL; arr[1] = DataH; arr[0] = 0; PecReg=PEC_Calculation(arr); //计算CRC } while(PecReg != Pec); //如果计算和收到的CRC相同则退出循环 GPIO_ToggleBits(GPIOF, GPIO_Pin_10); data = (DataH<<8) | DataL; return data; } /******************************************************************************* * Function Name : PEC_calculation * Description : 计算接收到的PEC 字节 * Input : pec[] * Output : None * Return : pec[0]-this byte contains calculated crc value *******************************************************************************/ u8 PEC_Calculation(u8 pec[]) { u8 crc[6]; u8 BitPosition=47; u8 shift; u8 i; u8 j; u8 temp; do { /*加载模式值0x000000000107*/ crc[5]=0; crc[4]=0; crc[3]=0; crc[2]=0; crc[1]=0x01; crc[0]=0x07; /*最大值设置为47 ( six bytes byte5...byte0,MSbit=47)*/ BitPosition=47; /*将换挡位置设置为0*/ shift=0; /*从MSByte字节5开始,在传输的消息中找到第一个“ 1”*/ i=5; j=0; while((pec[i]&(0x80>>j))==0 && i>0) { BitPosition--; if(j<7) { j++; } else { j=0x00; i--; } }/*结束循环*/ /*获取图案值的平移值*/ shift=BitPosition-8; /*换档值*/ while(shift) { for(i=5; i<0xFF; i--) { if((crc[i-1]&0x80) && (i>0)) { temp=1; } else { temp=0; } crc[i]<<=1; crc[i]+=temp; } shift--; } /*PEC ^ CRC*/ for(i=0; i<=5; i++) { pec[i] ^=crc[i]; } } while(BitPosition>8); return pec[0]; } /******************************************************************************* * Function Name : SMBus_ReadTemp * Description : 计算并返回温度 * Input : None * Output : None * Return : SMBus_ReadMemory(0x00, 0x07)*0.02-273.15 *******************************************************************************/ float SMBus_ReadTemp(void) { GPIO_ToggleBits(GPIOF, GPIO_Pin_9); float temp; temp = SMBUS_ReadMemory(SA, RAM_ACCESS|RAM_TOBJ1)*0.02-273.15+1.5; return temp; } void SMBUS_Delay(u16 time) { u16 i, j; for (i=0; i<4; i++) { for (j=0; j<time; j++); } } mlx90614.h:#ifndef __MLX90614_H #define __MLX90614_H #include "stm32f4xx.h" #include "sys.h" #define ACK 0 //应答 #define NACK 1 //无应答 #define SA 0x00 //Slave address 单个MLX90614时地址为0x00,多个时地址默认为0x5a #define RAM_ACCESS 0x00 //RAM access command RAM存取命令 #define EEPROM_ACCESS 0x20 //EEPROM access command EEPROM存取命令 #define RAM_TOBJ1 0x07 //To1 address in the eeprom 目标1温度,检测到的红外温度 -70.01 ~ 382.19度 #define SMBUS_PORT GPIOB //PB端口(端口和下面的两个针脚可自定义) #define SMBUS_SCK GPIO_Pin_8 //PB8:SCL #define SMBUS_SDA GPIO_Pin_9 //PB9:SDA #define SMBUS_SCL PBout(8) #define SMBUS_SDA_IN PBout(9) #define SMBUS_SDA_OUT PBout(9) #define SMBUS_SDA_PIN() PBin(9) //读取引脚(SDA)电平 void MLX90614_Init(void); void MLX_start(void); void MLX_stop(void); u8 SMBUS_Sendbyte(u8 dat_byte); void SMBUS_SendBit(u8 bit_out); u8 SMBUS_RecriveByte(u8 ack_nack); u8 SMBUS_ReceiveBit(void); u16 SMBUS_ReadMemory(u8 slaveAddress, u8 command); u8 PEC_Calculation(u8 pec[]); float SMBus_ReadTemp(void); void SMBUS_Delay(u16 time); #endif OLED_I2C.c:/************************************************************************************ * Copyright (c), 2014, HelTec Automatic Technology co.,LTD. * All rights reserved. * * Http: www.heltec.cn * Email: cn.heltec@gmail.com * WebShop: heltec.taobao.com * * File name: OLED_I2C.c * Project : HelTec.uvprij * Processor: STM32F103C8T6 * Compiler : MDK fo ARM * * Author : 小林 * Version: 1.00 * Date : 2014.4.8 * Email : hello14blog@gmail.com * Modification: none * * Description:128*64点阵的OLED显示屏驱动文件,仅适用于惠特自动化(heltec.taobao.com)的SD1306驱动OLED_Iic通信方式显示屏 * * Others: none; * * Function List: * 1. void I2C_Configuration(void) -- 配置CPU的硬件I2C * 2. void I2C_WriteByte(uint8_t addr,uint8_t data) -- 向寄存器地址写一个byte的数据 * 3. void WriteCmd(unsigned char I2C_Command) -- 写命令 * 4. void WriteDat(unsigned char I2C_Data) -- 写数据 * 5. void OLED_Init(void) -- OLED屏初始化 * 6. void OLED_SetPos(unsigned char x, unsigned char y) -- 设置起始点坐标 * 7. void OLED_Fill(unsigned char fill_Data) -- 全屏填充 * 8. void OLED_CLS(void) -- 清屏 * 9. void OLED_ON(void) -- 唤醒 * 10. void OLED_OFF(void) -- 睡眠 * 11. void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize) -- 显示字符串(字体大小有6*8和8*16两种) * 12. void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N) -- 显示中文(中文需要先取模,然后放到codetab.h中) * 13. void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]) -- BMP图片 * * History: none; * *************************************************************************************/ #include "OLED_I2C.h" #include "delay.h" #include "codetab.h" void I2C_Configuration(void) { GPIO_InitTypeDef GPIO_InitStruct; //使能GPIO E组时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //使能GPIO D组时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_10; //引脚9,引脚10 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出 GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //开漏 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ速度 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOE, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_15; //引脚1,引脚15 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出 GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ速度 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOD, &GPIO_InitStruct); //VCC PDout(1) = 1; //GND PDout(15) = 0; //空闲状态 OLED_SCL = 1; OLED_SDA_OUT = 1; } //数据引脚模式 void OLED_Iic_OLED_SDA_Mode(GPIOMode_TypeDef mode) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; //引脚8 GPIO_InitStruct.GPIO_Mode = mode; //输出模式 GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //开漏输出 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度 GPIO_Init(GPIOE, &GPIO_InitStruct); } //延时是为了给从机有一定的时间响应 //启动信号 void OLED_Iic_Start(void) { //数据为输出 OLED_Iic_OLED_SDA_Mode(GPIO_Mode_OUT); OLED_SCL = 1; OLED_SDA_OUT = 1; delay_us(5); OLED_SDA_OUT = 0; delay_us(5); OLED_SCL = 0; } //止信号 void OLED_Iic_Stop(void) { //数据为输出 OLED_Iic_OLED_SDA_Mode(GPIO_Mode_OUT); OLED_SCL = 0; OLED_SDA_OUT = 0; delay_us(5); OLED_SCL = 1; delay_us(5); OLED_SDA_OUT = 1; } //发送一位数据,数据转换为引脚电平 //Ack = 1:引脚为高电平 Ack=0,引脚为低电平 void OLED_Iic_Send_Ack(u8 Ack) { //数据为输出 OLED_Iic_OLED_SDA_Mode(GPIO_Mode_OUT); OLED_SCL = 0; //在时钟为低电平时,可变更引脚状态 if(Ack == 1) OLED_SDA_OUT = 1; else OLED_SDA_OUT = 0; delay_us(5); OLED_SCL = 1; delay_us(5); OLED_SCL = 0; } //发送一个字节(一个字节8位) //先发高位数据:1001 1000 void OLED_Iic_Send_Byte(u8 txdata) { u8 i; //数据为输出 OLED_Iic_OLED_SDA_Mode(GPIO_Mode_OUT); //8位要8个脉冲 OLED_SCL = 0; for(i=0; i<8; i++) { //在时钟为低电平时,可变更引脚状态 if( txdata & (1<<(7-i))) OLED_SDA_OUT = 1; else OLED_SDA_OUT = 0; delay_us(1); OLED_SCL = 1; delay_us(1); OLED_SCL = 0; } } //接受一位数据,判断引脚电平 u8 OLED_Iic_Recv_Ack(void) { u8 ack = 0; //数据为输入 OLED_Iic_OLED_SDA_Mode(GPIO_Mode_IN); OLED_SCL = 0; delay_us(2); //在时钟线为高电平时,判断引脚数据,接受数据 OLED_SCL = 1; delay_us(2); if(OLED_SDA_IN) ack = 1; else ack = 0; OLED_SCL = 0; return ack; } //接受一个字节 u8 OLED_Iic_Recv_Byte(void) { u8 i, rxdata = 0x00; //数据为输入 OLED_Iic_OLED_SDA_Mode(GPIO_Mode_IN); //8位要8个脉冲 OLED_SCL = 0; for(i=0; i<8; i++) { delay_us(5); OLED_SCL = 1; delay_us(5); //在时钟线为高电平时,判断引脚数据,接受数据 if(OLED_SDA_IN) rxdata |= (1<<(7-i)); // else // rxdata &= ~(1<<(7-i)); OLED_SCL = 0; } return rxdata; } void I2C_WriteByte(uint8_t addr,uint8_t data) { u8 ack = 0; //启动信号 OLED_Iic_Start(); //STM32做为输出,发送设备地址,并对24C02执行写操作 OLED_Iic_Send_Byte(OLED_ADDRESS); ack = OLED_Iic_Recv_Ack(); if(ack == 1) { printf("ack failure 1\n"); return; } //发送寄存器地址(一个字节) OLED_Iic_Send_Byte(addr); ack = OLED_Iic_Recv_Ack(); if(ack == 1) { printf("ack failure 1\n"); return; } //发送数据(一个字节) OLED_Iic_Send_Byte(data); ack = OLED_Iic_Recv_Ack(); if(ack == 1) { printf("ack failure 1\n"); return; } OLED_Iic_Stop(); } void WriteCmd(unsigned char I2C_Command)//写命令 { I2C_WriteByte(0x00, I2C_Command); } void WriteDat(unsigned char I2C_Data)//写数据 { I2C_WriteByte(0x40, I2C_Data); } void OLED_Init(void) { delay_ms(100); //这里的延时很重要 WriteCmd(0xAE); //display off WriteCmd(0x20); //Set Memory Addressing Mode WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7 WriteCmd(0xc8); //Set COM Output Scan Direction WriteCmd(0x00); //---set low column address WriteCmd(0x10); //---set high column address WriteCmd(0x40); //--set start line address WriteCmd(0x81); //--set contrast control register WriteCmd(0xff); //亮度调节 0x00~0xff WriteCmd(0xa1); //--set segment re-map 0 to 127 WriteCmd(0xa6); //--set normal display WriteCmd(0xa8); //--set multiplex ratio(1 to 64) WriteCmd(0x3F); // WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content WriteCmd(0xd3); //-set display offset WriteCmd(0x00); //-not offset WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency WriteCmd(0xf0); //--set divide ratio WriteCmd(0xd9); //--set pre-charge period WriteCmd(0x22); // WriteCmd(0xda); //--set com pins hardware configuration WriteCmd(0x12); WriteCmd(0xdb); //--set vcomh WriteCmd(0x20); //0x20,0.77xVcc WriteCmd(0x8d); //--set DC-DC enable WriteCmd(0x14); // WriteCmd(0xaf); //--turn on oled panel } void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标 { WriteCmd(0xb0+y); WriteCmd(((x&0xf0)>>4)|0x10); WriteCmd((x&0x0f)|0x01); } void OLED_Fill(unsigned char fill_Data)//全屏填充 { unsigned char m,n; for(m=0;m<8;m++) { WriteCmd(0xb0+m); //page0-page1 WriteCmd(0x00); //low column start address WriteCmd(0x10); //high column start address for(n=0;n<128;n++) { WriteDat(fill_Data); } } } void OLED_CLS(void)//清屏 { OLED_Fill(0x00); } //-------------------------------------------------------------- // Prototype : void OLED_ON(void) // Calls : // Parameters : none // Description : 将OLED从休眠中唤醒 //-------------------------------------------------------------- void OLED_ON(void) { WriteCmd(0X8D); //设置电荷泵 WriteCmd(0X14); //开启电荷泵 WriteCmd(0XAF); //OLED唤醒 } //-------------------------------------------------------------- // Prototype : void OLED_OFF(void) // Calls : // Parameters : none // Description : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA //-------------------------------------------------------------- void OLED_OFF(void) { WriteCmd(0X8D); //设置电荷泵 WriteCmd(0X10); //关闭电荷泵 WriteCmd(0XAE); //OLED休眠 } //-------------------------------------------------------------- // Prototype : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize) // Calls : // Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16) // Description : 显示codetab.h中的ASCII字符,有6*8和8*16可选择 //-------------------------------------------------------------- void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize) { unsigned char c = 0,i = 0,j = 0; switch(TextSize) { case 1: { while(ch[j] != '\0') { c = ch[j] - 32; if(x > 126) { x = 0; y++; } OLED_SetPos(x,y); for(i=0;i<6;i++) WriteDat(F6x8[c][i]); x += 6; j++; } }break; case 2: { while(ch[j] != '\0') { c = ch[j] - 32; if(x > 120) { x = 0; y++; } OLED_SetPos(x,y); for(i=0;i<8;i++) WriteDat(F8X16[c*16+i]); OLED_SetPos(x,y+1); for(i=0;i<8;i++) WriteDat(F8X16[c*16+i+8]); x += 8; j++; } }break; } } //-------------------------------------------------------------- // Prototype : void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N) // Calls : // Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引 // Description : 显示codetab.h中的汉字,16*16点阵 //-------------------------------------------------------------- void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N) { unsigned char wm=0; unsigned int adder=32*N; OLED_SetPos(x , y); for(wm = 0;wm < 16;wm++) { WriteDat(F16x16[adder]); adder += 1; } OLED_SetPos(x,y + 1); for(wm = 0;wm < 16;wm++) { WriteDat(F16x16[adder]); adder += 1; } } //-------------------------------------------------------------- // Prototype : void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]); // Calls : // Parameters : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8) // Description : 显示BMP位图 //-------------------------------------------------------------- void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]) { unsigned int j=0; unsigned char x,y; if(y1%8==0) y = y1/8; else y = y1/8 + 1; for(y=y0;y<y1;y++) { OLED_SetPos(x0,y); for(x=x0;x<x1;x++) { WriteDat(BMP[j++]); } } } OLED_I2c.h:#ifndef __OLED_I2C_H #define __OLED_I2C_H #include "stm32f4xx.h" #include "sys.h" #include "delay.h" #define OLED_SCL PEout(8) #define OLED_SDA_IN PEin(10) #define OLED_SDA_OUT PEout(10) #define OLED_ADDRESS 0x78 //通过调整0R电阻,屏可以0x78和0x7A两个地址 -- 默认0x78 void I2C_Configuration(void); void I2C_WriteByte(uint8_t addr,uint8_t data); void WriteCmd(unsigned char I2C_Command); void WriteDat(unsigned char I2C_Data); void OLED_Init(void); //x:0~127 y:0~7 void OLED_SetPos(unsigned char x, unsigned char y); void OLED_Fill(unsigned char fill_Data); void OLED_CLS(void); void OLED_ON(void); void OLED_OFF(void); void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize); void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N); void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]); #endif delay.c:#include "delay.h" //址詢症俣微墨覔时时荬覙勋要讓:0~999 void delay_us(uint32_t nus) { uint32_t ticks; uint32_t told,tnow,tcnt=0; //症俣值趯吱SysTick->LOAD == 999 uint32_t reload=SysTick->LOAD; //系统吱时謩讟諛值 //SystemCoreClock = 168000 000 //ticks = 168*nus ticks=nus*(SystemCoreClock/1000000);//穴要謩逇苿私 //症俣輪私矛症俣窑郫謩 told=SysTick->VAL; //贂薷色时謩輪私值 /* 趻前址讏[酄壯?堋占讉贌詤袌芏蓭媳蠟专葊占毡前蓭媳矛斋艤倩汀贌毡前蓭媳时荬謩瞢确褦] */ vTaskSuspendAll(); while(1) { tnow=SysTick->VAL; if(tnow!=told) { /* SYSTICK藝一俣譂莸謩輪私 */ if(tnow<told) tcnt+=told-tnow; else tcnt+=reload-tnow+told+1; told=tnow; /* 时荬蝇诮/謭詺要覔詸謩时荬ticks,詹螊远c*/ if(tcnt>=ticks) break; } } /* 軚卮址讏[酄壯 */ xTaskResumeAll(); } void delay_ms(uint32_t nms) { //系统蹃墨覔时 vTaskDelay(nms); } delay.h:#ifndef __DELAY_H #define __DELAY_H #include "stm32f4xx.h" #include "FreeRTOS.h" #include "task.h" void delay_us(uint32_t nus); void delay_ms(uint32_t nms); #endif ,帮我实现在oled屏幕上显示体温,
最新发布
06-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值