【前言】

LCD屏是单片机常用的外设。GC9306是一款240*320的彩色显示屏,这次是用spi1驱动它,也是我学习AT32F425的spi外设的题目之一。

【步聚】

1、先下载查看了AT32F425数据手册。

了解spi1的驱动IO是哪些。用于LCD屏的CS、DC、RES、BLK的普通IO是如何驱动的。

2、查看了spi1的驱动示例,学习如何配置spi1为主从模式。

3、上网学习其他网友的驱动LCD屏的例子。学习他们配置外设的方法。

4、用我原来在CH582M的驱动拷贝过来用在AT32F425的工程上。

【具体过程】

1、在RT_Thread Studio的工程目录下面新建一个drives的工程目录,把我原来用在CH582上面的5个文件lcd_init.c、lcd_init.h、lcd.c、lcd.h、lcdfont.h拷贝到这个目录下面。其实单片机学习就象堆积木,用的外设多了,哪怕换一款芯片,使用起来也方便,使用起来没有象以前从头开始、查资料、找驱动===。

2、根据上次驱动gc9306的经验,拷贝文件过来,只需要修改几个参数就行了:

  • 1、屏的init,这里主要是改写spi发送数据的这个LCD_Init函数。

  • 2、修改DC、RES、CS、BLK四个IO的驱动(宏配置)。

1)定义驱动的IO如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42


//  生成日期   : 2022-3-27


//  最近修改   :


//  功能描述   :AT32F425 GC9306


//              说明:


//              ----------------------------------------------------------------


//              GND   电源地


//              VCC   3.3v电源


//              SCL   PA5(SCLK)


//              SDA   PA7(MOSI)


//              RES   PB0                //复位


//              DC    PB1                //命令、数据选择位


//              CS    PA4                //spi片选


//              BLK   PB2                //背光


//              ----------------------------------------------------------------

2)配置spi1的时钟、GPIO等详细见代码注释:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231


void LCD_GPIO_Init(void)


{


   gpio_init_type gpio_init_struct;


   spi_init_type spi_init_struct;


    /* 使能GPIOA GPIOB时钟 */


   crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);


   crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);





/* set default parameter */


   gpio_default_para_init(&gpio_init_struct);





#if SOFTWARE_SPI_ENABLE        //为模拟spi





        /* configure the led gpio */


        gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; //加强的输出能力  跟CH582的 输出20mA与5mA的味道差不多,或者是STM32的推挽输出差不多吧。


        gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;             //上拉输出


        gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;                       //输出模式


        gpio_init_struct.gpio_pins = GPIO_PINS_4|GPIO_PINS_5|GPIO_PINS_7;    //选择输出的IO 分别为4、5、7脚  是不是由于专利的问题 我看到这个STM32:GPIO_PIN_0,CH582的GPIO_Pin_0


        gpio_init_struct.gpio_pull = GPIO_PULL_NONE;                        //floating for input, no pull for output 也就是stm32的floating的吧


        gpio_init(GPIOA, &gpio_init_struct);                        


        gpio_bits_set(GPIOA,GPIO_PINS_4|GPIO_PINS_5|GPIO_PINS_7);          //初始为高电平





#else    //为硬件spi   


        gpio_init_struct.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;


        gpio_init_struct.gpio_pull           = GPIO_PULL_UP;


        gpio_init_struct.gpio_mode           = GPIO_MODE_OUTPUT;


        gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;


        gpio_init_struct.gpio_pins = GPIO_PINS_4;


        gpio_init(GPIOA, &gpio_init_struct);


  


        gpio_bits_set(GPIOA,GPIO_PINS_4);





        gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_0);  //这个muxing 百度翻译也整不明白,是不是管角复用的意思?看到数据手册是乎是5的第1功能为SPI1


        gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_0);


        gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_0);


        gpio_default_para_init(&gpio_init_struct);





        gpio_init_struct.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;





        gpio_init_struct.gpio_pull           = GPIO_PULL_DOWN;


        gpio_init_struct.gpio_mode           = GPIO_MODE_MUX;


        gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;


        gpio_init_struct.gpio_pins = GPIO_PINS_5  ;


        gpio_init(GPIOA, &gpio_init_struct);





        gpio_init_struct.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;


        gpio_init_struct.gpio_pull           = GPIO_PULL_UP;


        gpio_init_struct.gpio_mode           = GPIO_MODE_MUX;


        gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;


        gpio_init_struct.gpio_pins = GPIO_PINS_7 | GPIO_PINS_6;


        gpio_init(GPIOA, &gpio_init_struct);





        crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);   //使能SPI1的时钟


  


        spi_default_para_init(&spi_init_struct);               //复位SPI1


        spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX;    //单线双向半双工模式传输(slben=1和slbtd=1)  


        spi_init_struct.master_slave_mode = SPI_MODE_MASTER;                //主机模式


        spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2;                //2分频,有空测一下频率是多少


        spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;         //MSB 高位在前?   


        spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;                     //一帧为8位


        spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;  //sck在空闲状态下保持高


        spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;       //数据捕获从第二个时钟边缘开始


        spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;  //CS由软件控制


        spi_init(SPI1, &spi_init_struct);





        spi_enable(SPI1, TRUE);


  


#endif  


        //配置DC、RES、BLK


        gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;


        gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;


        gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;


        gpio_init_struct.gpio_pins = GPIO_PINS_0|GPIO_PINS_1|GPIO_PINS_2;


        gpio_init_struct.gpio_pull = GPIO_PULL_NONE;


        gpio_init(GPIOB, &gpio_init_struct);   


        gpio_bits_set(GPIOB,GPIO_PINS_0|GPIO_PINS_1|GPIO_PINS_2);


} 

3)改发送函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93


/******************************************************************************


      函数说明:LCD串行数据写入函数


      入口数据:dat  要写入的串行数据


      返回值:  无


******************************************************************************/


void LCD_Writ_Bus(uint8_t dat)


{





  gpio_bits_reset( GPIOA, GPIO_PINS_4 ); //CS拉低,开始发送数数


	#if SOFTWARE_SPI_ENABLE        //模拟发送数据


        u8 i;


        for(i=0;i<8;i++)


        {                  


                LCD_SCLK_LOW();


                if(dat&0x80)


                {


                   LCD_MOSI_HIGH();


                }


                else


                {


                   LCD_MOSI_LOW();


                }


                LCD_SCLK_HIGH();


                dat<<=1;


        }


#else   //使用硬件SPI发送


  spi_i2s_data_transmit(SPI1, dat ); //发送数据


  while(spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET) {};  //等待发送完成,我感觉以后要加超时判断,要不会出问题会阻塞在这里


  gpio_bits_set(GPIOA, GPIO_PINS_4 );  //CS释放


#endif 


}

4)修改DC、RES、BLK的宏配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72


#define RES_PIN  GPIO_PINS_0


#define DC_PIN   GPIO_PINS_1


#define BLK_PIN  GPIO_PINS_2





#define CS_PIN   GPIO_PINS_4


//-----------------LCD端口定义----------------





#define LCD_CS_Clr   gpio_bits_reset(GPIOA, CS_PIN)//CS


#define LCD_CS_Set   gpio_bits_set(GPIOA, CS_PIN)





#define LCD_RES_Clr  gpio_bits_reset(GPIOB, RES_PIN)//RES


#define LCD_RES_Set  gpio_bits_set(GPIOB, RES_PIN)





#define LCD_DC_Clr   gpio_bits_reset(GPIOB, DC_PIN)//DC


#define LCD_DC_Set   gpio_bits_set(GPIOB,DC_PIN)





#define LCD_BLK_Clr  gpio_bits_reset(GPIOB,BLK_PIN)//BLK


#define LCD_BLK_Set  gpio_bits_set(GPIOB,BLK_PIN)





#define LCD_SCLK_LOW() gpio_bits_reset(GPIOA, GPIO_PINS_5)


#define LCD_SCLK_HIGH() gpio_bits_set(GPIOA, GPIO_PINS_5)





#define LCD_MOSI_LOW() gpio_bits_reset(GPIOA, GPIO_PINS_7)


#define LCD_MOSI_HIGH() gpio_bits_set(GPIOA, GPIO_PINS_7)

5)修改延时函数为rt_thread的rt_thread_mdelay()函数。

编译通过在main函数中添加头文件与显示函数,编译通过就OK了:

总结

【感受】AT32F425是ARM M4的核心,使用起来跟STM32、N32G45流程是一样的。这次驱动GC9306还是挺轻松的。不过用过沁恒的CH582M后,感觉ARM 配置外设相双RISC-V就复杂很多。AT32F425的在RT_Thread 驱动LCD很流畅。

【建议】AT32F425没有中文固件库文档,建议官方的固件库文档有中英文的。还有就是开发板的针脚排列不是按顺序的,印刷的字体很小、而且的斜的,特别是夹在中间的,看起来很吃力,建议如果有下一板适当的调整,提高一下舒适度。

项目代码

http://bbs.eeworld.com.cn/forum.php?mod=attachment&aid=NTk2Mzc4fGQ0MzZlZGEwfDE2NzU5MjQyNTZ8MTM4MDMzNXwxMTk4MDE0

原文:【新提醒】【AT-START-F425测评】SPI驱动LCD屏 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)