/* main.c
STM32F103C8T6
I2C2 -> AHT10 (PB10=SCL, PB11=SDA)
SPI1 -> SSD1680 (152x152)
res -> PA1
dc -> PA2
cs -> PA3
busy-> PA4
sck -> PA5 (SPI1_SCK)
sda -> PA7 (SPI1_MOSI)
SPI wrapper prefix: spd_spi_
All code in one file, simple 8x16 font included.
*/
#include "main.h"
#include "i2c.h"
#include "spi.h"
#include "gpio.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
/* ------------------------- AHT10 ------------------------- */
#define AHT10_ADDR (0x38 << 1)
#define AHT10_CMD_TRIGGER 0xAC
#define AHT10_CMD_ARG1 0x33
#define AHT10_CMD_ARG2 0x00
float g_temperature = 0.0f;
float g_humidity = 0.0f;
/* ------------------------- EPD pins ------------------------- */
#define EPD_RES_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET)
#define EPD_RES_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET)
#define EPD_DC_CMD() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET)
#define EPD_DC_DATA() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET)
#define EPD_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET)
#define EPD_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET)
#define EPD_BUSY_LEVEL GPIO_PIN_SET
#define EPD_IS_BUSY() (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == EPD_BUSY_LEVEL)
/* ------------------------- display ------------------------- */
#define EPD_WIDTH 152
#define EPD_HEIGHT 152
#define EPD_BUFFER_BYTES ((EPD_WIDTH * EPD_HEIGHT) / 8)
static uint8_t frame_buf[EPD_BUFFER_BYTES];
extern I2C_HandleTypeDef hi2c2;
extern SPI_HandleTypeDef hspi1;
/* ------------------------- simple 8x16 font ------------------------- */
static const uint8_t font_8x16_digits[][16] = {
/* '0' */ {0x00,0x3C,0x66,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00},
/* '1' */ {0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
/* '2' */ {0x00,0x3C,0x66,0xC3,0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0xFF,0x00,0x00,0x00,0x00},
/* '3' */ {0x00,0x3C,0x66,0xC3,0x03,0x06,0x1C,0x06,0x03,0x03,0x66,0x3C,0x00,0x00,0x00,0x00},
/* '4' */ {0x00,0x06,0x0E,0x1E,0x36,0x66,0xC6,0xFF,0x06,0x06,0x06,0x1F,0x00,0x00,0x00,0x00},
/* '5' */ {0x00,0xFF,0xC0,0xC0,0xC0,0xFC,0xCE,0x03,0x03,0x03,0x66,0x3C,0x00,0x00,0x00,0x00},
/* '6' */ {0x00,0x3C,0x66,0xC3,0xC0,0xFC,0xCE,0xC3,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00},
/* '7' */ {0x00,0xFF,0x03,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
/* '8' */ {0x00,0x3C,0x66,0xC3,0xC3,0x66,0x3C,0x66,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00},
/* '9' */ {0x00,0x3C,0x66,0xC3,0xC3,0xC3,0x67,0x3F,0x03,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00},
};
static const uint8_t font_8x16_misc[][16] = {
/* '.' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
/* '-' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
/* '%' */ {0x00,0xC1,0xC6,0x0C,0x18,0x30,0x60,0xC1,0x86,0x0C,0x18,0x30,0x60,0x00,0x00,0x00},
/* ':' */ {0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
/* 'C' */ {0x00,0x0F,0x1F,0x38,0x70,0x70,0x70,0x70,0x70,0x38,0x1F,0x0F,0x00,0x00,0x00,0x00},
/* 'c' */ {0x00,0x00,0x00,0x00,0x1E,0x30,0x60,0x7E,0x66,0x66,0x3E,0x00,0x00,0x00,0x00,0x00},
/* 'T' */ {0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
/* 'e' */ {0x00,0x00,0x00,0x00,0x1E,0x30,0x7E,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00},
/* 'm' */ {0x00,0x00,0x00,0x00,0xEE,0x92,0x92,0x92,0x92,0x92,0x92,0x00,0x00,0x00,0x00,0x00},
/* 'p' */ {0x00,0x00,0x00,0x00,0xDE,0x63,0x63,0x63,0x7E,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
/* 'H' */ {0x00,0xC3,0xC3,0xC3,0xC3,0xFF,0xC3,0xC3,0xC3,0xC3,0xC3,0x00,0x00,0x00,0x00,0x00},
/* 'u' */ {0x00,0x00,0x00,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC7,0x3B,0x00,0x00,0x00,0x00,0x00},
/* 'i' */ {0x00,0x18,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00},
/* ' ' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
};
static const uint8_t *get_font_ptr(char c)
{
if(c >= '0' && c <= '9') return font_8x16_digits[c - '0'];
switch(c) {
case '.': return font_8x16_misc[0];
case '-': return font_8x16_misc[1];
case '%': return font_8x16_misc[2];
case ':': return font_8x16_misc[3];
case 'C': return font_8x16_misc[4];
case 'c': return font_8x16_misc[5];
case 'T': return font_8x16_misc[6];
case 'e': return font_8x16_misc[7];
case 'm': return font_8x16_misc[8];
case 'p': return font_8x16_misc[9];
case 'H': return font_8x16_misc[10];
case 'u': return font_8x16_misc[11];
case 'i': return font_8x16_misc[12];
case ' ': return font_8x16_misc[13];
default: return font_8x16_misc[13];
}
}
/* ------------------------- framebuffer ------------------------- */
static void fb_set_pixel(int x,int y,int color)
{
if(x<0||x>=EPD_WIDTH||y<0||y>=EPD_HEIGHT) return;
int byte_index = (x+y*EPD_WIDTH)/8;
int bit = 7-(x&7);
if(color) frame_buf[byte_index] &= ~(1<<bit);
else frame_buf[byte_index] |= (1<<bit);
}
static void fb_clear(uint8_t white)
{
memset(frame_buf, white?0xFF:0x00, EPD_BUFFER_BYTES);
}
static void fb_draw_char8x16(int x,int y,char c)
{
const uint8_t *p = get_font_ptr(c);
for(int row=0;row<16;row++){
uint8_t bits = p[row];
for(int col=0;col<8;col++){
int bit=(bits&(0x80>>col))?1:0;
fb_set_pixel(x+col,y+row,bit);
}
}
}
static void fb_draw_string8x16(int x,int y,const char *s)
{
while(*s){
fb_draw_char8x16(x,y,*s);
x+=8; s++;
}
}
/* ------------------------- SPI wrapper ------------------------- */
static void spd_spi_write_cmd(uint8_t cmd)
{
uint8_t d=cmd;
EPD_DC_CMD(); EPD_CS_LOW();
HAL_SPI_Transmit(&hspi1,&d,1,100);
EPD_CS_HIGH();
}
static void spd_spi_write_data(uint8_t data)
{
uint8_t d=data;
EPD_DC_DATA(); EPD_CS_LOW();
HAL_SPI_Transmit(&hspi1,&d,1,100);
EPD_CS_HIGH();
}
static void epd_wait_busy(void)
{
uint32_t timeout=5000;
while(EPD_IS_BUSY() && timeout--){
HAL_Delay(1);
}
}
static void epd_init(void)
{
spd_spi_write_cmd(0x12); // soft reset
HAL_Delay(10); epd_wait_busy();
spd_spi_write_cmd(0x01);
spd_spi_write_data((EPD_HEIGHT-1)&0xFF);
spd_spi_write_data(((EPD_HEIGHT-1)>>8)&0xFF);
spd_spi_write_data(0x00);
spd_spi_write_cmd(0x0C);
spd_spi_write_data(0xD7);
spd_spi_write_data(0xD6);
spd_spi_write_data(0x9D);
spd_spi_write_cmd(0x2C);
spd_spi_write_data(0xA8);
spd_spi_write_cmd(0x3A);
spd_spi_write_data(0x1A);
spd_spi_write_cmd(0x3B);
spd_spi_write_data(0x08);
spd_spi_write_cmd(0x11);
spd_spi_write_data(0x03);
HAL_Delay(10);
}
static void epd_display_frame(const uint8_t *buf)
{
spd_spi_write_cmd(0x24);
for(int i=0;i<EPD_BUFFER_BYTES;i++) spd_spi_write_data(buf[i]);
spd_spi_write_cmd(0x20);
epd_wait_busy();
}
static void epd_clear(void)
{
fb_clear(1);
epd_display_frame(frame_buf);
}
/* ------------------------- AHT10 ------------------------- */
static uint8_t aht10_read(I2C_HandleTypeDef *hi2c,float *t,float *h)
{
uint8_t cmd[3]={AHT10_CMD_TRIGGER,AHT10_CMD_ARG1,AHT10_CMD_ARG2};
uint8_t data[6];
if(HAL_I2C_Master_Transmit(hi2c,AHT10_ADDR,cmd,3,100)!=HAL_OK) return 1;
HAL_Delay(80);
if(HAL_I2C_Master_Receive(hi2c,AHT10_ADDR,data,6,100)!=HAL_OK) return 2;
uint32_t raw_h=((uint32_t)data[1]<<12)|((uint32_t)data[2]<<4)|((data[3]&0xF0)>>4);
uint32_t raw_t=(((uint32_t)(data[3]&0x0F))<<16)|((uint32_t)data[4]<<8)|data[5];
*h=(raw_h/1048576.0f)*100.0f;
*t=(raw_t/1048576.0f)*200.0f-50.0f;
return 0;
}
/* ------------------------- UI ------------------------- */
static void draw_temp_hum_ui(float t,float h)
{
char line1[32],line2[32];
fb_clear(1);
snprintf(line1,sizeof(line1),"temp: %.1f C",t);
snprintf(line2,sizeof(line2),"humi: %.1f %%",h);
fb_draw_string8x16(8,24,line1);
fb_draw_string8x16(8,48,line2);
epd_display_frame(frame_buf);
}
/* ------------------------- main ------------------------- */
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C2_Init();
MX_SPI1_Init();
// reset EPD
EPD_RES_HIGH(); HAL_Delay(10);
EPD_RES_LOW(); HAL_Delay(20);
EPD_RES_HIGH(); HAL_Delay(20);
epd_init();
epd_clear();
while(1)
{
if(aht10_read(&hi2c2,&g_temperature,&g_humidity)==0){
draw_temp_hum_ui(g_temperature,g_humidity);
}
HAL_Delay(3000);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// ??????? HSE,HSI ????
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 8MHz HSE -> 72MHz SYSCLK
if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|
RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* CubeMX generated SystemClock_Config and Error_Handler already present */
void Error_Handler(void){__disable_irq();while(1){}}
代码哪里有问题
最新发布