03/28
13:40
技術(shù)控Linux GPIO Key 驅動(dòng)的加載無(wú)回復gpio-keys是基于input子系統實(shí)現的一個(gè)通用的GPIO按鍵驅動(dòng),基于platform來(lái)實(shí)現,位于drivers/input/keyboard/gpio_keys.c,這個(gè)文件是硬件無(wú)關(guān)的,而硬件有關(guān)的需要我們自己來(lái)注冊.進(jìn)入這個(gè)gpio_keys.c這個(gè)函數,第一步就是初始化.
C/C++
4 Lines
1
2
3
4
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
然后加載這個(gè)結構體:
C/C++
11 Lines
1
2
3
4
5
6
7
8
9
10
11
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &gpio_keys_pm_ops,
#endif
}
};
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
進(jìn)入后會(huì )執行probe函數,進(jìn)行設備的probe.當然只是注冊設備,沒(méi)什么必要看.還比如gpio_keys_isr就是去抖動(dòng)檢測,這是上半部分函數.
C/C++
15 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
struct gpio_keys_button *button = bdata->button;
BUG_ON(irq != gpio_to_irq(button->gpio));
if (button->debounce_interval)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(button->debounce_interval));
else
schedule_work(&bdata->work);
return IRQ_HANDLED;
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
然后由定時(shí)器在超時(shí)時(shí)候,觸發(fā)的下半部分.
C/C++
7 Lines
1
2
3
4
5
6
7
static void gpio_keys_work_func(struct work_struct *work)
{
struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work);
gpio_keys_report_event(bdata);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
既然gpio_keys這么簡(jiǎn)單,那么看看我們如何綁定.在此之前,先打開(kāi)相應的頭文件.
C/C++
22 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef _GPIO_KEYS_H
#define _GPIO_KEYS_H
struct gpio_keys_button {
/* Configuration parameters */
int code; /* input event code (KEY_*, SW_*) */
int gpio;
int active_low;
char *desc;
int type; /* input event type (EV_KEY, EV_SW) */
int wakeup; /* configure the button as a wake-up source */
int debounce_interval; /* debounce ticks interval in msecs */
bool can_disable;
};
struct gpio_keys_platform_data {
struct gpio_keys_button *buttons;
int nbuttons;
unsigned int rep:1; /* enable input subsystem auto repeat */
};
#endif
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
其中g(shù)pio_keys_button就是我們要引用到板級相關(guān)文件的一個(gè)重要的結構體,他的每個(gè)字段的意義,挑重點(diǎn)的說(shuō)一說(shuō).
code字段,意思就是對應Linux的按鍵事件,gpio要對應gpio號,active_low是低電平有效,desc是功能描述,debounce_interval是消抖間隔.當然這個(gè)gpio_keys_button最終要關(guān)聯(lián)到gpio_keys_platform_data里,其中nbuttons就是有的按鍵總數.在板級文件中要聲明.比如做2個(gè)引腳,一個(gè)是F1,一個(gè)是F2的功能.
C/C++
18 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static struct gpio_keys_button mx28evk_buttons[] =
{
{
.gpio = MXS_PIN_ENCODE(2, 4), /*K1 */
.code = KEY_F1,
.desc = "Button 1",
.active_low = 1,
},
{
.gpio = MXS_PIN_ENCODE(2, 6), /*K2 */
.code = KEY_F2,
.desc = "Button 2",
.active_low = 1,
},
};
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
然后聲明一個(gè)組合起來(lái)的platform結構.
C/C++
5 Lines
1
2
3
4
5
static struct gpio_keys_platform_data mx28evk_button_data =
{
.buttons = mx28evk_buttons,
.nbuttons = ARRAY_SIZE(mx28evk_button_data),
};
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
最后構建device,因為所有初始化都只識別device.
C/C++
8 Lines
1
2
3
4
5
6
7
8
static struct platform_device mx28evk_button_device =
{
.name = "gpio-keys",
.id = -1,
.dev = {
.platform_data = &mx28evk_button_data,
}
};
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
最后只需要注冊設備,就可以順利使用了.
C/C++
5 Lines
1
2
3
4
5
static struct platform_device *mx28evk_button_device_p[] __initdata = {
&mx28evk_button_device,
};
platform_add_devices(mx28evk_button_device_p,ARRAY_SIZE(mx28evk_button_device_p));
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
03/8
14:52
技術(shù)控LPC4357 官方FAQ無(wú)回復Q NXP的Cortex-M4芯片是雙核,M4和M0兩個(gè)CPU的最高速度分別是多少??jì)烧吖蚕硗瑯拥膬却婧屯庠O資源嗎?
A 兩個(gè)內核均可在高達204MHz的頻率下執行,但并不要求兩個(gè)內核的主頻必須相同,可以使用各自的時(shí)鐘源選擇與分頻設置。兩者可共享片上所有的內存與外設。
Q LPC4300的雙核能做什么用?有應用實(shí)例嗎?
A 可以讓M0響應一些高頻率的中斷,減輕M4的負擔,如同一個(gè)智能DMA,也可以把一些弱運算強度的工作,如I/O,驅動(dòng)程序,一些通信協(xié)議棧與文件存取交由M0處理。典型的應用包括高精密運動(dòng)控制,高端嵌入式音頻,需要處理許多通道的控制器等。
Q LPC4300雙核的調試工具有哪些?怎樣使用?
A 使用常規的ARM開(kāi)發(fā)工具,如MDK,EWARM等即可開(kāi)發(fā),并無(wú)特殊要求。如果要同時(shí)調試兩個(gè)內核的程序,同時(shí)運行兩個(gè)開(kāi)發(fā)工具的進(jìn)程即可。只是要注意需要先讓M4運行起來(lái)并且使它解除M0的復位狀態(tài)。另外,在啟動(dòng)調試會(huì )話(huà)時(shí)也不要把整片復位,以免影響另一個(gè)內核。
Q 什么叫非對稱(chēng)雙核?和對稱(chēng)雙核有什么不同?
A 非對稱(chēng)雙核中兩個(gè)內核的編程模型不同,并且兩個(gè)內核有主核與輔核之分,通常主核的處理能力與規模都高過(guò)輔核。輔核常用于協(xié)助主核完成一些雜項和I/O工作,也可見(jiàn)于完成某個(gè)專(zhuān)項任務(wù)。復位后主核開(kāi)始執行,而輔核要由主核準備好全部執行環(huán)境再由主核啟動(dòng)。主核與輔核間一般不運行同一份二進(jìn)制代碼,不需要專(zhuān)用OS的支持。而對稱(chēng)雙核的兩個(gè)內核編程模型完全相同,地位平等,處理能力與規模也相同,需要專(zhuān)門(mén)的OS支持。近年來(lái)ARM又提出了一種軟件上對稱(chēng),硬件上不對稱(chēng)的bit.LITTLE結構,由兩個(gè)編程模型相同但不同的內核來(lái)實(shí)現(Cortex-A7 + Cortex-A15)。
Q LPC4300和LPC1800只是內核不同嗎?外設和管腳配置都完全一致?
A 為L(cháng)PC18xx的映像可以直接在LPC43xx(尾號一致)上運行,但是LPC4300的片上RAM比LPC1800多64KB,并且LPC1800系列沒(méi)有SGPIO,而LPC4300有
Q SGPIO是什么?和普通的GPIO有什么不同?
A SGPIO是串行GPIO的簡(jiǎn)稱(chēng),只要設置好參數就可以根據時(shí)鐘基準自動(dòng)按照預設的時(shí)序要求,在合適的時(shí)間產(chǎn)生一連串正確的電平跳變,無(wú)需像GPIO那樣全手工操作,這在一定意義上相當于可編程邏輯器件的作用??捎糜趯?shí)現各種自定義的串行總線(xiàn)協(xié)議,也可用于無(wú)需軟件開(kāi)銷(xiāo)地模擬出更多的標準串行外設如UART, SPI等,實(shí)現它們無(wú)需額外的CPU時(shí)間。
Q LPC4300帶浮點(diǎn)運算功能嗎?
A LPC4300使用的是Cortex-M4F內核,F即代表浮點(diǎn),帶單精度浮點(diǎn)指令集以及專(zhuān)用的浮點(diǎn)寄存器組,這是Cortex-M4內核的可選功能。在C語(yǔ)言里直接使用浮點(diǎn)類(lèi)型編譯器即會(huì )自動(dòng)使用這些指令,在匯編中則要手工編寫(xiě)。在使用前需要手動(dòng)使能浮點(diǎn)處理單元。
Q 和通用的DSP芯片相比,LPC4300有什么優(yōu)勢?
A 可以使用同一套開(kāi)發(fā)工具,無(wú)需再使用MCU+DSP的雙芯片方案,流行的ARM架構和強大的ARM生態(tài)系統,繼承了Cortex-M的強大控制能力。簡(jiǎn)單地說(shuō)就是珠聯(lián)璧合,最適合數字信號控制(DSC)領(lǐng)域
Q LPC4300適合做哪些應用?
A 除了能想到的強大的MCU可勝任的一般應用外,專(zhuān)長(cháng)是需要乘加運算、SIMD運算,以及浮點(diǎn)運算的應用,多見(jiàn)于圖形圖像,音頻,運動(dòng)控制,智能控制等
Q 在Cortex-M3上編譯好的程序,在Cortex-M4上可以直接運行嗎?
A 從內核的視角來(lái)看沒(méi)有任何問(wèn)題,Cortex-M4對Cortex-M3完全向下兼容;從芯片級的視角來(lái)看,LPC43xx可運行相同尾號的LPC18xx的程序,但LPC17xx的不可以。
Q M4要啟動(dòng)M0需要做哪些工作
A M0只能把向量表放到零地址。為此M4需要編程一個(gè)專(zhuān)門(mén)的地址映射寄存器,把M0映像的入口地址(如有必要有可能拷貝到另外位置)映射為M0的零地址,然后設置另一個(gè)寄存器解除M0的復位狀態(tài)。這兩個(gè)寄存器都是內存映射的,用起來(lái)和普通的外設寄存器并無(wú)二致
Q M4與M0的映像是不是要分別燒寫(xiě)?
A 可以使用開(kāi)發(fā)工具提供的目標文件轉換功能把M0的映像轉換成C語(yǔ)言數組,放到一個(gè)C源文件中,并且隨M4的工程一起編譯鏈接,這樣就把M0的映像嵌入到M4的映像中了,燒寫(xiě)時(shí)只需燒寫(xiě)M4的映像。
Q M4的DSP與SIMD功能使用起來(lái)容易嗎,要使用這些專(zhuān)用的指令是不是只能靠匯編?
A ARM提供一套相當完整的數學(xué)庫可以使用,有經(jīng)驗的用戶(hù)也可以使用開(kāi)發(fā)工具提供的指令內在函數手工添加。也有第三方提供的針對M4優(yōu)化的科學(xué)計算庫。個(gè)別精悍而關(guān)鍵的底層小函數通常都是經(jīng)過(guò)手工匯編優(yōu)化的,但一般前人都已經(jīng)做了這些工作
Q NXP的M4 MCU是不是必須得兩個(gè)一起用?
A 對于LPC4300系列,如果不釋放M0,就可以無(wú)視它的存在當作單個(gè)M4內核的MCU來(lái)用。另外,NXP也會(huì )提供單個(gè)M4內核的產(chǎn)品
03/5
16:04
技術(shù)控LPC4357 開(kāi)箱并體驗無(wú)回復其實(shí)說(shuō)開(kāi)箱并不對,這是我自己Layout的板子,不過(guò)參考了不少人的例子.下載一下官方的例子.因為有很多個(gè)文件,所以… 提供鏈接:https://www.element14.com/community/docs/DOC-62633/l/element14-core-peripheral-drivers-for-lpc4357-evb-example-1
其他例子,也在附近的連接找的到.
解壓Examples1和Examples2兩個(gè)文件,其中Examples1解壓到當前文件夾,Examples2解壓到下層文件夾.打開(kāi)Gpio LedBlinky的例子.輕松看到直接執行c_entry程序:
C/C++
9 Lines
1
2
3
4
5
6
7
8
9
/* With ARM and GHS toolsets, the entry point is main() - this will
allow the linker to generate wrapper code to setup stacks, allocate
heap area, and initialize and copy code and data segments. For GNU
toolsets, the entry point is through __start() in the crt0_gnu.asm
file, and that startup code will setup stacks and data */
int main(void)
{
return c_entry();
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
因為我是自己Layout的板子,所以需要修改才能用.我自己的用的是P9_4這個(gè)IO,高電平點(diǎn)亮.因為L(cháng)PC這個(gè)MCU,P9_4并不等于GPIO9[4]的,所以還要查手冊.這一點(diǎn)非常不友好.對應關(guān)系如圖,查手冊:
所以設置scu時(shí)候,使用第四個(gè)復用,就GPIO功能,具體實(shí)現.
C/C++
26 Lines
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
#define LED1_BIT 17 //LEDUSB
#define LED1_PORT 5
int c_entry (void) { /* Main Program */
SystemInit();
CGU_Init();
scu_pinmux(0x9 ,4 , MD_PDN, FUNC4); // P8.1 : USB0_IND1 LED
GPIO_SetDir(LED1_PORT,(1<<LED1_BIT), 1);
// M3Frequency is automatically set when SetClock(BASE_M3_CLK... was called.
SysTick_Config(CGU_GetPCLKFrequency(CGU_PERIPHERAL_M4CORE)/1000); // Generate interrupt @ 1000 Hz
GPIO_ClearValue(LED1_PORT,(1<<LED1_BIT));
while (1)
{ // Loop forever
msec = 100;
while(msec);
GPIO_ClearValue(LED1_PORT,(1<<LED1_BIT));
msec = 100;
while(msec);
GPIO_SetValue(LED1_PORT,(1<<LED1_BIT));
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
在下載時(shí)候,默認下載到M4內核,然后看到LED在閃爍.初次體驗到此為止.一直想試試雙核,不過(guò)比較難,而且各種限制,所以先學(xué)一下單核操作.
03/2
14:07
技術(shù)控STMCUBE-MX HAL庫的學(xué)習之Demonstrations粗分析無(wú)回復因為官方針對這個(gè)HAL還是有不少的例子的,比如官方完整的Demo例子就有HAL的版本,其中涵蓋了大多數的函數~
文件夾也有分類(lèi)的,其中來(lái)說(shuō)Middlewares,Application,Drivers三大類(lèi),其中Middlewares,就是中間件,什么是中間件呢,就是不完全底層,不完全上層,屬于一個(gè)銜接.比如USB庫,MSC庫,RTOS以及圖形庫,然后Drivers屬于底層,Application自然是上層應用了,上層應用肯定是自己寫(xiě)的.主要從Drivers/BSP/Components先分析,主要關(guān)心底層的改變,上層的改變都是我們自己應用層的事情,跟底層并沒(méi)什么關(guān)系的.比如以下片段:
C/C++
19 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void I2Cx_Init(void)
{
if(HAL_I2C_GetState(&I2cHandle) == HAL_I2C_STATE_RESET)
{
I2cHandle.Instance = DISCOVERY_I2Cx;
I2cHandle.Init.ClockSpeed = BSP_I2C_SPEED;
I2cHandle.Init.DutyCycle = I2C_DUTYCYCLE_2;
I2cHandle.Init.OwnAddress1 = 0;
I2cHandle.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
I2cHandle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
I2cHandle.Init.OwnAddress2 = 0;
I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
I2cHandle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
/* Init the I2C */
I2Cx_MspInit(&I2cHandle);
HAL_I2C_Init(&I2cHandle);
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
暫且不說(shuō)I2Cx_MspInit是怎么實(shí)現的,而且因為沒(méi)有HAL前綴,也證明這個(gè)東西不是HAL庫的,而HAL_I2C_Init才是HAL庫的.傳入一個(gè)結構體,結構體的定義是如下的:
C/C++
1 Lines
1
I2C_HandleTypeDef I2cHandle;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
換句話(huà)說(shuō)就是I2C_HandleTypeDef,官方文檔里就有這個(gè)定義了.
而對比沒(méi)有HAL庫的代碼:
C/C++
22 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void IOE_I2C_Config(void)
{
I2C_InitTypeDef I2C_InitStructure;
/* If the I2C peripheral is already enabled, don't reconfigure it */
if ((IOE_I2C->CR1 & I2C_CR1_PE) == 0)
{
/* IOE_I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;
/* Initialize the I2C peripheral */
I2C_Init(IOE_I2C, &I2C_InitStructure);
/* Enable the I2C peripheral */
I2C_Cmd(IOE_I2C, ENABLE);
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
對比一看,HAL庫只需要寫(xiě)I2C速度,就可以大致計算,而且感覺(jué)HAL庫更深入底層設置.不過(guò)HAL還有個(gè)優(yōu)點(diǎn),發(fā)送I2C再也不用擔心不會(huì )用了.
C/C++
33 Lines
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
(#) Blocking mode functions are :
(++) HAL_I2C_Master_Transmit()
(++) HAL_I2C_Master_Receive()
(++) HAL_I2C_Slave_Transmit()
(++) HAL_I2C_Slave_Receive()
(++) HAL_I2C_Mem_Write()
(++) HAL_I2C_Mem_Read()
(++) HAL_I2C_IsDeviceReady()
(#) No-Blocking mode functions with Interrupt are :
(++) HAL_I2C_Master_Transmit_IT()
(++) HAL_I2C_Master_Receive_IT()
(++) HAL_I2C_Slave_Transmit_IT()
(++) HAL_I2C_Slave_Receive_IT()
(++) HAL_I2C_Mem_Write_IT()
(++) HAL_I2C_Mem_Read_IT()
(#) No-Blocking mode functions with DMA are :
(++) HAL_I2C_Master_Transmit_DMA()
(++) HAL_I2C_Master_Receive_DMA()
(++) HAL_I2C_Slave_Transmit_DMA()
(++) HAL_I2C_Slave_Receive_DMA()
(++) HAL_I2C_Mem_Write_DMA()
(++) HAL_I2C_Mem_Read_DMA()
(#) A set of Transfer Complete Callbacks are provided in non Blocking mode:
(++) HAL_I2C_MemTxCpltCallback()
(++) HAL_I2C_MemRxCpltCallback()
(++) HAL_I2C_MasterTxCpltCallback()
(++) HAL_I2C_MasterRxCpltCallback()
(++) HAL_I2C_SlaveTxCpltCallback()
(++) HAL_I2C_SlaveRxCpltCallback()
(++) HAL_I2C_ErrorCallback()
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
比如要發(fā)送接收都有了,還有非阻塞的,然后通過(guò)回調函數來(lái)實(shí)現功能,真是非常方便的,跟之前說(shuō)的GPIO中斷一樣,默認的回調都是weak的,需要你自己額外實(shí)現,就可以替換掉這個(gè)weak函數了.
C/C++
8 Lines
1
2
3
4
5
6
7
8
__weak void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_I2C_TxCpltCallback could be implemented in the user file
*/
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
這樣就容易很多了,再也不用有以前的擔心了,而且效率也是更高了哦.所以,HAL庫應該是以后的發(fā)展方向,當然如果ST官方愿意兼容以前的庫,就是名字不要改,這樣估計會(huì )更多人接收,只是這樣的分層管理就顯得沒(méi)有那么的方便了.
02/27
11:36
技術(shù)控STMCUBE-MX HAL庫的學(xué)習之GPIO操作無(wú)回復STM32CUBEMX生成的都是HAL庫,與以前傳統的庫不一樣,導致很多人說(shuō)不會(huì )用,太難用.但是這其實(shí)很好用啊,包括中間層什么都做了,雖然實(shí)際項目中經(jīng)常有方案變更搞到好費勁…
他的所有例子都在stm32cubef4\STM32Cube_FW_F4_V1.10.0\Projects\STM32F429I-Discovery\Examples (舉例我的開(kāi)發(fā)板,其他也會(huì )有,如果用自己的板子還是比較麻煩,為何不買(mǎi)一個(gè)官方的先實(shí)驗.),我這次用一個(gè)GPIO的例子.里面用到的東西由一個(gè)CHM文件描述.比如我的是STM32F439xx_User_Manual.chm啊.里面是怎么實(shí)現的,請不要深究,不然就怎么都搞不好了.除非以后還想怎么優(yōu)化時(shí)間那個(gè).
打開(kāi)GPIO例子,發(fā)現好簡(jiǎn)單的Main函數.
比如我們看chm格式的那個(gè)文檔,看看HAL_Init是干什么的.而不是直接看他里面代碼.
這個(gè)功能用于初始化HAL庫,它必須是main函數的第一個(gè)指令,如圖就是這樣的,它執行以下操作:配置Flash預取,指令和數據緩存,就是我們說(shuō)的很高端的什么ICACHE,DCACHE,配置SysTick產(chǎn)生一個(gè)1毫秒周期中斷,它的時(shí)鐘為HSI,不過(guò),在這一階段,時(shí)鐘還沒(méi)有被配置,因此系統從內部恒指運行在16兆赫),設置NVIC組優(yōu)先級為4… 好了,這么多廢話(huà),就是初始化底層嘛.然后下面就容易理解了,都是以前的東西,EXTILine0_Config配置一個(gè)EXIT0中斷,說(shuō)白了,就是PA0中斷.包括函數HAL_NVIC_SetPriority其實(shí)就是NVIC_SetPriority的變形.但是中斷處理邏輯就稍微復雜了一點(diǎn)點(diǎn)了.一樣是先進(jìn)入EXTI0_IRQHandler,然后執行HAL_GPIO_EXTI_IRQHandler,再執行HAL_GPIO_EXTI_Callback,雖然是這樣,層層包裹只是為了規范,這是…這樣真的好嗎?
好了,我們試過(guò)他的Demo,我們自己也可以生成一個(gè),用到以下資源.第一步先給PA0一個(gè)EXIT0的屬性.
給PG13輸出的功能,他連接著(zhù)LED.
時(shí)鐘跳過(guò)不配置,就跑16MHz.這時(shí)候還要設置NVIC屬性,把下面高亮的EXIT0打勾.
生成一個(gè)工程,發(fā)現生成的工程也是非常簡(jiǎn)潔.
然后還有MX_GPIO_Init進(jìn)行GPIO的邏輯配置.用到的函數也很熟悉,然后編譯,發(fā)現也成功了,但是中斷里面還沒(méi)寫(xiě)啊.是的,因為他以weak聲明了一個(gè),所以,我們應該在主函數聲明一個(gè)真正的CallBack.
真正的CallBack寫(xiě)法如下:
C/C++
8 Lines
1
2
3
4
5
6
7
8
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0)
{
/* Toggle LED3 */
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_13);
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
編譯再下載,整個(gè)工程就可以用了.再按鍵,看得到效果啦,就是這么方便.
02/20
13:09
技術(shù)控可愛(ài)又可恨的I2C無(wú)回復I2C有很多優(yōu)點(diǎn),對于用戶(hù)來(lái)說(shuō),兩根線(xiàn),掛好多設備,在速度要求不是很高的場(chǎng)合完全是夠用的.但是I2C相對來(lái)說(shuō)就是軟件上的編寫(xiě)比較復雜,需要涉及到復雜的狀態(tài)機,對CPU來(lái)說(shuō),處理效率并不高,因為同樣是發(fā)送數據,就涉及多多種狀態(tài)的轉換,是發(fā)地址呢,還是發(fā)數據,這個(gè)就比較糾結了,在軟件上就比較麻煩了.而SPI,雖然線(xiàn)很多,但是速度非???
目前用過(guò)很多不同的芯片上的I2C,雖然都是點(diǎn)燈的水平. 體驗上來(lái)說(shuō),比較麻煩,比如430,如果要考慮到功耗,應該盡量進(jìn)入休眠,這個(gè)I2C的發(fā)送中,CPU空閑率可以達到99%的,但是如果休眠,再進(jìn)中斷,繼續判斷邏輯,就比較麻煩了,就TI官方的程序也沒(méi)有這么干.比如STM32,直接使用DMA模式,這樣可以是可以,也節省一些時(shí)間,但是只是為了讀一個(gè)寄存器呢,如果是讀一個(gè)寄存器就查詢(xún)等待,那么在這個(gè)等待過(guò)程都可以讓SPI發(fā)了幾十個(gè)字節了.
當然I2C在正常傳輸,麻煩也頂多在協(xié)議狀態(tài)的轉換,如果涉及到多主機,然后總線(xiàn)丟失呢?又不像CAN一樣的管理,這樣就很麻煩了.大家說(shuō)的萬(wàn)能的模擬I2C在這里就一點(diǎn)辦法都沒(méi)有了,當然,還有一個(gè)毫無(wú)辦法的事情,就是做I2C從機,做I2C從機遇到的問(wèn)題就是必須有跟隨主機的引腳,出于功耗的考慮,還要去做一些休眠,地址喚醒,那么,你就完全不能避開(kāi)硬件I2C了.
當然I2C也不是全是毛病,比如做什么開(kāi)源硬件啊,這些都是很喜歡的接口,統一,而且方便.對于用戶(hù)來(lái)說(shuō),不管你是I2C還是SPI還是怎么怎么的,只要用起來(lái)好用,并且連線(xiàn)簡(jiǎn)單,就是最重要的了.
02/17
12:53
技術(shù)控驗證:使用STM32的MCO能否給5V單片機提供時(shí)鐘無(wú)回復一個(gè)很奇怪的實(shí)驗,首先有一個(gè)STM32,一個(gè)5V的51單片機.5V的51單片機時(shí)鐘需要從STM32的MCO獲取.究竟能不能,這就得驗證了.為了驗證這個(gè)問(wèn)題.首先配置一個(gè)輸出HSE時(shí)鐘的,這個(gè)通常比較低,只有8MHz,STC的51使用無(wú)源晶振下也能做12MHz,所以這個(gè)肯定不應該有問(wèn)題.我用的MCU是IAP15F2K61S2的早期版本,只能支持到25MHz.
C/C++
18 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void IO_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOs clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* GPIOB configuration */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8 , GPIO_AF??_MCO);
RCC_MCO1Config(RCC_MCO1Source_HSE,RCC_MCO1Div_1);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
然后接到STC的XTAL1,然后記得共地,然后上電檢測.記得切換到外部晶體振蕩器.然而,我萬(wàn)用表看是5V,他卻..
如果要得出25MHz怎么辦.變通的方法是輸出PLLI2SCLK,那就可以得到各種頻率了.
02/13
09:26
技術(shù)控STM32F429-DISCO 學(xué)習之驅動(dòng)800X480屏幕無(wú)回復用STM32F429最大的優(yōu)點(diǎn)就是驅動(dòng)TFT屏幕,支持RGB的SYNC模式,最大支持到1024*768,不過(guò)好像性?xún)r(jià)比比較高的,還是1024*600的,當然,更便宜一些的,就是5寸的800*480,因為7寸的還要一個(gè)外部電路,可沒(méi)想象中的幾十元那么便宜.當然,我指的都是淘寶隨便的拆機屏.當然,我是為了驗證功能的,就不要跟我說(shuō)什么程序結構了,毫無(wú)結構可言.
驅動(dòng)屏幕并沒(méi)什么難事,只是涉及到計算參數.首先是像素時(shí)鐘需要計算,然后查手冊知道同步前沿同步后沿,如圖,填下:
然后得出結果變成代碼:
C/C++
17 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Timing configuration */
/* Configure horizontal synchronization width */
LTDC_InitStruct.LTDC_HorizontalSync = 1;
/* Configure vertical synchronization height */
LTDC_InitStruct.LTDC_VerticalSync = 1;
/* Configure accumulated horizontal back porch */
LTDC_InitStruct.LTDC_AccumulatedHBP = 40;
/* Configure accumulated vertical back porch */
LTDC_InitStruct.LTDC_AccumulatedVBP = 23;;
/* Configure accumulated active width */
LTDC_InitStruct.LTDC_AccumulatedActiveW = 840;
/* Configure accumulated active height */
LTDC_InitStruct.LTDC_AccumulatedActiveH = 503;
/* Configure total width */
LTDC_InitStruct.LTDC_TotalWidth = 880;
/* Configure total height */
LTDC_InitStruct.LTDC_TotalHeigh = 516;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
然后建立一個(gè)完全顯示圖層,來(lái)做測試,要知道LTDC_HorizontalStart和LTDC_VerticalStart,其中LTDC_HorizontalStart應該是LTDC_AccumulatedHBP+1,而LTDC_VerticalStart應該是LTDC_AccumulatedVBP+1,結束像素就順延+1吧.為什么是+1呢,因為同步時(shí)鐘也是1啊.
C/C++
4 Lines
1
2
3
4
LTDC_Layer_InitStruct.LTDC_HorizontalStart = 41;
LTDC_Layer_InitStruct.LTDC_HorizontalStop = (800 + 40);
LTDC_Layer_InitStruct.LTDC_VerticalStart = 24;
LTDC_Layer_InitStruct.LTDC_VerticalStop = 480 + 23;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
然后做一個(gè)清空整個(gè)屏幕的函數,首先肯定是整個(gè)RAM寫(xiě)0,然后寫(xiě)另一個(gè)顏色,再寫(xiě)自己顏色,就能確定自己能完整填滿(mǎn)framebuffer.
C/C++
17 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void cLCD_Clear(uint16_t Color)
{
uint32_t index = 0;
for (index = 0x00; index < 0x400000; index++)
{
*(volatile uint16_t *)(0xd0000000 + (2 * index)) = 0x0000;
}
for (index = 0x00; index < 0x400000; index++)
{
*(volatile uint16_t *)(0xd0000000 + (2 * index)) = 0x03f0;
}
for (index = 0x00; index < 480 * 800; index++)
{
*(volatile uint16_t *)(0xd0000000 + (2 * index)) = (uint16_t)Color;
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
如果最后只刷480 * 800 – 1的話(huà),最后一個(gè)點(diǎn)的顏色,就是0x03f0了.可以測試一下.我的Main函數:
C/C++
19 Lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(void)
{
/* Configure LCD */
LCD_Config();
/* Enable Layer 1 */
LTDC_LayerCmd(LTDC_Layer1, ENABLE);
/* Reload LTDC configuration */
LTDC_ReloadConfig(LTDC_IMReload);
/* Enable The LCD */
LTDC_Cmd(ENABLE);
cLCD_Clear(0x00f8);
//LCD_Clear(0xf800);
for(;;);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
效果(白色是因為反光,不是屏幕顯示問(wèn)題):
完整源文件如下:
800x480_RGB_429PS:馬上要過(guò)年了,更新可能要延緩哦…
02/9
16:08
技術(shù)控拆臺電MP4黑心Flash做U盤(pán)并安國量產(chǎn)無(wú)回復拆一個(gè)臺電的MP4,里面有一片Flash,上面的型號已經(jīng)重新打標,寫(xiě)著(zhù)J31CG08PJ,容量就不知道了,這個(gè)MP4自稱(chēng)有4G用戶(hù)儲存,那么說(shuō),這個(gè)最有可能是8G的片子了.因為具體型號不清楚,只能焊接在萬(wàn)能兼容的安國主控AU6989上了.現在最新的安國工具,已經(jīng)可以在Windows 10下用了,我找到的版本是v15.09.15.00.針對你的不同型號主控,可能不一樣版本呢.
先看看是什么型號.
原來(lái)真身是Intel的片子啊.當正片試試.用最嚴格的全面掃描4,U盤(pán)速度優(yōu)先,不過(guò)這個(gè)MP4的讀寫(xiě)本身就奇慢無(wú)比.速度優(yōu)先估計也快不了哪里.
下面解釋一下,其中全面掃描 1,對于提供較快操作模式的 FLASH,可加快掃描的時(shí)間,全面掃描 2,使用一般的模式掃描,容量可能會(huì )高一些.全面掃描 1 有可能比全面掃描 2 掃描出較多的壞塊, 但對大部分 FLASH 來(lái)說(shuō)量 產(chǎn)出的穩定性并無(wú)明顯差別.全面掃描 3,同全面掃描 1,但增加檢查返回狀態(tài),幾乎最嚴格了.全面掃描 4,掃描兩次,全面 1+Y 掃描,當然4是最嚴格的.當然,低格是個(gè)很費時(shí)的操作.所以,先干點(diǎn)別的再說(shuō).另外ECC = 0,就是不開(kāi)ECC了,那么容量肯定也是最小的.ECC就是容錯的一個(gè)機制.當然,還沒(méi)開(kāi)低格校正,先試試能不能PASS再說(shuō).
這個(gè)8G的片子,經(jīng)過(guò)2小時(shí)量產(chǎn)(確實(shí)慢死了),然后被告知,壞磁區過(guò)多,這特么沒(méi)法玩,換句話(huà)說(shuō),嚴格意義上,這是個(gè)廢品.這實(shí)在是黑心啊.
只好加ECC = 1來(lái)繼續試試,如果繼續不行,就不斷加ECC,直到可以量產(chǎn)出結果為止,當然ECC越高,這個(gè)可靠性就越來(lái)越差.只能存一下日常.當然,現在安國能做到ECC = 15,理論上很多垃圾都可以用的.
然后竟然可用了!
量產(chǎn)就是這么簡(jiǎn)單.
02/5
16:08
技術(shù)控STM32F429-DISCO 學(xué)習之FreeRTOS內存管理無(wú)回復FreeRTOS的內存管理,目前有5個(gè)(版本8.2.3下),有heap_1.c, heap_2.c, heap_3.c, heap_4.c and heap_5.c,并不是網(wǎng)上說(shuō)的,要用heap_2.c怎么的,當然.他們是有區別的.
heap_1.c 最簡(jiǎn)單的分配器,不允許內存釋放.
heap_2.c 比heap_1.c改進(jìn)的地方就是可以釋放內存了.
heap_3.c是針對線(xiàn)程安全的.
heap_4.c可以合并相鄰的空塊,避免碎片,可以指定絕對地址.
heap_5.c比heap_4.c改進(jìn)的地方是,可以合并不相鄰的空閑內存.
使用heap_2.c做實(shí)驗,最初是為了簡(jiǎn)單,不容易出錯,如果內存比較大,并且可能產(chǎn)生不少碎片,那應該用heap_5的方式,是最好的,當然,這個(gè)內存管理工具也比較耗費內存.