本人非常荣幸参与了中科蓝讯AB32VG1 RISC-V开发板模块的评测任务活动并成功抢到测试SPI模块的机会,在此感谢主办方给予我这次机会。
说起这块板子,中规中矩。如下图1所示,AB32VG1 RISC-V开发板实物图。
图1 AB32VG1 RISC-V开发板实物图
AB32VG1 RISC-V开发板板载硬件资源相对还是丰富的,我主要申请的测试SPI任务。翻看了AB32VG1 RISC-V reg 手册,看到里面注明是有硬件SPI功能的(不过,我没有看的太明白相关描述)。
图2 AB32VG1 reg手册
这里,我还是按照任务软件SPI来进行测试工作的,如何验证软件SPI功能,那就需要借助一个SPI接口的设备了。我使用的是1.54寸墨水屏模块来验证软件SPI功能。其连接关系如下表所示。
表1 AB32VG1开发板引脚与墨水屏模块引脚连接关系
为了保证测试工作能够顺利进行,我先对这几个引脚能否工作进行了测试。用杜邦线依次将上表中(除了PF.1)引脚与彩色LED中的LED-R引脚连接起来,然后依次用点灯程序测试,除了PA.7感觉驱动弱一点外,均可以正常工作。印象看寄存器手册时,可以设置上拉阻值的,还是没有静下心来,没有尝试更改,只是将PA.0和PA.7连接线对调了一下。
经过近2周的测试工作(这里需要说明一下,其实工作就是将墨水屏的arduino代码改写一下,我开始觉得工作难度不大,但是实际效果很差,而且没有成功驱动墨水屏,感觉很失败,稍后总结一下原因),现在将自己写的代码列出来,希望大家帮我找找问题所在(这里只贴出初始化部分,显示部分就不贴了。)
// Display resolution
#define EPD_WIDTH 152
#define EPD_HEIGHT 152
// EPD1IN54B commands
#define PANEL_SETTING 0x00
#define POWER_SETTING 0x01
#define POWER_OFF 0x02
#define POWER_OFF_SEQUENCE_SETTING 0x03
#define POWER_ON 0x04
#define POWER_ON_MEASURE 0x05
#define BOOSTER_SOFT_START 0x06
#define DEEP_SLEEP 0x07
#define DATA_START_TRANSMISSION_1 0x10
#define DATA_STOP 0x11
#define DISPLAY_REFRESH 0x12
#define DATA_START_TRANSMISSION_2 0x13
#define PLL_CONTROL 0x30
#define TEMPERATURE_SENSOR_COMMAND 0x40
#define TEMPERATURE_SENSOR_CALIBRATION 0x41
#define TEMPERATURE_SENSOR_WRITE 0x42
#define TEMPERATURE_SENSOR_READ 0x43
#define VCOM_AND_DATA_INTERVAL_SETTING 0x50
#define LOW_POWER_DETECTION 0x51
#define TCON_SETTING 0x60
#define TCON_RESOLUTION 0x61
#define SOURCE_AND_GATE_START_SETTING 0x62
#define GET_STATUS 0x71
#define AUTO_MEASURE_VCOM 0x80
#define VCOM_VALUE 0x81
#define VCM_DC_SETTING_REGISTER 0x82
#define PROGRAM_MODE 0xA0
#define ACTIVE_PROGRAM 0xA1
#define READ_OTP_DATA 0xA2
//PA0、PA1、PA2、PE4、PA6
/--------------------引脚定义--------------------------/
uint8_t SPI_CS;//低电平有效
uint8_t SPI_SCK;
uint8_t SPI_MOSI;
uint8_t SPI_MISO;
uint8_t OLED_DC;//数据/命令控制引脚(高电平表示数据,低电平表示命令)
uint8_t OLED_RES;//外部复位引脚(低电平复位)
uint8_t OLED_BUSY ;//工作状态输出引脚(高电平表示正在工作)
#define SPI_CS_HIGH rt_pin_write(SPI_CS, PIN_HIGH)
#define SPI_CS_LOW rt_pin_write(SPI_CS, PIN_LOW)
#define SPI_SCK_HIGH rt_pin_write(SPI_SCK, PIN_HIGH)
#define SPI_SCK_LOW rt_pin_write(SPI_SCK, PIN_LOW)
#define SPI_MOSI_HIGH rt_pin_write(SPI_MOSI, PIN_HIGH)
#define SPI_MOSI_LOW rt_pin_write(SPI_MOSI, PIN_LOW)
#define OLED_DC_HIGH rt_pin_write(OLED_DC, PIN_HIGH)
#define OLED_DC_LOW rt_pin_write(OLED_DC, PIN_LOW)
#define OLED_RES_HIGH rt_pin_write(OLED_RES, PIN_HIGH)
#define OLED_RES_LOW rt_pin_write(OLED_RES, PIN_LOW)
/definition--------------------------------------------/
const unsigned char lut_vcom0[] = {
0x0E, 0x14, 0x01, 0x0A, 0x06, 0x04, 0x0A, 0x0A,
0x0F, 0x03, 0x03, 0x0C, 0x06, 0x0A, 0x00
};
const unsigned char lut_w[] = {
0x0E, 0x14, 0x01, 0x0A, 0x46, 0x04, 0x8A, 0x4A,
0x0F, 0x83, 0x43, 0x0C, 0x86, 0x0A, 0x04
};
const unsigned char lut_b[] = {
0x0E, 0x14, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A,
0x0F, 0x83, 0x43, 0x0C, 0x06, 0x4A, 0x04
};
const unsigned char lut_g1[] = {
0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A,
0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04
};
const unsigned char lut_g2[] = {
0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A,
0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04
};
const unsigned char lut_vcom1[] = {
0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char lut_red0[] = {
0x83, 0x5D, 0x01, 0x81, 0x48, 0x23, 0x77, 0x77,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char lut_red1[] = {
0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/*******************************************************************************
函数名称: void spi_send_byte(uint8_t value)
函数功能: 发送8bit数据
函数说明: gpio模拟spi
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void spi_send_byte(uint8_t value)
{
uint8_t i;
uint8_t data=0;
SPI_SCK_LOW; //先将时钟线拉低
for(i=0;i<8;i++)
{
if((value&0x80)==0x80) //从高位发送
{
SPI_MOSI_HIGH;
}
else
{
SPI_MOSI_LOW;
}
SPI_SCK_HIGH; //将时钟线拉高,在时钟上升沿,数据发送到从设备
value<<=1;
//data<<=1;
//if(rt_pin_read(SPI_MISO)) //读取从设备发射的数据
//{
// data|=0x01;
//}
SPI_SCK_LOW; //在下降沿数据被读取到主机
}
return data; //返回读取到的数据
}
/*******************************************************************************
函数名称: void spi_init(void)
函数功能: gpio模拟spi端口初始化
函数说明: gpio模拟spi
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void spi_init(void)
{
rt_pin_mode(SPI_CS, PIN_MODE_OUTPUT);
rt_pin_mode(SPI_SCK, PIN_MODE_OUTPUT);
rt_pin_mode(SPI_MOSI, PIN_MODE_OUTPUT);
//rt_pin_mode(SPI_MISO, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(OLED_DC, PIN_MODE_OUTPUT);
rt_pin_mode(OLED_RES, PIN_MODE_OUTPUT);
rt_pin_mode(OLED_BUSY, PIN_MODE_INPUT);
}
/*******************************************************************************
函数名称: void EPD_reset(void)
函数功能: 复位EPD屏
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_reset(void)
{
SPI_CS_HIGH;
rt_thread_mdelay(200);
SPI_CS_LOW;
rt_thread_mdelay(200);
SPI_CS_HIGH;
rt_thread_mdelay(200);
}
/*******************************************************************************
函数名称: void EPD_send_cmd(uint8_t cmd)
函数功能: 向EPD屏发送命令
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_send_cmd(uint8_t cmd)
{
OLED_DC_LOW;
SPI_CS_LOW;
spi_send_byte(cmd);
SPI_CS_HIGH;
}
/*******************************************************************************
函数名称: void EPD_send_data(uint8_t data)
函数功能: 向EPD屏发送数据
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_send_data(uint8_t data)
{
OLED_DC_HIGH;
SPI_CS_LOW;
spi_send_byte(data);
SPI_CS_HIGH;
}
/******************************************************************************* 函数名称: void EPD_wait_2_idle(void)
函数功能: 等待EPD屏空闲
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_wait_2_idle(void)
{
while(rt_pin_read(OLED_BUSY) == 0) { //LOW: busy, HIGH: idle
rt_thread_mdelay(200);
rt_kprintf("\n--ddddd'");//目前定位的问题所在,不知道是引脚问题还是协议问题,一直读取为高电平。主要手头没有示波器,无法抓取信号。另外,这种兼容arduino、树莓派方式的母排座方式,令人也不习惯。连接墨水屏,还得找公母对的杜邦线。
}
}
/*******************************************************************************
函数名称: void EPD_set_lut_bw(void)
函数功能: 将EPD屏设置黑白模式
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_set_lut_bw(void)
{
uint32_t count;
EPD_send_cmd(0x20); //g vcom
for(count = 0; count < 15; count++) {
EPD_send_data(lut_vcom0[count]);
}
EPD_send_cmd(0x21); //g ww --
for(count = 0; count < 15; count++) {
EPD_send_data(lut_w[count]);
}
EPD_send_cmd(0x22); //g bw r
for(count = 0; count < 15; count++) {
EPD_send_data(lut_b[count]);
}
EPD_send_cmd(0x23); //g wb w
for(count = 0; count < 15; count++) {
EPD_send_data(lut_g1[count]);
}
EPD_send_cmd(0x24); //g bb b
for(count = 0; count < 15; count++) {
EPD_send_data(lut_g2[count]);
}
}
/*******************************************************************************
函数名称: void EPD_set_lut_r(void)
函数功能: 将EPD屏设置红色模式
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_set_lut_r(void)
{
uint32_t count;
EPD_send_cmd(0x25);
for(count = 0; count < 15; count++) {
EPD_send_data(lut_vcom1[count]);
}
EPD_send_cmd(0x26);
for(count = 0; count < 15; count++) {
EPD_send_data(lut_red0[count]);
}
EPD_send_cmd(0x27);
for(count = 0; count < 15; count++) {
EPD_send_data(lut_red1[count]);
}
}
/*******************************************************************************
函数名称: void EPD_init(void)
函数功能: 初始化EPD屏
函数说明:
输入参数:
输出参数:
返回值 :
*******************************************************************************/
uint8_t EPD_init(void)
{
EPD_reset();
EPD_send_cmd(POWER_SETTING);
EPD_send_data(0x07);
EPD_send_data(0x00);
EPD_send_data(0x08);
EPD_send_data(0x00);
EPD_send_cmd(BOOSTER_SOFT_START);
EPD_send_data(0x07);
EPD_send_data(0x07);
EPD_send_data(0x07);
EPD_send_cmd(POWER_ON);
EPD_wait_2_idle();
EPD_send_cmd(PANEL_SETTING);
EPD_send_data(0x0f);
EPD_send_data(0x0d);
EPD_send_cmd(VCOM_AND_DATA_INTERVAL_SETTING);
EPD_send_data(0xF7);
EPD_send_cmd(PLL_CONTROL);
EPD_send_data(0x39);
EPD_send_cmd(TCON_RESOLUTION); //set x and y
EPD_send_data(EPD_WIDTH & 0xff); //x
EPD_send_data(EPD_HEIGHT >> 8); //y High eight
EPD_send_data(EPD_HEIGHT & 0xff); //y Low eight
EPD_send_cmd(VCM_DC_SETTING_REGISTER); //VCOM
EPD_send_data(0x0E);
EPD_set_lut_bw();
EPD_set_lut_r();
return 0;
}
/*******************************************************************************
函数名称: void EPD_clear(void)
函数功能: 清EPD屏
函数说明: gpio模拟spi
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_clear(void)
{
uint32_t Width, Height;
Width = (EPD_WIDTH % 8 == 0)? (EPD_WIDTH / 8 ): (EPD_WIDTH / 8 + 1);
Height = EPD_HEIGHT;
//send black data
EPD_send_cmd(DATA_START_TRANSMISSION_1);
for(uint32_t i = 0; i < Height; i++) {
for(uint32_t i = 0; i < Width; i++) {
EPD_send_data(0xFF);
}
}
//send red data
EPD_send_cmd(DATA_START_TRANSMISSION_2);
for(uint32_t i = 0; i < Height; i++) {
for(uint32_t i = 0; i < Width; i++) {
EPD_send_data(0xFF);
}
}
EPD_send_cmd(DISPLAY_REFRESH);
EPD_wait_2_idle();
}
/*******************************************************************************
函数名称: void EPD_display(const uint8_t *blackimage, const uint8_t *redimage)
函数功能: 显示一副图片
函数说明: gpio模拟spi
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_display(const uint8_t *blackimage, const uint8_t *redimage)
{
uint32_t Width, Height;
Width = (EPD_WIDTH % 8 == 0)? (EPD_WIDTH / 8 ): (EPD_WIDTH / 8 + 1);
Height = EPD_HEIGHT;
EPD_send_cmd(DATA_START_TRANSMISSION_1);
for (uint32_t j = 0; j < Height; j++) {
for (uint32_t i = 0; i < Width; i++) {
EPD_send_data(blackimage[i + j * Width]);
}
}
EPD_send_cmd(DATA_START_TRANSMISSION_2);
for (uint32_t j = 0; j < Height; j++) {
for (uint32_t i = 0; i < Width; i++) {
EPD_send_data(redimage[i + j * Width]);
}
}
//Display refresh
EPD_send_cmd(DISPLAY_REFRESH);
EPD_wait_2_idle();
}
/*******************************************************************************
函数名称: void EPD_sleep(void)
函数功能: 进入睡眠模式
函数说明: gpio模拟spi
输入参数:
输出参数:
返回值 :
*******************************************************************************/
void EPD_sleep(void)
{
EPD_send_cmd(VCOM_AND_DATA_INTERVAL_SETTING);
EPD_send_data(0x17);
EPD_send_cmd(VCM_DC_SETTING_REGISTER); //to solve Vcom drop
EPD_send_data(0x00);
EPD_send_cmd(POWER_SETTING); //power setting
EPD_send_data(0x02); //gate switch to external
EPD_send_data(0x00);
EPD_send_data(0x00);
EPD_send_data(0x00);
EPD_wait_2_idle();
EPD_send_cmd(POWER_OFF); //power off
}
int main(void)
{
uint32_t cnt = 0;
uint8_t pin = rt_pin_get("PE.1");
rt_pin_mode(pin, PIN_MODE_OUTPUT);
SPI_CS = rt_pin_get("PA.0");//低电平有效
SPI_SCK = rt_pin_get("PA.1");
SPI_MOSI = rt_pin_get("PF.0");
SPI_MISO = rt_pin_get("PA.7");
OLED_DC = rt_pin_get("PF.0");//数据/命令控制引脚(高电平表示数据,低电平表示命令)
OLED_RES = rt_pin_get("PA.5");//外部复位引脚(低电平复位)
OLED_BUSY = rt_pin_get("PF.1");//工作状态输出引脚(高电平表示正在工作)
spi_init();
rt_kprintf("Reading.....");
if(EPD_init()) {
rt_kprintf("e-Paper init failed\r\n");
}
EPD_clear();
rt_thread_mdelay(200);
while (1)
{
if (cnt % 2 == 0) {
rt_pin_write(pin, PIN_LOW);
} else {
rt_pin_write(pin, PIN_HIGH);
}
cnt++;
rt_thread_mdelay(1000);
}
return 0;
}
希望有大侠帮我释疑,或提出解决方案。
下来谈谈我的使用感受:从硬件角度说,AB32VG1开发板功能还是非常强大,我目前还只是触摸一下冰山的一角,相信其强大的功能会在今后项目中发挥出来。
但测试过程中由于中科蓝讯方没有提供X_LINK模块功能(开发板原理图上看到此模块,虽然不知道它具有哪些功能,但从名字看,应该功能不必J_LINK差吧。)令我感到非常遗憾,以及在测试工作中感到非常痛苦,让我不停的在RT Studio IDE、downloader和串口putty间来回切换,导致工作效率非常低下。
如果AB32VG1开发板能支持像J-Link在IDE环境中单步调试、查看var等功能,那么相信基于该板子的开发工作会更高的,也能更好的定位问题所在。
原作者:mabo124