static struct spi_driver spi_ssd1306_driver = {
.driver = {
.name = "spi_ssd1306",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = spi_ssd1306_probe,
.remove = __devexit_p(spi_ssd1306_remove),
};
static int spi_ssd1306_init(void)
{
return spi_register_driver(&spi_ssd1306_driver);
}
到这里,基本工作已经完成。怎样驱动ssd1306 OLED呢?
ssd1306 OLED的使用方法,请参考相关的手册。
本例提供的ssd1306 OLED驱动,只需要我们提供一个基本9位spi数据收发的接口即可。
static void ssd1306_write_byte(uint8_t chData, uint8_t chCmd)
{
struct spi_transfer t;
struct spi_message m;
uint16_t data = chData;
/* [cgw]: 情况spi_transfer */
memset(&t,0,sizeof(struct spi_transfer));
/* [cgw]: 第9位表示前8位是命令还是数据,1:数据,0:命令 */
if (chCmd) {
data |= (1 << 8);
} else {
data &= ~(1 << 8);
}
/* [cgw]: 要发送的数据 */
t.tx_buf = &data;
/* [cgw]: 长度,2字节 */
t.len = 2;
/* [cgw]: 9位spi */
t.bits_per_word = 9;
//t.cs_change = 1;
/* [cgw]: 把数据添加到收发列表,工作队列调度时会从收发队列中取出,并进行收发
* 注意这里并没有直接收发
*/
spi_message_init(&m);
spi_message_add_tail(&t, &m);
spi_sync(spi_ssd1306_dev, &m);
}
注意,在网上看到一些例子,用8位模式驱动ssd1306 OLED的,需要用DC的状态来表示数据或命令的,他们的做法如下:
void ssd1306_write_cmd(uint8_t cmd)
{
ssd1306_dc_clr();
spi_write(cmd);
ssd1306_dc_set();
}
void ssd1306_write_data(uint8_t data)
{
ssd1306_dc_set();
spi_write(data);
ssd1306_dc_clr();
}
我本人认为是不正确的,至少不符合这个spi框架的逻辑,因为spi数据的收发并不是直接在spi_write()实现,而是在工作队列bitbang_work()中实现。尽管这样仍然能驱动ssd1306 OLED,但理论上不应该这么做。要改的话应该改bitbang_work()中改,添加DC状态的控制。
static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
/* [cgw]: 队列不为空 */
while (!list_empty(&bitbang->queue)) {
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
struct spi_transfer *t = NULL;
unsigned tmp;
unsigned cs_change;
int status;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
/* [cgw]: 取出spi_message */
m = container_of(bitbang->queue.next, struct spi_message,
queue);
/* [cgw]: 删除这个节点 */
list_del_init(&m->queue);
/* [cgw]: 进入临界区 */
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
* word-at-a-time bitbang code, and presumably chipselect()
* should enforce these requirements too?
*/
nsecs = 100;
spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
setup_transfer = NULL;
/* [cgw]: 历遍spi_message中的收发列表 */
list_for_each_entry (t, &m->transfers, transfer_list) {