由STM32F103C8T6和ISD1820,LCD1602,按键模块组成的录音设备,麦克风进行声音的采集,这个模块的代码怎么写,并给出详细的解释
时间: 2023-11-28 10:05:15 浏览: 94
首先,需要确定录音设备的功能和实现方式。根据题目描述,该设备需要对声音进行采集、录制和播放,同时需要控制LCD1602和按键模块。因此,可以将设计分为以下几个部分:
1. 声音采集部分:使用ISD1820进行声音采集,并将采集到的声音数据存储到STM32F103C8T6的内部Flash或外部SD卡中。
2. 录制控制部分:通过按键模块控制录制开始、停止和保存等操作。
3. 播放控制部分:通过按键模块控制播放开始、停止和暂停等操作。
4. LCD1602显示部分:显示录音状态、录音时间和播放状态等信息。
以下是具体的代码实现和解释:
1. 声音采集部分:
- 首先,需要定义一些常量和变量,包括采样率、采样位数、采样通道数、Flash地址、Flash扇区大小、Flash页大小、缓冲区大小等。
```c
#define REC_SAMPLE_RATE 8000 // 采样率
#define REC_SAMPLE_BITS 16 // 采样位数
#define REC_SAMPLE_CHANNELS 1 // 采样通道数
#define FLASH_REC_ADDR 0x080E0000 // Flash地址
#define FLASH_REC_SECTOR FLASH_Sector_11 // Flash扇区
#define FLASH_REC_SECTOR_SIZE FLASH_Sector_11_Size // Flash扇区大小
#define FLASH_REC_PAGE_SIZE FLASH_PageSize_2KByte // Flash页大小
#define REC_BUF_SIZE 1024 // 缓冲区大小
uint16_t rec_buf[REC_BUF_SIZE];
```
- 然后,需要初始化ISD1820和ADC模块,并设置采样率、采样位数和采样通道数。
```c
ISD1820_Init(); // 初始化ISD1820
ADC_Init(); // 初始化ADC
ADC_SetSampleRate(REC_SAMPLE_RATE); // 设置采样率
ADC_SetResolution(REC_SAMPLE_BITS); // 设置采样位数
ADC_SetChannels(REC_SAMPLE_CHANNELS); // 设置采样通道数
```
- 接下来,需要实现声音采集函数,该函数通过ADC模块读取声音数据,并将数据存储到缓冲区中。
```c
void REC_Record(void)
{
uint32_t i, addr = FLASH_REC_ADDR;
uint16_t *p_buf = rec_buf;
uint32_t page_size = FLASH_REC_PAGE_SIZE;
uint32_t sect_size = FLASH_REC_SECTOR_SIZE;
uint32_t sect_addr = FLASH_REC_ADDR;
// 擦除Flash扇区
FLASH_Unlock();
FLASH_EraseSector(FLASH_REC_SECTOR, VoltageRange_3);
FLASH_Lock();
// 开始录音
ADC_Start(); // 启动ADC
while (1) {
if (ADC_Ready()) { // 判断是否采集完成
*p_buf++ = ADC_Read(); // 读取采样数据
if (p_buf >= rec_buf + REC_BUF_SIZE) { // 判断是否缓冲区已满
// 存储到Flash中
FLASH_Unlock();
for (i = 0; i < REC_BUF_SIZE; i += page_size) {
FLASH_ProgramPage(addr, p_buf + i, page_size);
addr += page_size;
}
FLASH_Lock();
p_buf = rec_buf;
sect_addr += sect_size;
if (sect_addr >= FLASH_REC_ADDR + FLASH_REC_SECTOR_SIZE) break; // 判断是否录音结束
}
}
}
ADC_Stop(); // 停止ADC
}
```
2. 录制控制部分:
- 首先,需要定义按键模块的引脚和状态变量。
```c
#define REC_START_PIN GPIO_Pin_0
#define REC_STOP_PIN GPIO_Pin_1
#define REC_SAVE_PIN GPIO_Pin_2
#define REC_START_PORT GPIOA
#define REC_STOP_PORT GPIOA
#define REC_SAVE_PORT GPIOA
#define REC_STATE_IDLE 0 // 空闲状态
#define REC_STATE_RECORD 1 // 录音状态
#define REC_STATE_SAVE 2 // 保存状态
uint8_t rec_state = 0; // 录音状态变量
```
- 然后,需要初始化按键模块的引脚,并设置中断回调函数。
```c
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 初始化按键模块的引脚
GPIO_InitStruct.GPIO_Pin = REC_START_PIN | REC_STOP_PIN | REC_SAVE_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(REC_START_PORT, &GPIO_InitStruct);
GPIO_Init(REC_STOP_PORT, &GPIO_InitStruct);
GPIO_Init(REC_SAVE_PORT, &GPIO_InitStruct);
// 设置按键模块的中断回调函数
EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn | EXTI1_IRQn | EXTI2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
```
- 最后,需要实现中断回调函数,并根据按键状态进行相应的录制操作。
```c
void EXTI0_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line0);
if (rec_state == REC_STATE_IDLE) {
rec_state = REC_STATE_RECORD;
REC_Record();
rec_state = REC_STATE_SAVE;
}
}
void EXTI1_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line1);
if (rec_state == REC_STATE_RECORD) {
ADC_Stop();
rec_state = REC_STATE_SAVE;
}
}
void EXTI2_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line2);
if (rec_state == REC_STATE_SAVE) {
// 保存录音数据到SD卡或其他存储设备
rec_state = REC_STATE_IDLE;
}
}
```
3. 播放控制部分:
- 首先,需要定义按键模块的引脚和状态变量。
```c
#define PLAY_START_PIN GPIO_Pin_3
#define PLAY_STOP_PIN GPIO_Pin_4
#define PLAY_PAUSE_PIN GPIO_Pin_5
#define PLAY_START_PORT GPIOA
#define PLAY_STOP_PORT GPIOA
#define PLAY_PAUSE_PORT GPIOA
#define PLAY_STATE_IDLE 0 // 空闲状态
#define PLAY_STATE_PLAY 1 // 播放状态
#define PLAY_STATE_PAUSE 2 // 暂停状态
uint8_t play_state = 0; // 播放状态变量
```
- 然后,需要初始化按键模块的引脚,并设置中断回调函数。
```c
GPIO_InitStruct.GPIO_Pin = PLAY_START_PIN | PLAY_STOP_PIN | PLAY_PAUSE_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(PLAY_START_PORT, &GPIO_InitStruct);
GPIO_Init(PLAY_STOP_PORT, &GPIO_InitStruct);
GPIO_Init(PLAY_PAUSE_PORT, &GPIO_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line3 | EXTI_Line4 | EXTI_Line5;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn | EXTI4_IRQn | EXTI9_5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
```
- 最后,需要实现中断回调函数,并根据按键状态进行相应的播放操作。
```c
void EXTI3_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line3);
if (play_state == PLAY_STATE_IDLE) {
// 播放录音数据
play_state = PLAY_STATE_PLAY;
} else if (play_state == PLAY_STATE_PAUSE) {
// 恢复播放
play_state = PLAY_STATE_PLAY;
}
}
void EXTI4_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line4);
if (play_state == PLAY_STATE_PLAY || play_state == PLAY_STATE_PAUSE) {
// 停止播放
play_state = PLAY_STATE_IDLE;
}
}
void EXTI9_5_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line5);
if (play_state == PLAY_STATE_PLAY) {
// 暂停播放
play_state = PLAY_STATE_PAUSE;
}
}
```
4. LCD1602显示部分:
- 首先,需要定义LCD1602的引脚和一些常量和变量。
```c
#define LCD_RS_PIN GPIO_Pin_6
#define LCD_RW_PIN GPIO_Pin_7
#define LCD_EN_PIN GPIO_Pin_8
#define LCD_DB4_PIN GPIO_Pin_9
#define LCD_DB5_PIN GPIO_Pin_10
#define LCD_DB6_PIN GPIO_Pin_11
#define LCD_DB7_PIN GPIO_Pin_12
#define LCD_RS_PORT GPIOB
#define LCD_RW_PORT GPIOB
#define LCD_EN_PORT GPIOB
#define LCD_DB4_PORT GPIOB
#define LCD_DB5_PORT GPIOB
#define LCD_DB6_PORT GPIOB
#define LCD_DB7_PORT GPIOB
#define LCD_LINE1_ADDR 0x00
#define LCD_LINE2_ADDR 0x40
#define LCD_CMD_CLEAR_DISPLAY 0x01
#define LCD_CMD_RETURN_HOME 0x02
#define LCD_CMD_ENTRY_MODE_SET 0x04
#define LCD_CMD_DISPLAY_CONTROL 0x08
#define LCD_CMD_CURSOR_SHIFT 0x10
#define LCD_CMD_FUNCTION_SET 0x20
#define LCD_CMD_SET_CGRAM_ADDR 0x40
#define LCD_CMD_SET_DDRAM_ADDR 0x80
#define LCD_ENTRY_MODE_SET_INC 0x02
#define LCD_ENTRY_MODE_SET_SHIFT 0x01
#define LCD_DISPLAY_CONTROL_ON 0x04
#define LCD_DISPLAY_CONTROL_CURSOR_ON 0x02
#define LCD_DISPLAY_CONTROL_BLINK_ON 0x01
#define LCD_FUNCTION_SET_4BIT 0x00
#define LCD_FUNCTION_SET_2LINE 0x08
#define LCD_FUNCTION_SET_5X8DOTS 0x00
#define LCD_FUNCTION_SET_5X10DOTS 0x04
uint8_t lcd_line = 1; // 当前行数
```
- 然后,需要初始化LCD1602的引脚,并设置LCD1602的显示模式。
```c
GPIO_InitStruct.GPIO_Pin = LCD_RS_PIN | LCD_RW_PIN | LCD_EN_PIN |
LCD_DB4_PIN | LCD_DB5_PIN | LCD_DB6_PIN | LCD_DB7_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LCD_RS_PORT, &GPIO_InitStruct);
GPIO_Init(LCD_RW_PORT, &GPIO_InitStruct);
GPIO_Init(LCD_EN_PORT, &GPIO_InitStruct);
GPIO_Init(LCD_DB4_PORT, &GPIO_InitStruct);
GPIO_Init(LCD_DB5_PORT, &GPIO_InitStruct);
GPIO_Init(LCD_DB6_PORT, &GPIO_InitStruct);
GPIO_Init(LCD_DB7_PORT, &GPIO_InitStruct);
LCD_Cmd(LCD_CMD_FUNCTION_SET | LCD_FUNCTION_SET_4BIT | LCD_FUNCTION_SET_2LINE | LCD_FUNCTION_SET_5X8DOTS);
LCD_Cmd(LCD_CMD_DISPLAY_CONTROL | LCD_DISPLAY_CONTROL_ON | LCD_DISPLAY_CONTROL_CURSOR_ON | LCD_DISPLAY_CONTROL_BLINK_ON);
LCD_Cmd(LCD_CMD_ENTRY_MODE_SET | LCD_ENTRY_MODE_SET_INC | LCD_ENTRY_MODE_SET_SHIFT);
```
- 最后,需要实现一些LCD1602的基本操作函数,包括发送命令、发送数据、清屏、返回首页、设置光标位置和显示字符等。
```c
void LCD_Cmd(uint8_t cmd)
{
LCD_RS_PORT->BRR = LCD_RS_PIN;
LCD_RW_PORT->BRR = LCD_RW_PIN;
LCD_EN_PORT->BRR = LCD_EN_PIN;
LCD_DB4_PORT->BRR = LCD_DB4_PIN | LCD_DB5_PIN | LCD_DB6_PIN | LCD_DB7_PIN;
LCD_DB4_PORT->BSRR = ((cmd & 0x0F) << 9);
LCD_EN_PORT->BSRR = LCD_EN_PIN;
delay_us(10);
LCD_EN_PORT->BRR = LCD_EN_PIN;
delay_us(100);
LCD_DB4_PORT->BRR = LCD_DB4_PIN | LCD_DB5_PIN | LCD_DB6_PIN | LCD_DB7_PIN;
LCD_DB4_PORT->BSRR = ((cmd >> 4) << 9);
LCD_EN_PORT->BSRR = LCD_EN_PIN;
delay_us(10);
LCD_EN_PORT->BRR = LCD_EN_PIN;
delay_us(100);
}
void LCD_Data(uint8_t data)
{
LCD_RS_PORT->BSRR = LCD_RS_PIN;
LCD_RW_PORT->BRR = LCD_RW_PIN;
LCD_EN_PORT->BRR = LCD_EN_PIN;
LCD_DB4_PORT->BRR = LCD_DB4_PIN | LCD_DB5_PIN | LCD_DB6_PIN | LCD_DB7_PIN;
LCD_DB4_PORT->BSRR = ((data & 0x0F) << 9);
LCD_EN_PORT->BSRR = LCD_EN_PIN;
delay_us(10);
LCD_EN_PORT->BRR = LCD_EN_PIN;
delay_us(100);
LCD_DB4_PORT->BRR = LCD_DB4_PIN | LCD_DB5_PIN | LCD_DB6_PIN | LCD_DB7_PIN;
LCD_DB4_PORT->BSRR = ((data >> 4) << 9);
LCD_EN_PORT->BSRR = LCD_EN_PIN;
delay_us(10);
LCD_EN_PORT->BRR = LCD_EN_PIN;
delay_us(100);
}
void LCD_Clear(void)
{
LCD_Cmd(LCD_CMD_CLEAR_DISPLAY);
delay_ms(2);
}
void LCD_Home(void)
{
LCD_Cmd(LCD_CMD_RETURN_HOME);
delay_ms(2);
}
void LCD_SetCursor(uint8_t line, uint8_t col)
{
uint8_t addr = LCD_LINE1_ADDR;
if (line == 2) addr = LCD_LINE2_ADDR;
addr += col - 1;
LCD_Cmd(LCD_CMD_SET_DDRAM_ADDR | addr);
}
void LCD_PutChar(char ch)
{
LCD_Data(ch);
}
void LCD_PutString(char *str)
{
while (*str) LCD_Data(*str++);
}
```
综上所述,以上是基于STM32F103C8T6和ISD1820、LCD1602、按键模块实现的录音设备的代码实现和详细解释。
阅读全文